EDIT: The series got featured on my favorite hackspiration site…! Thanks to Brian Benchoff for his very kind and encouraging review. It got me hovering around like a quadcopter all day with a big smile on my face, asking my wife to ask me again what the hours and nights spent on this project were for… And it won’t be the last ;o)
Discounters. Perfect for basic groceries, good quality at a fair price and above all: clear. I mean, who needs ten different brands for milk with twenty different prices when they all come from the same cow?!? But that’s a different topic…
The other day, LIDL offered this nice little quaocopter for only 30 Euro. I just couldn’t resist, especially not after reading through Dzls’ post on controlling a toy quaocopter with an Arduino (also featured on Hackaday.com). Unfortunately he doesn’t go much into detail when it comes to the SPI eavesdropping part (probably because it was just too easy and not worth elaborating on it). As I only had little to no experience with that specific interface other than using pre-defined libraries to let my Arduino talk to sensors and radio modules, I accepted the challenge. In the end, I managed to reverse engineer the protocol, pair the quaocopter with an Arduino UNO and build my own RC. It is even possible to change the address and use a different one than the original remote control does…
There is still three misterious bytes in the data packets, which seem to be somewhat of a counter (one counting up, the other two counting down), but they don’t seem to influence the operation much. I had the Arduino “move” the throttle up and down for almost 30 minutes and did not change the values seeing no impact.
Part one of this project will describe the way from zero to finding the right channel up to listening to ALL the communication on a specific channel (promiscuous mode). Later parts will talk about how to interpret the received data and how to tap into the SPI communication (and the pitfalls of it) to finally bring all the bits and pieces together. To keep things short and simple, I will not go into detail on things like how to connect an nRF24L01 transceiver to the arduino board, as there are already plenty of examples all over the web. The project shall be more about the (quite rewarding) learning curve experienced through reverse engineering.
So this is what I did (of course only after taking the quadcopter for a short spin):
Step 1: Check Documentation (yes, this includes the manual!)
The box and also the 75 page manual (4 languages) did not give away much. The only half useful information was the specification of the remote control:
- Transmitting Frequency 2.4 GHz
- Range: max 30m
- Operating Voltage: 3 x 1.5V Batteries
The remote itself has a “4-CH” sticker on it, which most probably refers rather to the transmitted information (throttle, yaw, pitch and roll) than the radio characteristics.
So nothing on used channels, CE certificates or anything. What the manual does talk about is “intended use”, which I probably violated a litte here…
Step 2: Listen to the radio
In one of my drawers I had two Sparkfun nRF24L01+ breakout boards, which are also operating on 2.4GHz. Following the scanner example, which came with the RF24 library written by J.Coliz, I started scanning the 127 channels. The code needed a little adjustment to 1) make the range of scanned channels a bit more narrow and 2) to reduce overhead and make the scanning faster. The code can be found in my github repository.
The serial output during a few scans showed increased activity between channels 58 and 62, and closing the range and threshold, the most traffic was found on channel 60.
Step 3: Listen to Channel 60 – First haystack
A rather challenging step. The nRF24L01 board is meant to
receive make messages only visible when they have been
- received on a chosen channel (0 .. 127)
- at a chose data rate (250kbps, 1Mbit or 2Mbit)
- with a chosen header that can be anything between 3 to 5 bytes long
It is able to listen on six “pipes” simultaniously, though, but they would be sharing the first 2 – 4 bytes of the address. This seemed to be quite a haystack (about 2,3xE^29 straws, given that we already know the right frequency).
SDR would have been one option, but probably too timeconsuming. So I did a quick search on promiscuous mode, which is not officially supported by the nRF24L01. It led me to Travis Goodspeed’s blog on which he is desribing in detail how to tag into the radio stream of a wireless 2.4GHz keyboard. He was using an “illegal” register setting for the address length on the nRF24L01 along with the fact that the preamble byte of each transmission would be either 01010101 (0x55) or 10101010 (0xAA), depending on whether the first bit of the address in use is set to 0 or 1. So with CRC turned off, one byte of the address being either 0x00 or 0xFF (hoping for noise to trigger it) and the second byte being either 0x55 or 0xAA, chances were pretty high to catch whole transmission packets including the address, packet control field, payload and CRC byte(s) as described on p.28 of the nRF24L01+ product specification. I also had to hope the protocol used by the RC transceiver would be similar to the one used by the nRF24 (which in fact it is. As it turned out, those low cost modules like the RFM70 or the Beken BK2423 are pretty similar to the nRF24L01).
This is what the “promiscuous” reception looks like: The receiver gets triggered by random noise, uses more noise as first byte of the address and the the preamble of the RC is being interpreted as the second byte of the address. CRC check was deactivated, thus whatever might be in the following 32 byte is being concidered as payload.If the data comes from the RC (and follows the RF24 protocol), the received message will definitely contain the address bytes and also the packet control bits, if there are any. Depending on the RC address length (up to 5 bytes), there might be a worst case overlap of 25 bytes and 7 bits for the RC payload. Expecting a rather small ammout of data to be sent (supposedly one byte each for throttle, yaw, pitch and roll, maybe some control bytes, so probably no more than 8 bytes), the whole transmission might even fit into the received payload (and as we will see in the serial output and when , it does!)
The code for the promiscuous receiver can be found in my github repository.