merbanan / rtl_433

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

RFXMeter decoder #2141

Open dblas opened 1 year ago

dblas commented 1 year ago

For those who are looking to use rtl_433 in place of rfxcom to decode RFXMeter frames here is the decoder. Didn't find something here.

Some incertainties about the packet's end. Sometimes it's necessary to skim signal if it's too noisy with something like -Y level=-10. Would be more useful to apply a low-band filter or a average filter like the one in URH.

The data has the same meaning and order as through RFXCom. Value is therefore 256 (256 4th data byte + 2nd data byte ) + 3rd data byte. 1st byte is the channel. 5th byte is type.

RFXMeter is using X10RF-like frame with some variations.


File rfxmeter.conf


# The device uses PPM encoding,
# - 0 is encoded as 1 ms pulse and 1 ms gap,
# - 1 is encoded as 1 ms pulse and 3 ms gap.
# A packet starts with a 18 ms pulse, followed by a 8 ms gap us pulse. 
#
# Packet is 165 ms long, message is 139 ms long. 
# Packet is repeated 4 times for each address.
#
# There is 70 ms between each packet.
# Message ends with a 0 (10 then). True? (Or is it 1000 => 141 and 167 ms long and then 68 ms between packets).

frequency 433.92M

decoder {
    name         = rfxmeter,
    modulation   = OOK_PPM,
    bits         = 48,
    short        = 1000,
    long         = 2000,
    gap          = 8000,
    reset        = 2900,
    get          = address:@0:{8},
    get          = address_complemented:@8:{8},
    get          = msb:@32:{8},
    get          = isb:@16:{8},
    get          = lsb:@24:{8},
    get          = packet_type:@40:{4},
    get          = parity:@44:{4},
}
zuckschwerdt commented 1 year ago

Thanks! Is there any kind of checksum or validation possible? We might want to turn this into full decoder if so.

What's in the byte between channel and value (@8:{8})?

dblas commented 1 year ago

Here is the definition coming from the RFXCom (I understand that this product was renamed RFXtrx).

The frame sent by the RFXMeter seems to be identical when seen directly or through the RFXtrx. We can then benefit from this.

So. Message is 48-byte long.

|     Byte 0    |     Byte 1    |     Byte 2    |     Byte 3    |     Byte 4    |     Byte 5    |
 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
 4               4                   3                   2                   1
 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 
 < - - - - - - address - - - - > < - - - - - - - - counter value - - - - - - - > < - - > <parity>
                                 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 2 2 2 2 1 1 1 1    | 
                                 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 3 2 1 0 9 8 7 6    packettype

2 bytes address. Byte 2 = byte 1 with the complement (bit 7-4). In this way, a maximum of 256 modules can be used. The RFXPwr module counter is capable of measuring with an accuracy of 0.001kWh and can count from 0 to 16777.215 kWh.

Type of packets:

0000 normal data packet
0001 setting a new time interval.
   Byte 2 
            0x01 30 seconds 0x02 1 minute
            0x04 6 minutes (RFXPower = 5 minutes) 
            0x08 12 minutes (RFXPower = 10 minutes) 0x10 15 minutes
            0x20 30 minutes
            0x40 45 minutes
            0x80 60 minutes
0010 calibration value in <counter value> in μsec.
0011 setting a new address
0100 counter value reset to 0 1011 enter counter value
1100 enter interval setting mode within 5 seconds
1101 enter calibration mode within 5 seconds
1110 enter address setting mode within 5 seconds
1111 identification packet
      Byte 2 = firmware version 
              0x00 - 0x3F = RFXPower
              0x40 - 0x7F = RFU
              0x80 - 0xBF = RFU
              0xC0 - 0xFF = RFXMeter
      Byte 3 = time interval (see packet type 0001)

4 bits for parity computed as complement to: byte0 bit7-4 + byte0 bit3-0 + byte1 bit7-4 + byte1 bit3-0 + byte2 bit7-4 + byte2 bit3-0 + byte3 bit7-4 + byte3 bit3-0 + byte4 bit7-4 + byte4 bit3-0 + byte5 bit7-4

Enjoy! db

zuckschwerdt commented 1 year ago

Very thorough information, thanks! I think it would be good to use the validation of the address and the parity and make this a full decoder with checking. I might be able to do this next week.

dblas commented 1 year ago

Very thorough information, thanks! I think it would be good to use the validation of the address and the parity and make this a full decoder with checking. I might be able to do this next week.

As you wish. No time on my side for this. Could be integrated in the main stream then: one more decoder for this great project! Thanks. db

dblas commented 1 year ago

Changed the get part of the decoder according to the documentation herebefore. db

zuckschwerdt commented 1 year ago

Can you grab a sample with -S unknown then test it works with your flex decoder and upload here as zip?

dblas commented 1 year ago

Here are in-row 2 recordings of the RFXMeter I have. I confirm they are correctly decoded with the flex. For the g004 it gives (-F json):

{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced81905", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 216, "packet_type" : 0, "parity" : 5}], "codes" : ["{48}01f1ced81905"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced81905", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 216, "packet_type" : 0, "parity" : 5}], "codes" : ["{48}01f1ced81905"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced81905", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 216, "packet_type" : 0, "parity" : 5}], "codes" : ["{48}01f1ced81905"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced81905", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 216, "packet_type" : 0, "parity" : 5}], "codes" : ["{48}01f1ced81905"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced81905", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 216, "packet_type" : 0, "parity" : 5}], "codes" : ["{48}01f1ced81905"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f25907750b", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 7, "packet_type" : 0, "parity" : 11}], "codes" : ["{48}02f25907750b"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f25907750b", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 7, "packet_type" : 0, "parity" : 11}], "codes" : ["{48}02f25907750b"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f25907750b", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 7, "packet_type" : 0, "parity" : 11}], "codes" : ["{48}02f25907750b"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f25907750b", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 7, "packet_type" : 0, "parity" : 11}], "codes" : ["{48}02f25907750b"]}
{"time" : "@0.204888s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f25907750b", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 7, "packet_type" : 0, "parity" : 11}], "codes" : ["{48}02f25907750b"]}

For the g006 it gives:

{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced91904", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 217, "packet_type" : 0, "parity" : 4}], "codes" : ["{48}01f1ced91904"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced91904", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 217, "packet_type" : 0, "parity" : 4}], "codes" : ["{48}01f1ced91904"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced91904", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 217, "packet_type" : 0, "parity" : 4}], "codes" : ["{48}01f1ced91904"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "01f1ced91904", "address" : 1, "address_complemented" : 241, "msb" : 25, "isb" : 206, "lsb" : 217, "packet_type" : 0, "parity" : 4}], "codes" : ["{48}01f1ced91904"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f259097509", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 9, "packet_type" : 0, "parity" : 9}], "codes" : ["{48}02f259097509"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f259097509", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 9, "packet_type" : 0, "parity" : 9}], "codes" : ["{48}02f259097509"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f259097509", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 9, "packet_type" : 0, "parity" : 9}], "codes" : ["{48}02f259097509"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f259097509", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 9, "packet_type" : 0, "parity" : 9}], "codes" : ["{48}02f259097509"]}
{"time" : "@0.106964s", "model" : "rfxmeter", "count" : 1, "num_rows" : 1, "rows" : [{"len" : 48, "data" : "02f259097509", "address" : 2, "address_complemented" : 242, "msb" : 117, "isb" : 89, "lsb" : 9, "packet_type" : 0, "parity" : 9}], "codes" : ["{48}02f259097509"]}

2 RFXMeter traces.zip db

zuckschwerdt commented 1 year ago

The PR #2142 should now basically work. Proper type decoding needs to be added, but please test.

Interestingly the timing is double-speed from what you describe, i.e.

The device uses PPM encoding,

A packet starts with a 9 ms pulse, followed by a 4 ms gap us pulse. There is 38 ms between each packet.

dblas commented 1 year ago

Interesting indeed. These timings were given by URH, following a direct capture, and, indeed, wondered why they appeared twice as much as those for x10RF (on which RFXMeter is based). I didn't compare with another software (gnuradio). There is another unknown: what is the value of the final bit of the message (0 which means a 10 signal or 1 which means a 1000 signal)? Thanks. db

zuckschwerdt commented 1 year ago

About the timings, maybe there was confusion about the sample rate -- that directly links to the timing. Or maybe the file was imported as 16-bit samples (native is 8 bit I/Q), but that would "speed up" the timings.

If you look at the signal in PDV (switch to PPM and enter the timings) you'll note that the last bit is before the last pulse. I.e. in PPM there should be N+1 pulses for N bits.

dblas commented 1 year ago

About the timings, maybe there was confusion about the sample rate -- that directly links to the timing. Or maybe the file was imported as 16-bit samples (native is 8 bit I/Q), but that would "speed up" the timings.

There was no import, URH generated itself a trace following the capture. And such a software is supposed to be consistant with itself when the timings are measured from a capture made by itself. No matter. I've just made another capture with another software and saved an audio extract. You're right, timings are more about 9 ms for the preamble, 4 or 5 ms then and ~80 ms for each packet than 18, 9 and 150 respectively. But, why do the attributes' values work - in the flex decoder - work in this case? db

zuckschwerdt commented 1 year ago

If a decoder is not instructed to use strict timings then only a discrimination (above/below) is used.

dblas commented 1 year ago

And how do you instruct a decoder to use strict timings?

BTW, triq.org never worked for me (the web server doesn't answer whichever port, 80 or 443, is reached).

zuckschwerdt commented 1 year ago

And how do you instruct a decoder to use strict timings?

with the tolerance= parameter.

BTW, triq.org never worked for me

Pretty strict anti-spam rules on that server, sorry. If you are in France then chances are the OVH rules are over-blocking. Or maybe it's a VPN. Try https://scamalytics.com/ to see if you are flagged, or https://dnslytics.com/ with your IP for a BL lookup.

dblas commented 1 year ago

And how do you instruct a decoder to use strict timings?

with the tolerance= parameter. Ah, ok, I know it :) And it's not filled in in my flex decoder. You're right.

BTW, triq.org never worked for me

Pretty strict anti-spam rules on that server, sorry. If you are in France then chances are the OVH rules are over-blocking. Or maybe it's a VPN. Try https://scamalytics.com/ to see if you are flagged, or https://dnslytics.com/ with your IP for a BL lookup.

Nope. My Internet provider is indeed OVH but the addressing plan is separated from those that serve the cloud stuff.

Scamalytics says the risk is low and dnslytics doesn't reference any BL about me. db

zuckschwerdt commented 1 year ago

provider is indeed OVH but the addressing plan is separated from those that serve the cloud stuff.

Currently most of ASN16276 (about 50 /16 IPv4 prefixes) from OVH is blocked. Which prefix (/16 or /24) are you on? I'll unblock that until I see spam and abuse again.

dblas commented 1 year ago

provider is indeed OVH but the addressing plan is separated from those that serve the cloud stuff.

Currently most of ASN16276 (about 50 /16 IPv4 prefixes) from OVH is blocked. Which prefix (/16 or /24) are you on? I'll unblock that until I see spam and abuse again.

It doesn't matter. Forget it. I've the capability to establish VPN with other machines located on other providers or even using my phones (tethering). It was useful to know where was the pb. Now that I know what's going on I can circumvent it. db

dblas commented 1 year ago

About PDV Very nice tool indeed! Is that you who developed this site?

BRAVO! db

zuckschwerdt commented 1 year ago

Thanks! Yes, some tooling around rtl_433 and SDR in general to make hacking easier. I'll see if I can host the domain on Netlify -- where I don't care about the DDOS and other abuse ;)

dblas commented 1 year ago

For your information I'm located in a city with many households around and near a busy street. rtl_433 has been running now for 2 days continuously. The -S unknown is enabled only since this morning. So far, I have collected 27 different protocols (RFXMeter not included) and no unknown protocols. Good job. In the future I'll will install another RTL on the other side of the building with a Yagi antenna in order to collect other frames (there are other streets over there :)).

These protocols are:

2
3
12
19
36
50
52
53
54
60
73
82
83
85
88
89
90
92
131
148
156
178
186
191
197
201
212

db

zuckschwerdt commented 1 year ago

Thanks! It's very interesting to hear rtl_433 now has such a wide coverage (no unknown's).

dblas commented 1 year ago

Thanks! It's very interesting to hear rtl_433 now has such a wide coverage (no unknown's).

Yes, it's now competing with solutions like RFXCom! db

dblas commented 1 year ago

About the pull, it seems to be working perfectly (with -R 224). Here is what it gives. Nice for having done the direct counter conversion.

{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 1, "msg_type" : 0, "msg_value" : 1724108, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 1, "msg_type" : 0, "msg_value" : 1724108, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 1, "msg_type" : 0, "msg_value" : 1724108, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 1, "msg_type" : 0, "msg_value" : 1724108, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 1, "msg_type" : 0, "msg_value" : 1724108, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 2, "msg_type" : 0, "msg_value" : 7723305, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 2, "msg_type" : 0, "msg_value" : 7723305, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 2, "msg_type" : 0, "msg_value" : 7723305, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 2, "msg_type" : 0, "msg_value" : 7723305, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}
{"time" : "2022-08-13 19:40:54.256941", "protocol" : 224, "model" : "RfxMeter", "id" : 2, "msg_type" : 0, "msg_value" : 7723305, "mic" : "PARITY", "mod" : "ASK", "freq" : 433.886, "rssi" : -0.248, "snr" : 28.235, "noise" : -28.483}

All good, Have a nice we, db

gdt commented 9 months ago

Has this code been merged? If not what is the PR? What is the status and plan?

gdt commented 1 month ago

Status? Is this now captured in #2142 ?

dblas commented 1 month ago

I've been using it for 2 years now w/o any problem. What could I do to push it onto the main line? db

gdt commented 1 month ago

I can't really understand your comment. Are you saying that you are running rtl_433 built from current master with the changes in #2142, and it is satisfactory? Or are you running the code in the PR which is perhaps old? If either is true, adding a comment to the PR saying exactly what you are doing and what works and doesn't work and what remains to be done would be useful.

Or do you mean something different?

I see progress as multiple steps. One is turning an issue into a PR which if merged solves it, and the second is merging the PR. My real question here is if the PR supercedes the issue, in which case we can close the issue.