Skip to content

Commit fe52c50

Browse files
authored
Add Flowis protocol decoder (#2357)
1 parent 96d2c60 commit fe52c50

File tree

5 files changed

+149
-0
lines changed

5 files changed

+149
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
319319
[234] Watchman Sonic Advanced / Plus
320320
[235] Oil Ultrasonic SMART FSK
321321
[236] Gasmate BA1008 meat thermometer
322+
[237] Flowis flow meters
322323
323324
* Disabled by default, use -R n or a conf file to enable
324325

conf/rtl_433.example.conf

+1
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ stop_after_successful_events false
458458
protocol 234 # Watchman Sonic Advanced / Plus
459459
protocol 235 # Oil Ultrasonic SMART FSK
460460
protocol 236 # Gasmate BA1008 meat thermometer
461+
protocol 237 # Flowis flow meters
461462

462463
## Flex devices (command line option "-X")
463464

include/rtl_433_devices.h

+1
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@
244244
DECL(oil_watchman_advanced) \
245245
DECL(oil_smart) \
246246
DECL(gasmate_ba1008) \
247+
DECL(flowis) \
247248

248249
/* Add new decoders here. */
249250

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ add_library(r_433 STATIC
110110
devices/fineoffset_wn34.c
111111
devices/fineoffset_ws80.c
112112
devices/flex.c
113+
devices/flowis.c
113114
devices/fordremote.c
114115
devices/fs20.c
115116
devices/ft004b.c

src/devices/flowis.c

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/** @file
2+
Flowis water meter.
3+
4+
Copyright (C) 2023 Benjamin Larsson
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+
Heavily based on marlec_solar.c
12+
Copyright (C) 2021 Christian W. Zuckschwerdt <[email protected]>
13+
*/
14+
15+
/**
16+
Flowis water meter.
17+
18+
There are several different message types with different message lengths.
19+
All signals are transmitted with a preamble (0xA or 0x5) and then the
20+
syncword d391 d391. This is a popular syncword used by the CC110x transceiver
21+
family.
22+
23+
24+
Message layout type 1 (15 bytes of length), 4-bit nibble:
25+
26+
0 1 2 3 4 5 6 7 8 9 A B C ....... 15
27+
SSSS SSSS LL YY IIIIIIII ?? TTTTTTTT AA BB ???????? CC
28+
29+
- S 32b: 2 x 16 bit sync words d391 d391 (32 bits)
30+
- L 8b: message length, different message types have different lengths
31+
- Y 8b: message type (1 and 2 has been observed
32+
- I 32b: meter id, visible on the actual meter
33+
- ? 8b: unknown
34+
- T 32b: timestamp, bitpacked
35+
- V 32b: Volume in m3
36+
- A 8b: Alarm
37+
- B 8b: Backflow
38+
- ? xb: unknown
39+
- C 16b: CRC-16 with poly=0x8005 and init=0xFFFF over data after sync
40+
41+
Message type 2 uses same message syntax, length type, payload and checksum.
42+
43+
Type 2 messages usually contain long runs of zeros that might cause bitstream desyncs.
44+
45+
*/
46+
47+
#include "decoder.h"
48+
49+
static int flowis_decode(r_device *decoder, bitbuffer_t *bitbuffer)
50+
{
51+
uint8_t const preamble[] = {
52+
/*0xaa, 0xaa, */ 0xaa, 0xaa, // preamble
53+
0xd3, 0x91, 0xd3, 0x91 // sync word
54+
};
55+
56+
data_t *data;
57+
58+
if (bitbuffer->num_rows != 1) {
59+
return DECODE_ABORT_EARLY;
60+
}
61+
62+
int row = 0;
63+
// Validate message and reject it as fast as possible : check for preamble
64+
unsigned start_pos = bitbuffer_search(bitbuffer, row, 0, preamble, sizeof (preamble) * 8);
65+
66+
if (start_pos == bitbuffer->bits_per_row[row]) {
67+
return DECODE_ABORT_EARLY; // no preamble detected
68+
}
69+
70+
uint8_t len;
71+
bitbuffer_extract_bytes(bitbuffer, row, start_pos + sizeof (preamble) * 8, &len, 8);
72+
73+
74+
uint8_t frame[256+2+1] = {0}; // uint8_t max bytes + 2 bytes crc + 1 lenght byte
75+
frame[0] = len;
76+
// Get frame (len don't include the length byte and the crc16 bytes)
77+
bitbuffer_extract_bytes(bitbuffer, row,
78+
start_pos + (sizeof (preamble) + 1) * 8,
79+
&frame[1], (len + 2) * 8);
80+
81+
decoder_log_bitrow(decoder, 2, __func__, frame, (len + 1) * 8, "frame data");
82+
83+
uint16_t crc = crc16(frame, len + 1, 0x8005, 0xffff);
84+
85+
if ((frame[len + 1] << 8 | frame[len + 2]) != crc) {
86+
decoder_logf(decoder, 1, __func__, "CRC invalid %04x != %04x",
87+
frame[len + 1] << 8 | frame[len + 2], crc);
88+
return DECODE_FAIL_MIC;
89+
}
90+
uint8_t* b = frame;
91+
int type = b[1];
92+
93+
/* Only type 1 decoding is supported */
94+
if (type != 1) return DECODE_ABORT_EARLY;
95+
96+
int id = b[5] << 24 | b[4] << 16 | b[3] << 8 | b[2];
97+
int volume = b[13] << 16 | b[12] << 8 | b[11];
98+
99+
char fts_str[20];
100+
int fts_year = b[10] >> 2;
101+
int fts_mth = ((b[9]>>6) | (b[10]&3)<<2);
102+
int fts_day = (b[9]&0x3E) >> 1;
103+
int fts_hour = (b[8]>>4) | ((b[9]&1)<<4);
104+
int fts_min = ((b[8]&0xF)<<2) | ((b[7]&0xC0)>>6);
105+
int fts_sec = b[7]&0x3F;
106+
sprintf(fts_str, "%4d-%02d-%02dT%02d:%02d:%02d", fts_year + 2000, fts_mth, fts_day, fts_hour, fts_min, fts_sec);
107+
108+
/* clang-format off */
109+
data = data_make(
110+
"model", "", DATA_STRING, "Flowis",
111+
"id", "Meter id", DATA_INT, id,
112+
"type", "Type", DATA_INT, type,
113+
"volume_m3", "Volume", DATA_FORMAT, "%.3f m3", DATA_DOUBLE, volume/1000.0,
114+
"device_time", "Device time", DATA_STRING, fts_str,
115+
"alarm", "Alarm", DATA_INT, b[11],
116+
"backflow", "Backflow", DATA_INT, b[12],
117+
"mic", "Integrity", DATA_STRING, "CRC",
118+
NULL);
119+
/* clang-format on */
120+
121+
decoder_output_data(decoder, data);
122+
return 1;
123+
}
124+
125+
static char *output_fields[] = {
126+
"model",
127+
"id",
128+
"type",
129+
"volume_m3",
130+
"device_time",
131+
"alarm",
132+
"backflow",
133+
"mic",
134+
NULL,
135+
};
136+
137+
r_device const flowis = {
138+
.name = "Flowis flow meters",
139+
.modulation = FSK_PULSE_PCM,
140+
.short_width = 10,
141+
.long_width = 10,
142+
.reset_limit = 5000,
143+
.decode_fn = &flowis_decode,
144+
.fields = output_fields,
145+
};

0 commit comments

Comments
 (0)