Reverse Engineering a 27MHz RC Toy communication using RTL SDR
作者使用RTL SDR和GNU Radio工具分析了27MHz频段的遥控消防车玩具通信协议,通过频率检测、信号解调和逆向工程确定了其调制方式为幅度移键(ASK),并最终解析出遥控信号的帧结构和编码方式。 2025-9-20 05:0:5 Author: nitrojacob.wordpress.com(查看原文) 阅读量:13 收藏

My kids have this RC fire engine that works in the 27MHz band. I got curious how the communication is, with objective to control the toy from laptop. I had an RTL SDR in my toolbox. I have used it with gnuradio software for a couple of analog reception tasks. Not for anything serious so far. So I started simple and kept building on top.

Following is the final block diagram I arrived at. Seems complex right?. I will walk you through the steps involved in reverse engineering the communication.

Though the toy says it uses 27MHz, the exact frequency can be anywhere in the 26-28MHz range. Identifying the exact frequency and visualizing the signal in a waterfall diagram is the first step. Created a gnuradio project and added RTL-SDR Source block and QT GUI Sink block connected to its output to do this. Also added a QT GUI Range block to vary the RF frequency. Executed the flow graph. After playing a bit with the slider at bottom of the output window, I could visualize the RF signal as shown below. Note the strong red indicates the time when i was pressing the key on remote control.

Now that we understood the frequency to be 27.1MHz, we need to find out the modulation scheme. Modulation can be on Amplitude, the frequency or the phase, or a combination. To figure if there is any amplitude modulation, we need to look at the RF time domain with a large observation window. So I added a time domain visualisation by configuring the QT GUI sink block. During execution, increased the FFT size (observation window) from default 1024 to 16384

It appears like it is some form of Amplitude Shift Keying (ASK). (It can be a combination of Amplitude and phase modulation also. But we build on this assumption, and see whether we can differentiate all the RC keys with just the ASK). Next step is to demodulate the ASK signal with the help of an AM Demod block. From my experience with analog reception, I added an AGC2 and Decimating FIR Filter blocks to improve the signal to noise ratio. The demodulator output was then visualised using a QT GUI Sink. Also the output was taken to an Audio Sink to hear the message, after passing through a Low Pass Filter to reduce the sample rate to 32kHz required for the Audio Sink.

The time domain output of the QT GUI Sink is shown above while pressing one of the keys. Shows repeating Ones and Zeros last for 0.5 ms each. I changed the FFT size parameter on bottom to get idea about the larger frame structure.

At observation window of 32768, a repeating sequence of low frequency (111011101110) pattern followed by high frequency (10101010…) pattern is evident. The envelope change is an artefact of the AGC, so just ignore that. Tried a different switch on the remote.

Here we have a shorter high frequency (1010…) pattern. So the switches are encoded using the length of high frequency region. Now we need to estimate the length of each of the frames. For this we need to downsample it such that each sample represents 1 bit. A Symbol Sync block is required for this downsampling. Then with a Threshold block the amplitudes were converted to the [0,1] range. To count the bits after the preamble pattern (111011101110) I tried using many built in blocks like “Correlate Access Code – Tag” block etc. But did not work as per expectation. So I added a Python Block and decided to write the python code to count it, and renamed it to Custom Decode Block. The code used to count the frame length is shown below

The code was arrived at after a few trials and errors. It just correlates the 110110110 pattern with the datastream to identify the frame starts, and calculate the distance between the frame starts. Now it prints the frame length to the console. And also shows that as height of spike in an output visualization.

Almost done. But one more problem. Some keys especially those that had the long sequence of 1010.. were not showing up. I had to solve one more issue that is rooted on the gnu radio implementation to get all the frame length. The reason is the buffers passed to the code were very small and insufficient to hold a full frame in case of the long frames. So I had to add this Stream to Vector and Vector to Stream block pairs to aggregate multiple block and make a bigger block before passing to my Python block.

After all this exercise, I arrive at the following table. First 2 columns are experimentally obtained and other two are calculated.

Key CombinationFrame Length (bits)Data Length (Frame Length – 12) (bits)Data Length/12
^13212010
V1201089
<24121
>84726
^,<72605
^, >96847
V, <60484
V, >108968

A video tutorial on the above is available at https://www.youtube.com/watch?v=4koACEEZMiQ


文章来源: https://nitrojacob.wordpress.com/2025/09/03/reverse-engineering-a-27mhz-rc-toy-communication-using-rtl-sdr/
如有侵权请联系:admin#unsafe.sh