mirror of
https://github.com/merbanan/rtl_433.git
synced 2026-04-19 00:56:53 -04:00
Improve FS20 decoding, add FHT support (#1783)
Co-authored-by: Stefan Brüns <stefan@localhost.localdomain>
This commit is contained in:
@@ -31,6 +31,53 @@ Command extensions are also not decoded. feel free to improve!
|
||||
|
||||
#include "decoder.h"
|
||||
|
||||
static int fs20_find_preamble(bitbuffer_t *bitbuffer, int bitpos)
|
||||
{
|
||||
// Preamble is 12 x '0' | '1', but we ignore the first preamble bit
|
||||
// Last bit ('1') is at position (pattern[1] >> 4 & 1)
|
||||
uint8_t const preamble_pattern[2] = {0x00, 0x10};
|
||||
uint8_t const min_packet_length = 4 * (8 + 1);
|
||||
|
||||
// fast scan for 8 consecutive '0' bits
|
||||
uint8_t *bits = bitbuffer->bb[0];
|
||||
while ((bitpos + 12 + min_packet_length < bitbuffer->bits_per_row[0])
|
||||
&& ((bits[(bitpos / 8) + 1] == 0) || (bits[(bitpos / 8)] != 0))) {
|
||||
bitpos += 8;
|
||||
}
|
||||
if (bitpos) {
|
||||
bitpos--;
|
||||
bitpos &= ~0x3;
|
||||
}
|
||||
|
||||
while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, preamble_pattern, 12)) < bitbuffer->bits_per_row[0]) {
|
||||
if (bitpos + min_packet_length >= bitbuffer->bits_per_row[0])
|
||||
return DECODE_ABORT_LENGTH;
|
||||
|
||||
return bitpos + 12;
|
||||
}
|
||||
|
||||
// preamble not found
|
||||
return DECODE_FAIL_SANITY;
|
||||
}
|
||||
|
||||
struct parity_byte {
|
||||
uint8_t data;
|
||||
uint8_t err;
|
||||
};
|
||||
|
||||
static struct parity_byte get_byte(uint8_t* bits, unsigned pos)
|
||||
{
|
||||
uint16_t word = (bits[pos / 8] << 8) | bits[(pos / 8) + 1];
|
||||
struct parity_byte res;
|
||||
|
||||
word <<= pos & 7;
|
||||
res.data = word >> 8;
|
||||
// parity8 returns odd parity, bit 9 is even parity
|
||||
res.err = parity8(res.data) != (word >> 7 & 1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fs20_decode(r_device *decoder, bitbuffer_t *bitbuffer)
|
||||
{
|
||||
static char const *const cmd_tab[] = {
|
||||
@@ -67,35 +114,125 @@ static int fs20_decode(r_device *decoder, bitbuffer_t *bitbuffer)
|
||||
"unused",
|
||||
"unused",
|
||||
};
|
||||
static char const *flags_tab[8] = {
|
||||
"(none)",
|
||||
"Extended",
|
||||
"BiDir",
|
||||
"Extended | BiDir",
|
||||
"Response",
|
||||
"Response | Extended",
|
||||
"Response | BiDir",
|
||||
"Response | Extended | BiDir",
|
||||
};
|
||||
static char const *fht_cmd_tab[16] = {
|
||||
"end-of-sync",
|
||||
"valve open",
|
||||
"valve close",
|
||||
"? (0x3)",
|
||||
"? (0x4)",
|
||||
"? (0x5)",
|
||||
"valve open <ext>%",
|
||||
"? (0x7)",
|
||||
"offset adjust",
|
||||
"? (0x9)",
|
||||
"valve de-scale",
|
||||
"? (0x11)",
|
||||
"sync countdown",
|
||||
"? (0x13)",
|
||||
"beep",
|
||||
"pairing?",
|
||||
};
|
||||
static char const *fht_flags_tab[8] = {
|
||||
"(none)",
|
||||
"Extended",
|
||||
"BS?",
|
||||
"Extended | BS?",
|
||||
"Repeat",
|
||||
"Repeat | Extended",
|
||||
"Repeat | BS?",
|
||||
"Repeat | Extended | BS?",
|
||||
};
|
||||
|
||||
uint8_t *b; // bits of a row
|
||||
bitbuffer_invert(bitbuffer);
|
||||
|
||||
uint8_t *bits = bitbuffer->bb[0];
|
||||
uint8_t cmd;
|
||||
uint8_t hc1;
|
||||
uint8_t hc2;
|
||||
uint16_t hc;
|
||||
uint8_t address;
|
||||
uint8_t ext = 0;
|
||||
uint8_t sum;
|
||||
|
||||
data_t *data;
|
||||
uint16_t ad_b4 = 0;
|
||||
uint32_t hc_b4 = 0;
|
||||
|
||||
// check length of frame
|
||||
if (bitbuffer->bits_per_row[0] != 58) {
|
||||
// check extended length (never tested!)
|
||||
if (bitbuffer->bits_per_row[0] != 67)
|
||||
return DECODE_ABORT_LENGTH;
|
||||
int rc = DECODE_FAIL_MIC;
|
||||
int bitpos = 0;
|
||||
|
||||
while ((bitpos = fs20_find_preamble(bitbuffer, bitpos)) >= 0) {
|
||||
if (decoder->verbose) {
|
||||
fprintf(stderr, "Found preamble at %d\n", bitpos);
|
||||
}
|
||||
|
||||
struct parity_byte res;
|
||||
|
||||
res = get_byte(bits, bitpos);
|
||||
if (res.err)
|
||||
continue;
|
||||
hc = res.data << 8;
|
||||
|
||||
res = get_byte(bits, bitpos + 9);
|
||||
if (res.err)
|
||||
continue;
|
||||
hc |= res.data;
|
||||
|
||||
res = get_byte(bits, bitpos + 18);
|
||||
if (res.err)
|
||||
continue;
|
||||
address = res.data;
|
||||
|
||||
res = get_byte(bits, bitpos + 27);
|
||||
if (res.err)
|
||||
continue;
|
||||
cmd = res.data;
|
||||
|
||||
res = get_byte(bits, bitpos + 36);
|
||||
if (res.err)
|
||||
continue;
|
||||
|
||||
if (cmd & 0x20) {
|
||||
ext = res.data;
|
||||
if (bitpos + 45 + 9 > bitbuffer->bits_per_row[0])
|
||||
break;
|
||||
|
||||
res = get_byte(bits, bitpos + 45);
|
||||
if (res.err)
|
||||
continue;
|
||||
}
|
||||
sum = res.data;
|
||||
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
b = bitbuffer->bb[0];
|
||||
// check preamble first 13 bits '0000 0000 0000 1'
|
||||
if ((b[0] != 0x00) || ((b[1] & 0xf8) != 0x08))
|
||||
return DECODE_FAIL_SANITY;
|
||||
// propagate MIC
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
|
||||
// parse values from buffer
|
||||
hc1 = (b[1] << 5) | (b[2] >> 3);
|
||||
hc2 = (b[2] << 6) | (b[3] >> 2);
|
||||
hc = hc1 << 8 | hc2;
|
||||
address = (b[3] << 7) | (b[4] >> 1);
|
||||
cmd = b[5] & 0x1f;
|
||||
if (bitpos < 0)
|
||||
return bitpos;
|
||||
|
||||
// Sum is (HC1 + HC2 + Addr + Cmd [+ Ext] + Type + Repeater-Hopcount
|
||||
// Type is either 6 for regular FS20 devices (switches, dimmers, ...)
|
||||
// or 0xC for FHT (radiator valves)
|
||||
sum -= hc >> 8;
|
||||
sum -= hc & 0xff;
|
||||
sum -= address;
|
||||
sum -= cmd;
|
||||
sum -= ext;
|
||||
|
||||
if ((sum < 6) || (sum > 0xC + 2))
|
||||
return DECODE_FAIL_SANITY;
|
||||
|
||||
// convert address to fs20 format (base4+1)
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
@@ -111,10 +248,12 @@ static int fs20_decode(r_device *decoder, bitbuffer_t *bitbuffer)
|
||||
|
||||
/* clang-format off */
|
||||
data = data_make(
|
||||
"model", "", DATA_STRING, "FS20",
|
||||
"model", "", DATA_STRING, (sum < 0xc) ? "FS20" : "FHT",
|
||||
"housecode", "", DATA_FORMAT, "%x", DATA_INT, hc_b4,
|
||||
"address", "", DATA_FORMAT, "%x", DATA_INT, ad_b4,
|
||||
"command", "", DATA_STRING, cmd_tab[cmd],
|
||||
"command", "", DATA_STRING, (sum < 0xc) ? cmd_tab[cmd & 0x1f] : fht_cmd_tab[cmd & 0xf],
|
||||
"flags", "", DATA_STRING, (sum < 0xc) ? flags_tab[cmd >> 5] : fht_flags_tab[cmd >> 5],
|
||||
"ext", "", DATA_FORMAT, "%x", DATA_INT, ext,
|
||||
NULL);
|
||||
/* clang-format on */
|
||||
decoder_output_data(decoder, data);
|
||||
@@ -127,16 +266,18 @@ static char const *const output_fields[] = {
|
||||
"housecode",
|
||||
"address",
|
||||
"command",
|
||||
"flags",
|
||||
"ext",
|
||||
NULL,
|
||||
};
|
||||
|
||||
r_device const fs20 = {
|
||||
.name = "FS20",
|
||||
.modulation = OOK_PULSE_PPM,
|
||||
.modulation = OOK_PULSE_PWM,
|
||||
.short_width = 400,
|
||||
.long_width = 600,
|
||||
.reset_limit = 9000,
|
||||
.decode_fn = &fs20_decode,
|
||||
.disabled = 1, // missing MIC and no sample data
|
||||
.disabled = 0,
|
||||
.fields = output_fields,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user