merbanan / rtl_433

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

Decoding ANT and ANT+ packets (1 Mbps, GFSK, 160 kHz deviation) #1990

Closed robcazzaro closed 2 years ago

robcazzaro commented 2 years ago

I'm trying to understand how ANT+ works, so I'm using Pluto SDR centered at 2.466Ghz, and I can see the rapid bursts of data, together with a lot of 2.4GHz noise. To minimize interference, I'm using just a 31mm wire near the ANT device, instead of a better antenna that would pick up everything. I can see what looks like packets in URH, but I still haven't learnt enough to figure out the content.

To generate the ANT signal, I'm using a nRF SDK, and within the limitations of what the SDK exposes, I can send pretty much any data packet, which would make it easier to understand the actual encoding and any encryption at the physical layer. I'm at the moment sending the same ANT packet over and over, at 1 sec intervals, and I can clearly see a single, well defined packet every second (see below for a URH snapshot). Incidentally, ANT+ is simply a subset of ANT, using a single frequency (2.457GHz) and a specific network key)

According to the available documentation, ANT uses GFSK, 1Mbps, with 160khz deviation and Enhanced Shockburst. https://www.west-l.com/uploads/tdpdf/nrf24ap2_8ch_productbrief_tds.pdf

ANT+ is a proprietary protocol, mostly undocumented at the lower layers. The only effort to understand ANT+ with SDR is the following https://github.com/sghctoma/antfs-poc-defcon24. I tried following, but it's too obscure for my stage of knowledge. It would also require setting up a pretty complex environment, and recompile code written originally for Linux (I have WSL in Windows and used it before)

Before getting into that, I was wondering if rtl433 could be used. ANT and ANT+ is widely adopted, especially for sport devices, so I think it would be a benefit to the whole community.

I tried capturing some packet with rtl_433 -d "" -g 25 -f 2466.025M -s 4M -S unknown, and I'm enclosing one of the files generated below. But I can't make much progress trying to use the decoders on my own. I'm hoping someone can provide a pointer to move forward

URH_ANT

g006_2466.03M_4000k.cs16.zip

merbanan commented 2 years ago

Screenshot from 2022-02-20 21-52-08

Unfortunately the demodulation code will not be able to recover the bits from this signal.

merbanan commented 2 years ago

Try and see if you can actually get Shockburst payloads to decode with this code:

https://github.com/sghctoma/antfs-poc-defcon24/blob/master/sniff/shockburst.c

and the actual signal should look like this: https://github.com/sghctoma/antfs-poc-defcon24/blob/master/sniff/packets.h#L6

If you look at the blue line in the picture above you see what the fsk demod is doing with the signal. The low level at the second part looks good but the first half makes no sense.

merbanan commented 2 years ago

Anyway the signal recorder works fine, but I'm not sure the signal integrity is there. Maybe the signal is saturated somehow. The amplitude looks stable though which makes the signal segmenter happy.

And can you feed this file through rtl_fm and see if it looks any better?

robcazzaro commented 2 years ago

First of all, thanks!

It's entirely possible that the signal is saturated, since I'm still trying to figure out the proper gains and antenna distances. What did you use to generate the bitmap you enclosed in your reply? I'm still struggling to figure out all the proper steps, sorry. The learning curve is pretty steep.

The code you mention is the one I refer to initially, but I still have no idea on how to use it. See above for why :)

Do these look any better? I used a bigger distance (once I understand what you used for the image above, I can optimize the signal)

g0xx_2466.03M_4000k.zip

rtl_fm seems to be only for rtl_sdr devices, and those don't get high enough in frequency (and have too low sampling rates for a 1Mbps signal). Can I use SDRAngel, SDR# or SDR++ to do the same demodulation? If so, what should I do to record the right signal?

merbanan commented 2 years ago

src/rtl_433 -f 2466M -r ../signals/g006_2466.03M_4000k.cs16 -W test.sr

merbanan commented 2 years ago

and use this instead https://github.com/rxseger/rx_tools of rtl_fm

robcazzaro commented 2 years ago

Thanks again. When running what you suggest, I get

C:\Work\SDR\rtl_433>rtl_433 -f 2466M -r signals/g006_2466.03M_4000k.cs16 -W test.sr
rtl_433 version 21.12 branch  at 202112141644 inputs file rtl_tcp RTL-SDR SoapySDR
Use -h for usage help and see https://triq.org/ for documentation.
Trying conf file at "C:\Work\SDR\rtl_433\rtl_433.conf"...
Trying conf file at "C:\Users\Roberto\AppData\Local\rtl_433\rtl_433.conf"...
Trying conf file at "C:\ProgramData\rtl_433\rtl_433.conf"...

New defaults active, use "-Y classic -s 250k" for the old defaults!

Registered 176 out of 207 device decoding protocols [ 1-4 8 11-12 15-17 19-23 25-26 29-36 38-60 63 67-71 73-100 102-105 108-116 119 121 124-128 130-149 151-161 163-168 170-175 177-197 199 201-207 ]
Opening file: signals/g006_2466.03M_4000k.cs16 failed!
CreateProcess for 7z.exe failed.: No such file or directory
Opening Pulseview not implemented for win32

and I get various files (version, metadata, logic-1-1, analog=1-4-1, analog-1-5-1, analog-1-6-1, analog-1-7-1). I assume those are for PulseView (which I regularly use with a logic analyzer, so that part I should be able to figure out)

In order to use rx_tools, I'll need to get an Ubuntu machine up and running. Which is proving to be harder than I bargained for due to https://github.com/pothosware/SoapyPlutoSDR/issues/37 (SoapyPlutoSDR fails on devices with built in sensors)

merbanan commented 2 years ago

It looks the same with regards to demodulation. When the signal is active there should only be transitions between 2 clear states. I suspect that some filter eats to much of the signal. But I have no time to analyse further.

merbanan commented 2 years ago

-w will generate a pv session file instead if you have that but not in the path.

merbanan commented 2 years ago

src/rtl_433 rtl_433_tests/tests/m-bus/01/g020_868.95M_1200k.cu8 -W test.sr

merbanan commented 2 years ago

get that sample signal and you will see what you need the signal to look like for working demodulation with rtl_433.

robcazzaro commented 2 years ago

Thanks again for all the help. I now have a list of things to figure out and to try (I just managed to get the Linux installation work, so I'll switch to that in the future. That way I can use all your suggestions :)

robcazzaro commented 2 years ago

I managed to finally get rtl_433 work with Pluto on Ubuntu, and I captured a few more samples that look much better. Pulseview shows what looks like clean FSK pulses

Would you have some time to look at the enclosed files and help with guidance on next steps?

I looked at rx_fm (in lieu of rtl_fm) and I have it working, but I'm not sure what I'm supposed to capture with it (happy to keep using rtl_433 now that seems to be working)

Ubuntu_RTL_433.zip

merbanan commented 2 years ago

Screenshot from 2022-02-22 10-54-25

The framing of the signal looks ok (maybe the end has issues) and if you look at the FSK pulse placement they look like they match the signal if we adjust with an offset. The signal clearly has 2 different frequencies in it also.

offset

The pulse length seem to be 3-5us when I load the .sr, the real pulse rate could be something else.

Anyway this is real good, we have clear pulses that are somewhat of the correct length. Now you can start using the flex decoder to try to get the bit stream. From there you need to figure out the encoding and the meaning of the bits. But all this should exist in the available documentation.

robcazzaro commented 2 years ago

I have finally dialed up the settings for PlutoSDR and my transmitter, and managed to capture really good samples (I think :)

The sample rate is 4M, and the signal is 1M signal, so each bit is 4 samples, i.e. 1us. Coding is GFSK, and I think that the FSK in rtl_433 should work for that, right?

ANT packets are 8 bytes and there is a header, undocumented, and a packet type, 1 byte. I suspect that the header includes the network key, used to allow multiple devices in the same frequency (the receiver ignores all packets with the wrong header). I built a transmitter that sends a packet with the following payload 0x00, 0x55, 0x00, 0x00, 0xAA, 0x00, 0x00, and the last byte is a counter from 0x0 to 0xFF, That way the end of the packet always changes, while everything else stays the same and has easy to recognize pulse patterns

I captured 4 packets which look really good in Pulseview. I tried to decode them with rtl_433 -s 4M -f 2466M -r g218_2466.03M_4000k.cs16 -X n=ANT,m=FSK_PCM,s=1,l=1,r=128, but I don't get anywhere near what I expected. In Pulseview I can clearly see the 0x55 and 0xAA pulse trains, everything else 0, and the last byte changing as expected.

I think that the slicer is not syncing properly on the header, and then interprets the packet shifting the timing. I used 0x55 and 0xAA on purpose, because even when shifted usually produce at least one 5 or A. I even tried rtl_433 -s 1M -f 2466M -r g218_2466.03M_4000k.cs16 -X n=ANT,m=FSK_PCM,s=4,l=4,r=128, pretending to have a 1M sampling rate, and 4us pulses, but I get the exact same values in either case (as expected)

Do you have any suggestion on how to progress further?

ANT_packets.zip

robcazzaro commented 2 years ago

Using URH, I managed to make more sense of the bits. I captured 4 packets, as above

111111111111110101010101011011001001011011111010111101000000100000000100001010000000000101010100000000000000001010101000000000000000001100000011110001111100011111 111111111111110101010101011011001001011011111010111101000000100000000100001010000000000101010100000000000000001010101000000000000000001100000111100001110100001111 111111111111110101010101011011001001011011111010111101000000100000000100001010000000000101010100000000000000001010101000000000000000001100001011010001101100111111 111111111111110101010101011011001001011011111010111101000000100000000100001010000000000101010100000000000000001010101000000000000000001100001111000001100100101111

I'm pretty sure that there is a 14bit header, followed by 18 bytes, then a 4 bit end marker. Once I remove the first 14 bits, I get 555b25 bebd0201 0a 00550000aa0000c0 f1f1f for the first line.

where I haven't figured out the first 3 bytes for now, then the following 8 bytes are the device characteristics: 0xbebd are the device ID#, LSB followed by MSB the device is 0xBDBE, device type=2 and transmission type =1), then packet length including crc (possibly), then the 8 bytes payload, followed by what I assume is a CRC and the 4 bits end marker

URH also seems to demodulate pretty well, it requires lowering what it's called "center", which I'm not entirely sure what it does (yet). here's how it looks

URH_ANT

robcazzaro commented 2 years ago

Updating with more information. ANT is built on the Shockburst packet, with minimal changes. More info https://github.com/omriiluz/NRF24-BTLE-Decoder and https://github.com/sghctoma/antfs-poc-defcon24/blob/master/sniff/shockburst.c. I also managed to make the shockburst code work, by using the following ocmmand line rx_fm -f2457M -s 2M -g70 | shockburst. Unfortunately rx_fm seems to have problems with fm decoding and a Pluto, unless the gain is raised too high. URH decodes just fine with a gain of 20, rx_fm needs a gain of 70

Every ANT packet has a variable length burst to get the radio on, followed by a 0x55 or 0xAA preamble. The protocol uses 0x55 when the following byte starts with 0, and 0xAA if the following byte starts with 1. Then a 2 bytes address (0xA6 0xC5 for ANT+)

The ANT standard uses an 8 byte ANT+ key, reserved only to "ANT adopters". But since the protocol can only use a 2 byte actual address, the ANT devices must derive the 2 bytes from the original 8 bytes key. I verified that using non-standard keys results in an address like 0x5B 0x25, and that any ANT USB stick can receive those transmissions even if the receiver key doesn't match the transmitter key.

Then, as mentioned before, the following 8 bytes are the device characteristics: 0xbebd are the device ID# (LSB followed by MSB the device in this case is 0xBDBE), device type=2 and transmission type =1, then packet length including crc, then the 8 bytes payload, followed by a CRC

I used reveng (https://reveng.sourceforge.io/) to reverse engineer the CRC, and the result is as follows

reveng -w16 -s A6C5310078010AB9A521FBBD72C3459471 5B25310078010AFDFEFF00010203044D10 5B25310078010A010203040506070864F9 5B25310078010A05060708090A0B0C1862
width=16  poly=0x1021  init=0xffff  refin=false  refout=false  xorout=0x0000  check=0x29b1  residue=0x0000  name="CRC-16/IBM-3740"

Do you know of any other alternative to rx_fm? I could not find much. And I see that nobody has been working on issues for a long time on that project

robcazzaro commented 2 years ago

More progress to report. I finally found a flex decoder that works, but I'm not sure how to handle the fact that the preamble can be 0x55 or 0xAA. I think that the next step will be to figure out how to build a decoder in C and try both using a 0x55 preamble or a 0xAA one (basically a one bit shift), then using the CRC to validate which one it is. I saw coding guidelines, on protocol decoders, but could not (yet) find a tutorial or an example on how to write a custom decoder in C. I'm set up to build on Ubuntu. so figuring this out is my next step

For now, this will decode an ANT+ packet (i.e. 0xA6C5 network code), even if it has false positives (0xA6C5 can be part of the payload). Will not decode standard ANT packets or ANT FS

rtl_433 -d driver=plutosdr,uri=ip:192.168.2.1 -g 15 -f 2457.025M -s 4M -X "n=ANT+,m=FSK_PCM,s=1,l=1,r=500,preamble=aa,match={16}0xa6c5,bits>=120,bits<=180"

and this scans all ANT and ANT+ packets, even if in some cases they can be shifted by a bit

rtl_433 -d driver=plutosdr,uri=ip:192.168.2.1 -g 15 -f 2457.025M -s 4M -X "n=ANT_Plus,m=FSK_PCM,s=1,l=1,r=500,preamble=aa,match={16}0xa6c5,bits>=120,bits<=180" -X "n=ANT,m=FSK_PCM,s=1,l=1,r=500,preamble=55,bits>=120,bits<=180"
merbanan commented 2 years ago

"I finally found a flex decoder that works, but I'm not sure how to handle the fact that the preamble can be 0x55 or 0xAA."

That sounded bogus but then I found this:

https://d.lij.uno/misc-nrf24-ble.html

1 byte of preamble (either 01010101 or 10101010, depending on the first bit of the access address).

So if BLE does that then it must be of some use. You can look at the following code for an example on how to create a decoder:

https://github.com/merbanan/rtl_433/blob/master/src/devices/fineoffset.c#L158

Declare the preamble as AAA6C5 and you should only get the ant stuff (if your info is correct). Then validate the CRC and after that you can decode the actual data.

Anyway you are on the right track just look in the code for other uses of what you need.

robcazzaro commented 2 years ago

Yes, the 55/aa preamble seems bogus, I agree :) but it's documented so widely that it's actually true. Even better, it works properly with pretty much anything I can throw at it now. Using the CRC to determine which preamble is used also works well. I generated a range of packets with my nRF52 dev board, and the new C decoder I wrote works with everything. I suspect that it's used by the simple hardware in those receivers to properly sync the pulse length, and there always are 8 transitions no matter what, even using only an 8 bit preamble

Even if the process of writing a new decoder is not documented (as far as I can tell), the functionality provided by rtl_433 is pretty awesome, and it's actually very easy to write a decoder once one takes the time to understand the code. I needed to complete this for a project, but I will take the time to rite a few lines on "protocol decoder writing for idiots like me" to add to the readme

I now have a decoder that decodes all ANT-like packets, printing only the ones with a correct CRC. I also print ANT or ANT+, depending on the network key used. I need to be able to capture non ANT+ packets, in order to monitor ANT devices using private keys

Right now it's very ugly code, but works for my deadline. As soon as I have a moment, I'll clean it up, capture signals for test, and submit it for consideration. Thanks for all the help

robcazzaro commented 2 years ago

Added pull requests https://github.com/merbanan/rtl_433/pull/2004 and https://github.com/merbanan/rtl_433_tests/pull/421

Hopefully I followed the guidelines properly... please provide feedback in case I messed up

robcazzaro commented 2 years ago

PR has been merged in https://github.com/merbanan/rtl_433/commit/94df283cf41bebe5d72753b5f94ec246aeb2363e, closing this issue now.