Skip to content

Commit 0b95fa2

Browse files
authored
Add support for Badger ORION water meter (#2089)
1 parent eee45de commit 0b95fa2

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed

include/rtl_433_devices.h

+1
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@
230230
DECL(maverick_xr30) \
231231
DECL(fineoffset_wn34) \
232232
DECL(rubicson_pool_48942) \
233+
DECL(badger_orion) \
233234

234235
/* Add new decoders here. */
235236

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ add_library(r_433 STATIC
235235
devices/x10_rf.c
236236
devices/x10_sec.c
237237
devices/yale_hsa.c
238+
devices/badger_water.c
238239
)
239240

240241
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" MATCHES "Clang")

src/devices/badger_water.c

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/** @file
2+
Badger ORION water meter support.
3+
4+
Copyright (C) 2022 Nicko van Someren
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation; either version 2 of the License, or
9+
(at your option) any later version.
10+
*/
11+
12+
#include "decoder.h"
13+
14+
/** @fn static int badger_orion_decode(r_device *decoder, bitbuffer_t *bitbuffer)
15+
Badger ORION water meter.
16+
17+
S.a. https://fccid.io/GIF2006B
18+
19+
For the single-frequency models the center frequency is 916.45MHz. The bit rate is
20+
100KHz, so the sample rate should be at least 1.2MHz; using 1.6MHz may work better
21+
when the signal is weak or noisy.
22+
23+
The low-level encoding is much the same as M-Bus mode T, but the payload differs.
24+
25+
The specification sheet states that "The endpoint broadcasts its unique endpoint
26+
serial number, current meter reading and applicable status indicators" and also that
27+
status reports include "Premise Leak Detection", "Cut-Wire Indication", "Reverse Flow
28+
Indication", "No Usage Indication" and "Encoder Error", but the specific flag values
29+
are not known.
30+
31+
The data is preceded by several sync bytes of 01010101, followed by the ten bit
32+
preamble of 0000 1111 01. This is followed by 10 bytes encoded using a 4:6 NRZ
33+
encoding. This code treats 6 bits of the sync sequence as part of a 16 bit preamble.
34+
35+
Once the data has been decoded with the NRZ 6:4 decoding, it has the following format:
36+
- Device ID: 3 bytes, little-endian. Typically utility provider's number, mod 2^24 or mod 10^7.
37+
- Device flags: 1 byte. Fields not known
38+
- Meter reading: 3 bytes, little-endian. Value in gallons for meters with 1-gallon resolution.
39+
- Status flags: 1 byte. Fields not known
40+
- CRC: 2 bytes, crc16, polynomial 0x3D65
41+
42+
*/
43+
44+
// Mapping from 6 bits to 4 bits. "3of6" coding used for Mode T
45+
static uint8_t badger_decode_3of6(uint8_t byte)
46+
{
47+
uint8_t out = 0xFF; // Error
48+
//fprintf(stderr,"Decode %0d\n", byte);
49+
switch(byte) {
50+
case 22: out = 0x0; break; // 0x16
51+
case 13: out = 0x1; break; // 0x0D
52+
case 14: out = 0x2; break; // 0x0E
53+
case 11: out = 0x3; break; // 0x0B
54+
case 28: out = 0x4; break; // 0x1C
55+
case 25: out = 0x5; break; // 0x19
56+
case 26: out = 0x6; break; // 0x1A
57+
case 19: out = 0x7; break; // 0x13
58+
case 44: out = 0x8; break; // 0x2C
59+
case 37: out = 0x9; break; // 0x25
60+
case 38: out = 0xA; break; // 0x26
61+
case 35: out = 0xB; break; // 0x23
62+
case 52: out = 0xC; break; // 0x34
63+
case 49: out = 0xD; break; // 0x31
64+
case 50: out = 0xE; break; // 0x32
65+
case 41: out = 0xF; break; // 0x29
66+
default: break; // Error
67+
}
68+
return out;
69+
}
70+
71+
// Decode the DC-free 4:6 encoding
72+
static int badger_decode_3of6_buffer(uint8_t const *bits, unsigned bit_offset, uint8_t *output)
73+
{
74+
for (unsigned n=0; n<10; ++n) {
75+
uint8_t nibble_h = badger_decode_3of6(bitrow_get_byte(bits, n*12+bit_offset) >> 2);
76+
uint8_t nibble_l = badger_decode_3of6(bitrow_get_byte(bits, n*12+bit_offset+6) >> 2);
77+
if ((nibble_h | nibble_l) > 15) {
78+
return 1;
79+
}
80+
output[n] = (nibble_h << 4) | nibble_l;
81+
}
82+
return 0;
83+
}
84+
85+
static int badger_orion_decode(r_device *decoder, bitbuffer_t *bitbuffer)
86+
{
87+
static uint8_t const preamble_pattern[] = {0x54, 0x3D};
88+
89+
uint8_t data_in[10] = {0}; // Data from Physical layer decoded to bytes
90+
data_t *data;
91+
92+
// Validate package length
93+
// The minimum preamble is 16 bits and the payload is 10 4:6 encoded bytes.
94+
// There is often a long preamble and a 64 or more bits of tail, so the maximum likely length is longer
95+
if (bitbuffer->bits_per_row[0] < (16 + 12 * 10) || bitbuffer->bits_per_row[0] > (128 + 16 + 12 * 10 + 96)) {
96+
return DECODE_ABORT_LENGTH;
97+
}
98+
99+
// Find the preamble
100+
unsigned bit_offset = bitbuffer_search(bitbuffer, 0, 0, preamble_pattern, sizeof(preamble_pattern) * 8);
101+
if (bit_offset + 12 * 10 >= bitbuffer->bits_per_row[0]) {
102+
return DECODE_ABORT_EARLY;
103+
}
104+
105+
decoder_logf_bitbuffer(decoder, 2, __func__, bitbuffer, "Preamble found at: %u", bit_offset);
106+
bit_offset += sizeof(preamble_pattern) * 8; // skip preamble
107+
108+
if (badger_decode_3of6_buffer(bitbuffer->bb[0], bit_offset, data_in) < 0) {
109+
return DECODE_FAIL_MIC;
110+
}
111+
112+
uint16_t crc_read = (((uint16_t)data_in[8] << 8) | data_in[9]);
113+
uint16_t crc_calc = ~crc16(data_in, 8, 0x3D65, 0);
114+
if (crc_calc != crc_read) {
115+
decoder_logf(decoder, 1, __func__,
116+
"Badger ORION: CRC error: Calculated 0x%0X, Read 0x%0X",
117+
(unsigned)crc_calc, (unsigned) crc_read);
118+
return DECODE_FAIL_MIC;
119+
}
120+
121+
uint32_t device_id = data_in[0] | (data_in[1] << 8) | (data_in[2] << 16);
122+
uint8_t flags_1 = data_in[3];
123+
uint32_t volume = data_in[4] | (data_in[5] << 8) | (data_in[6] << 16);
124+
uint8_t flags_2 = data_in[7];
125+
126+
/* clang-format off */
127+
data = data_make(
128+
"model", "", DATA_STRING, "Badger-ORION",
129+
"id", "ID", DATA_INT, device_id,
130+
"flags_1", "Flags-1", DATA_INT, flags_1,
131+
"volume_gal", "Volume", DATA_INT, volume,
132+
"flags_2", "Flags-2", DATA_INT, flags_2,
133+
"mic", "Integrity", DATA_STRING, "CRC",
134+
NULL);
135+
/* clang-format on */
136+
137+
decoder_output_data(decoder, data);
138+
return 0;
139+
}
140+
141+
// Note: At this time the exact meaning of the flags is not known.
142+
static char *badger_output_fields[] = {
143+
"model",
144+
"id",
145+
"flags_1",
146+
"volume_gal",
147+
"flags_2",
148+
"mic",
149+
NULL,
150+
};
151+
152+
// Badger ORION water meter,
153+
// Frequency 916.45 MHz, Bitrate 100 kbps, Modulation NRZ FSK
154+
r_device badger_orion = {
155+
.name = "Badger ORION water meter, 100kbps (-f 916450000 -s 1200000)", // Minimum samplerate = 1.2 MHz (12 samples of 100kb/s)
156+
.modulation = FSK_PULSE_PCM,
157+
.short_width = 10, // Bit rate: 100 kb/s
158+
.long_width = 10, // NRZ encoding (bit width = pulse width)
159+
.reset_limit = 1000, //
160+
.decode_fn = &badger_orion_decode,
161+
.fields = badger_output_fields,
162+
};

0 commit comments

Comments
 (0)