merbanan / rtl_433

Program to decode radio transmissions from devices on the ISM bands (and other frequencies)
GNU General Public License v2.0
6.1k stars 1.32k forks source link

Help decoding Watts Thermostat #2230

Closed aadnehovda closed 1 year ago

aadnehovda commented 1 year ago

I have a 12 zone underfloor heating system with wireless thermostats which each have a wheel for setting temperature and a thermistor for sensing the current temp. MASTER-RAIL NY.PDF

The data is PWM encoded with a duty cycle of approx. 1/3, which is correctly detected by the analyzer. Here shown in pulseview: image

Another sample: https://triq.org/pdv/#AAB1041850025C0108F3208291A291A1A291A291A1A2929291A291A1A2929291A29291A1A1A1A1A2929291A1A1A1A1A29292929291A291A1A291A291A1A1A1A1A1A355

I have started to analyze the data received from the sensors and found the ID and the ANNOUNCE flag (used for pairing with the controller). After power on the thermostat quickly sends 5 packets, then settles down to a slower pace, I think perhaps the pace is also dependent on the current temp versus the set-point, but I'm not sure.

I expect at least the current temp and the set point, but maybe not in real units like C or F, since the controller does not really need it. Also maybe some checksum. The following bitbench is a power on of a single thermostat and then a gradual sweep of the set-point wheel from min to max. All performed within around 30 seconds, so the sensor value probably did not change much. image

http://triq.net/bitbench#c=%7B54%7D5ab24970f799e4&c=%7B54%7D5ab24978f79be4&c=%7B54%7D5ab24978f79be4&c=%7B54%7D5ab24978f79be4&c=%7B54%7D5ab249f8f79814&c=%7B54%7D5ab249f8f79814&c=%7B54%7D5ab249f8f686ec&c=%7B54%7D5ab249f0f01620&c=%7B54%7D5ab249ff707408&c=%7B54%7D5ab249f8f07508&c=%7B54%7D5ab249f8f0cd58&c=%7B54%7D5ab249f0f0ce58&c=%7B54%7D5ab249f0f25f14&c=%7B54%7D5ab249f8f65e94&f=ID%3Ahhhh%20ANNOUNCE%3Ab%20bbbbbbbbbbbbbbbbbbbbbbbbbbbbb%0A&a=Preamble&m=5a&i=true&co=24&cl=22&cw=2

aadnehovda commented 1 year ago

Current conf-file I'm using:


sample_rate 2M

decoder {
    name=WattsThermostat,
    modulation=OOK_PWM,
    short=260,
    long=600,
    sync=6000,
    gap=0,
    bits=54,
    reset=900,
    match={8}5a,
    get=@8:{16}:id,
    get=@24:{1}:pairing
}
zuckschwerdt commented 1 year ago

Indeed looks like PWM: we observe 4 pulse/gap-symbols: long/long, long/short, short/short, short/long -- but when looking at gap/pulse-symbols: long/short, short/long only.

The pulse sample is {62} 5A C5 89 F1 F0 5A FC but the BitBench has shorter codes (54) -- can you check that? The possible checksum looks roughly XOR-based, but we need to be sure about alignment and size to inspect it. Then bitbrk from revdgst can uncover a relation given enough codes.

aadnehovda commented 1 year ago

The pulse sample is {62} 5A C5 89 F1 F0 5A FC but the BitBench has shorter codes (54) -- can you check that?

I noticed that too, but it only shows up as 62 bits on the web page (where the last 8 bits are always 0). The incoming data is always 54 bits.

Here's a sample from the analyzer:

2022-11-07 15:26:34,,{54}5ab249f5f79a54,WattsThermostat,1,1,
Analyzing pulses...
Total count:   55,  width: 53.24 ms             (106475 S)
Pulse width distribution:
 [ 0] count:    1,  width: 6238 us [6238;6238]  (12475 S)
 [ 1] count:   23,  width:  632 us [630;645]    (1264 S)
 [ 2] count:   31,  width:  286 us [284;296]    ( 572 S)
Gap width distribution:
 [ 0] count:   23,  width:  238 us [238;240]    ( 477 S)
 [ 1] count:   31,  width:  584 us [582;585]    (1167 S)
Pulse period distribution:
 [ 0] count:    1,  width: 6475 us [6475;6475]  (12950 S)
 [ 1] count:   17,  width: 1216 us [1214;1225]  (2431 S)
 [ 2] count:   16,  width:  525 us [524;534]    (1050 S)
 [ 3] count:   20,  width:  870 us [869;884]    (1740 S)
Pulse timing distribution:
 [ 0] count:    1,  width: 6238 us [6238;6238]  (12475 S)
 [ 1] count:   54,  width:  604 us [582;645]    (1208 S)
 [ 2] count:   54,  width:  266 us [238;296]    ( 531 S)
 [ 3] count:    1,  width: 62376 us [62376;62376]       (124751 S)
Level estimates [high, low]:   7572,    119
RSSI: -3.4 dB SNR: 18.0 dB Noise: -21.4 dB
Frequency offsets [F1, F2]:    7148,      0     (+218.1 kHz, +0.0 kHz)
Guessing modulation: Pulse Width Modulation with sync/delimiter
view at https://triq.org/pdv/#AAB104185D025C0109F3A78291A291A1A291A291A291A1A29291A29291A29291A29291A1A1A1A1A291A291A1A1A1A1A291A1A1A1A29291A1A291A29291A291A291A355
Attempting demodulation... short_width: 286, long_width: 632, reset_limit: 586, sync_width: 6238
Use a flex decoder with -X 'n=name,m=OOK_PWM,s=286,l=632,r=586,g=0,t=0,y=6238'
pulse_demod_pwm(): Analyzer Device
bitbuffer:: Number of rows: 1
[00] {54} 5a b2 49 f5 f7 9a 54

I find it weird that there are so many bits changing between samples when I only adjust the set-point wheel, I would have thought some kind of pattern emerge, but I can't really see it. Could the payload be double-encoded somehow?

aadnehovda commented 1 year ago

I turned on and put one device inside the refrigerator and captured the data, including timestamps. This should provide some data about changing thermistor-values only, without any changes to the set-point.


2022-11-07 15:39:14,,{54}5ab2497ef39894,WattsThermostat,1,1,
2022-11-07 15:39:15,,{54}5ab24971f39a94,WattsThermostat,1,1,
2022-11-07 15:39:15,,{54}5ab24971f79994,WattsThermostat,1,1,
2022-11-07 15:39:16,,{54}5ab24971f79994,WattsThermostat,1,1,
2022-11-07 15:39:16,,{54}5ab249f1f79b94,WattsThermostat,1,1,
2022-11-07 15:39:19,,{54}5ab249f1f79b94,WattsThermostat,1,1,
2022-11-07 15:39:22,,{54}5ab249f9f79854,WattsThermostat,1,1,
2022-11-07 15:39:25,,{54}5ab249f5f79a54,WattsThermostat,1,1,
2022-11-07 15:39:37,,{54}5ab249f68f998c,WattsThermostat,1,1,
2022-11-07 15:39:39,,{54}5ab249f98f9a4c,WattsThermostat,1,1,
2022-11-07 15:39:42,,{54}5ab249f58b9a4c,WattsThermostat,1,1,
2022-11-07 15:39:45,,{54}5ab249fb8f9acc,WattsThermostat,1,1,
2022-11-07 15:39:51,,{54}5ab249f44f992c,WattsThermostat,1,1,
2022-11-07 15:39:54,,{54}5ab249f24b9b2c,WattsThermostat,1,1,
2022-11-07 15:39:57,,{54}5ab249fa4f9aac,WattsThermostat,1,1,
2022-11-07 15:40:00,,{54}5ab249fe4f9bac,WattsThermostat,1,1,
2022-11-07 15:40:08,,{54}5ab249f34f98ec,WattsThermostat,1,1,
2022-11-07 15:40:11,,{54}5ab249fb4f9aec,WattsThermostat,1,1,
2022-11-07 15:40:14,,{54}5ab249f8cf9a1c,WattsThermostat,1,1,
2022-11-07 15:40:17,,{54}5ab249f4cf991c,WattsThermostat,1,1,
2022-11-07 15:40:26,,{54}5ab249f1cf985c,WattsThermostat,1,1,
2022-11-07 15:40:29,,{54}5ab249f9cf9a5c,WattsThermostat,1,1,
2022-11-07 15:40:32,,{54}5ab249f9cf9a5c,WattsThermostat,1,1,
2022-11-07 15:40:40,,{54}5ab249f7cf99dc,WattsThermostat,1,1,
2022-11-07 15:40:43,,{54}5ab249ffcf9bdc,WattsThermostat,1,1,
2022-11-07 15:40:49,,{54}5ab249fc2f9b3c,WattsThermostat,1,1,
2022-11-07 15:40:52,,{54}5ab249fa2b98bc,WattsThermostat,1,1,
2022-11-07 15:40:54,,{54}5ab249f62b9abc,WattsThermostat,1,1,
2022-11-07 15:40:57,,{54}5ab249fe2f9bbc,WattsThermostat,1,1,
2022-11-07 15:41:00,,{54}5ab249f92b987c,WattsThermostat,1,1,
2022-11-07 15:41:03,,{54}5ab249f52f997c,WattsThermostat,1,1,
2022-11-07 15:41:12,,{54}5ab249f32f98fc,WattsThermostat,1,1,
2022-11-07 15:41:14,,{54}5ab249fb2f9afc,WattsThermostat,1,1,
2022-11-07 15:41:17,,{54}5ab249ff2f9bfc,WattsThermostat,1,1,
2022-11-07 15:41:26,,{54}5ab249fcaf9b00,WattsThermostat,1,1,
2022-11-07 15:41:29,,{54}5ab249f2ab9b00,WattsThermostat,1,1,
2022-11-07 15:41:31,,{54}5ab249faaf9a80,WattsThermostat,1,1,
2022-11-07 15:41:34,,{54}5ab249f6af9980,WattsThermostat,1,1,
2022-11-07 15:41:37,,{54}5ab249f1ab9b80,WattsThermostat,1,1,
2022-11-07 15:41:43,,{54}5ab249f9ab9840,WattsThermostat,1,1,
2022-11-07 15:41:46,,{54}5ab249f5ab9a40,WattsThermostat,1,1,
2022-11-07 15:41:48,,{54}5ab249fdab9940,WattsThermostat,1,1,
2022-11-07 15:41:51,,{54}5ab249f3ab9b40,WattsThermostat,1,1,
2022-11-07 15:41:54,,{54}5ab249fbaf9ac0,WattsThermostat,1,1,
2022-11-07 15:41:57,,{54}5ab249ffab99c0,WattsThermostat,1,1,
2022-11-07 15:42:00,,{54}5ab249f06b9bc0,WattsThermostat,1,1,
2022-11-07 15:42:05,,{54}5ab249fc6b9920,WattsThermostat,1,1,
2022-11-07 15:42:08,,{54}5ab249fc6b9920,WattsThermostat,1,1,
2022-11-07 15:42:25,,{54}5ab249f96f9a60,WattsThermostat,1,1,
2022-11-07 15:42:28,,{54}5ab249fd6b9960,WattsThermostat,1,1,
2022-11-07 15:42:31,,{54}5ab249f56b9a60,WattsThermostat,1,1,
2022-11-07 15:42:33,,{54}5ab249f36b9b60,WattsThermostat,1,1,
2022-11-07 15:42:36,,{54}5ab249fb6b98e0,WattsThermostat,1,1,
2022-11-07 15:42:39,,{54}5ab249f76f99e0,WattsThermostat,1,1,
2022-11-07 15:42:47,,{54}5ab249f8eb9810,WattsThermostat,1,1,
2022-11-07 15:42:50,,{54}5ab249f8ef9a10,WattsThermostat,1,1,
2022-11-07 15:42:53,,{54}5ab249f8eb9810,WattsThermostat,1,1,
2022-11-07 15:42:59,,{54}5ab249f2eb9b10,WattsThermostat,1,1,
2022-11-07 15:43:01,,{54}5ab249faeb9890,WattsThermostat,1,1,
2022-11-07 15:43:04,,{54}5ab249faef9a90,WattsThermostat,1,1,
2022-11-07 15:44:57,,{54}5ab249f49f9908,WattsThermostat,1,1,
2022-11-07 15:46:49,,{54}5ab249fa5f9aa8,WattsThermostat,1,1,
2022-11-07 15:48:40,,{54}5ab249f8df9a18,WattsThermostat,1,1,
2022-11-07 15:50:30,,{54}5ab249f5df9958,WattsThermostat,1,1,
zuckschwerdt commented 1 year ago

Was going to suggest that, slowly changing just the temperature so we can observe where that field is. If you use just v as format in BitBench you can make out the temperature field in the middle and the checksum at the end.

Exactly the last 8 bit do change but not random, looks like a counter. The 8 bits before that never change, the 10 bits before that do change but the pattern there is not clear.

I'd guess LSB/MSB are reversed, see this BitBench.

aadnehovda commented 1 year ago

Wow, that's good progress. I noticed I had experimented with inverting the data and left it flipped in the bitbench. When I flip it back it indeed counts from 0-255 and rolls over.

zuckschwerdt commented 1 year ago

Also the "10 bit" field does count down, if we put the last two bits somewhere(?) else: SYN?8h ID:16h FLAGS?4b TEMP?^8d 2b SETP?^8h ^CHK?8d

zuckschwerdt commented 1 year ago

For the two extra temp bits the left(earlier) one seems to be either a sign or MSB, the right one seems to be the temp LSB. The checksum is not by byte but by "field", note that it is in step (1 unit count) with all of: 8-bit temp field, announce flag, the temp LSB bit. Not sure where the temp sign/MSB or the other flags go into the checksum though.

aadnehovda commented 1 year ago

Okay, so perhaps it makes sense to keep the data inverted. I noticed this text in the datasheet for the receiver. image The analyzer defaults to long pulse => logic 0, while the test instructions for the chip has it the other way. I'd guess the people implementing the protocol just picked the same encoding as in the datasheet. At least the temp readings look better that way (as you so quickly found :-), and the counter could very well be designed to decrement.

zuckschwerdt commented 1 year ago

There is no common understanding how symbols should map to 0/1. In rtl_433 we take the arbitrary (but easy to mnemonic) rule: low frequency (slow change, long symbol) -> 0 and high frequency (fast change, short symbol) -> 1.

With "counter" it just wanted to express "counting through a range of values". I don't think it's a counter but rather the temperature value. The data series was the sensor cooling down, right? Otherwise the field should be "counting" up rather...

aadnehovda commented 1 year ago

I see, makes sense. Yes, that data stream should be from around 25C to around 4C-6C which seems correct. The set-point seems to be upside down, i.e. the numbers decrease as I increase the setpoint temp. The range printed on the case is 5C -> 30C. I've added some more samples, with comments, to bitbench, including a few from a device inside the freezer (to possibly test for sign) but not sure the device supports subzero measurements, seems to stop at 0C.

zuckschwerdt commented 1 year ago

Looks like the temp field is 9-bit then. SYN?8h ID:16h FLAGS?4b TEMP?^9d SETP?^9d ^CHK?8d

And the other bit in the middle then could be the setpoint LSB -- at least it very much changes with that field. Use SETP?^9b and watch it work nicely in e.g. "Took it out from the freezer".

zuckschwerdt commented 1 year ago

Could be a coincidence but temp looks like 1/10 C, right?

aadnehovda commented 1 year ago

Yes, 9bits for the temps looks correct! I think it's pretty normal for such values to have a factor of 10 and it matches pretty well.

By the way, is there a way to reverse LSB/MSB in a flex-decoder defined in a conf-file? Was looking for a way to "get" the values directly. Browsing through flex.c I cannot find an option to do that. I tried adding negative length for the field, but that ended in a segfault :-).

Here is my current conf-file:


# Need at least 1M sample rate (maybe 0.5M is enough)
sample_rate 2M

decoder {
    name=WattsThermostat,
    modulation=OOK_PWM,
    short=260,
    long=600,
    sync=6000,
    gap=0,
    bits=54,
    reset=900,
    match={8}5a,
    get = @0:{8}:syn,
    get = @8:{16}:id,
    get = @24:{4}:%X:flags,
    get = @28:{9}:temp,
    get = @37:{9}:setpoint,
    get = @46:{8}:%X:chk
}
zuckschwerdt commented 1 year ago

Sorry, the getters are not that sophisticated. But to verify the checksum I would recommand a custom decoder anyway. For testing maybe send the raw-ish output to a python script. Should be the easiest option.

aadnehovda commented 1 year ago

It looks as if all the fields could be LSB/MSB swapped. ID:^16d resolves to an ID matching a five digit number which is hand written directly on the PCB (some poor guy had to sit down and program each device one by one. :-D) The preamble is the same bitpattern both LSB/MSB so it does not matter. For the checksum it looks as if all fields are included as you mentioned and that it is some sort of SUM.

(I added invert to the flex-conf during capture on the following data series instead of setting it in bitbench)

https://triq.net/bitbench#c=%5BFLAGS%2B1%20adds%201%20to%20CHK%5D&c=%7B54%7Da5b476090862ac&c=%7B54%7Da5b476890861ac&c=%5BAlmost%20same%20readings%2C%20CHK%20seems%20to%20be%20offset%20by%20higher%20ID%5D&c=%7B54%7Da53a760a0ea37c&c=%7B54%7Da53a768a0ea0fc&c=%5BTEMP%2B1%20adds%201%20to%20CHK%5D&c=%7B54%7Da53a76860ea2fc&c=%5BSETP%2B1%20adds%201%20to%20CHK%5D&c=%7B54%7Da53a7683f7a7bc&c=%7B54%7Da53a7683f0647c&c=%7B54%7Da53a768bf7a47c&c=%7B54%7Da53a7687f7a67c&f=ID%3A%5E16d%20FLAGS%3A%5E4b%20TEMP%3A%5E9d%20SETP%3A%5E9d%20CHK%3A%5E8d%0A&a=Preamble&m=a5&cl=40&cw=8

zuckschwerdt commented 1 year ago

Very nice. If you try adding the checksum on the larger dataset there are minor problems though -- some rows differ by 1, some bit is still off.

aadnehovda commented 1 year ago

FYI, my system is a somewhat older OEM system with a different brand name, but I found a current product which I assume is compatible with the protocol. WATTS WFHT-RF Basic (P01857). The PCB looks kind of similar, or at least like it evolved.

Theirs: (OF: 252063, some revision number?) image

Mine: (OF: 221439, printed on the box) image

aadnehovda commented 1 year ago

I adapted some code, let me know if this should go in a PR or whether chksum validation is required first.

/** @file
    Watts WFHT-RF Thermostat

    Copyright (C) 2022 Ådne Hovda <aadne@hovda.no>
    based on protocol decoding by Christian W. Zuckschwerdt <zany@triq.net>
    and Ådne Hovda <aadne@hovda.no>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
*/

#include "decoder.h"

/**
Watts WFHT-RF Thermostat

This code is based on a slightly older OEM system created by ADEV in France which 
later merged with Watts. The closest thing currently available seems to be 
https://wattswater.eu/catalog/regulation-and-control/radio-wfht-thermostats/electronic-room-thermostat-with-rf-control-wfht-rf-basic/,
but it is not known whether they are protocol compatible.

https://github.com/merbanan/rtl_433/issues/2230

TODO: chksum

Analyzer output:
    Analyzing pulses...
    Total count:   55,  width: 337.75 ms            (675502 S)
    Pulse width distribution:
     [ 0] count:    1,  width: 291096 us [291096;291096]    (582191 S)
     [ 1] count:   26,  width:  630 us [630;640]    (1260 S)
     [ 2] count:   28,  width:  288 us [286;302]    ( 576 S)
    Gap width distribution:
     [ 0] count:   26,  width:  233 us [232;234]    ( 466 S)
     [ 1] count:   28,  width:  576 us [576;576]    (1151 S)
    Pulse period distribution:
     [ 0] count:    1,  width: 291328 us [291328;291328]    (582657 S)
     [ 1] count:   15,  width: 1206 us [1205;1216]  (2412 S)
     [ 2] count:   14,  width:  522 us [520;530]    (1043 S)
     [ 3] count:   24,  width:  864 us [862;877]    (1727 S)
    Pulse timing distribution:
     [ 0] count:    1,  width: 291096 us [291096;291096]    (582191 S)
     [ 1] count:   54,  width:  602 us [576;640]    (1204 S)
     [ 2] count:   54,  width:  262 us [232;302]    ( 523 S)
     [ 3] count:    1,  width: 100000 us [100000;100000]    (200001 S)
    Level estimates [high, low]:  15961,    150
    RSSI: -0.1 dB SNR: 20.3 dB Noise: -20.4 dB
    Frequency offsets [F1, F2]:   17869,      0     (+545.3 kHz, +0.0 kHz)
    Guessing modulation: Pulse Width Modulation with sync/delimiter
    view at https://triq.org/pdv/#AAB104FFFF025A0105FFFF8291A291A1A291A29291A29291A291A1A1A2929291A29291A1A1A1A1A29291A291A1A1A1A291A1A29291A1A29291A29291A1A2929291A355
    Attempting demodulation... short_width: 288, long_width: 630, reset_limit: 577, sync_width: 291096
    Use a flex decoder with -X 'n=name,m=OOK_PWM,s=288,l=630,r=577,g=0,t=0,y=291096'
    pulse_slicer_pwm(): Analyzer Device
    bitbuffer:: Number of rows: 1
    [00] {54} 5a 4b 89 f2 f6 64 c4

Data Layout:

    10100101   1011010001110110   1000   100100001   000011000   10101011
    preamble   id                 flags  temp         setpoint   chksum

    All fields need reflection, possibly easier to just reverse the whole 
    row first. The only flag found is PAIRING (0b0001). Chksum seems to be 
    additive.

Raw data:

    {54}5ab24971f79994
    {54}5ab24971f79994
    {54}5ab249f1f79b94
    {54}5ab249f1f79b94
    {54}5ab249f9f79854
    {54}5ab249f5f79a54
    {54}5ab249f68f998c
    {54}5ab249f98f9a4c
    {54}5ab249f58b9a4c
    {54}5ab249fb8f9acc

    https://tinyurl.com/2z5jtfuu

    Format string:
    ID:^16d FLAGS:^4b TEMP:^9d SETP:^9d CHK:^8d

Decoded example:

    ID:28205 FLAGS:0001 TEMP:265 SETP:048 CHK:214

*/

static int watts_thermostat_decode(r_device *decoder, bitbuffer_t *bitbuffer)
{
    int ret                          = 0;

    if (bitbuffer->num_rows != 1) {
        decoder_logf(decoder, 2, __func__, "Only expect a single row. num_rows=%d", bitbuffer->num_rows);
        ret = DECODE_ABORT_EARLY;
        return ret;
    }

    uint8_t const preamble_pattern = 0xa5;

    for (unsigned row = 0; row < bitbuffer->num_rows; ++row) {
        // we expect 54 bits
        if (bitbuffer->bits_per_row[row] != 54) {
            decoder_log(decoder, 2, __func__, "Length check fail");
            ret = DECODE_ABORT_LENGTH;
            continue;
        }

        bitbuffer_invert(bitbuffer);

        unsigned pos = bitbuffer_search(bitbuffer, row, 0, &preamble_pattern, 8);

        if (pos >= bitbuffer->bits_per_row[row]) {
            decoder_log(decoder, 2, __func__, "Preamble not found");
            ret = DECODE_ABORT_EARLY;
            continue;
        }
        decoder_logf(decoder, 2, __func__, "Found row: %d", row);

        pos += 8;

        uint8_t id_raw[2] = {0};
        bitbuffer_extract_bytes(bitbuffer, row, pos, id_raw, 16);
        unsigned id = (reverse8((id_raw[1])) << 8) | reverse8((id_raw[0]));

        pos += 16;

        uint8_t PAIRING = 0b0001;
        uint8_t flags   = {0};
        bitbuffer_extract_bytes(bitbuffer, row, pos, &flags, 4);
        flags = reverse8(flags);
        unsigned pairing = flags & PAIRING;

        pos += 4;

        uint8_t temp_raw[2] = {0};
        bitbuffer_extract_bytes(bitbuffer, row, pos, temp_raw, 9);
        unsigned temp = (reverse8((temp_raw[1])) << 8) | reverse8((temp_raw[0]));

        pos += 9;

        uint8_t setp_raw[2] = {0};
        bitbuffer_extract_bytes(bitbuffer, row, pos, setp_raw, 9);
        unsigned setp = (reverse8((setp_raw[1])) << 8) | reverse8((setp_raw[0]));

        pos += 9;

        uint8_t chk_raw = {0};
        bitbuffer_extract_bytes(bitbuffer, row, pos, &chk_raw, 8);
        unsigned chk = reverse8(chk_raw);

        //// verify checksum
        // if ((add_bytes(b, 31) & 0xff) != b[31]) {
        //     decoder_log(decoder, 2, __func__, "Checksum fail");
        //     ret = DECODE_FAIL_MIC;
        //     continue;
        // }

        /* clang-format off */
        data_t *data = data_make(
            "model",            "Model",            DATA_STRING, "Watts WFHT-RF Thermostat",
            "id",               "ID",               DATA_INT,    id,
            "pairing",          "Pairing",          DATA_COND, pairing,   DATA_INT,    pairing,
            "temperature_C",    "Temperature",      DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp * 0.1f,
            "setpoint_C",       "Setpoint",         DATA_FORMAT, "%.1f C", DATA_DOUBLE, setp * 0.1f,
            NULL);
        /* clang-format on */

        decoder_output_data(decoder, data);
        return 1;
    }
    return ret;
}

static char *output_fields[] = {
        "model",
        "id",
        "pairing",
        "temperature_C",
        "setpoint_C",
        NULL,
};

r_device watts_thermostat = {
        .name        = "Watts WFHT-RF Thermostat",
        .modulation  = OOK_PULSE_PWM,
        .short_width = 260,
        .long_width  = 600,
        .sync_width  = 6000,
        .reset_limit = 900,
        .decode_fn   = &watts_thermostat_decode,
        .fields      = output_fields,
};

image

zuckschwerdt commented 1 year ago

Looks good. The first line after /** needs a dot (.) a the end. You can start a PR and then add more commits as you go along. We always squash commits on merge.

aadnehovda commented 1 year ago

So I think I've figured out the checksum. It's a 1 byte sum of all the decoded fields except the preamble, but each value is chopped up into max 1 byte pieces. So the id(16b), temp(9b) and setpoint(9b) are split into high and low bytes.

chksum = ((id >> 8) + (id & 0xFF) + flags + (temp >> 8) + (temp & 0xFF) + (setp >> 8) + (setp & 0xFF)) & 0xFF
gdt commented 1 year ago

@aadnehovda Status? Looks like you made a huge amount of progress and would be great to have it in the codebase.

My take is that decoders without enough checksums have to be not on by default, but can be in the codebase. Others will surely comment if you file a PR though.

aadnehovda commented 1 year ago

Thanks for the reminder! :-)