In part two of this series, we first tried to interpret the data received in promiscuous mode from the quadcopter RC radio. We then started collecting data from the SPI interface of an Arduino Uno controlling a nRF24L01+ transceiver.
In this part we will compare the collected SPI data we some documentation, trying to understand both (data and documentation). At the end we will even start listening to the SPI interface of the RC.
Step 6: Verification of the collected data by comparing it to the datasheets and libraries
This step is
more or less exactly the same as coloring by numbers. As we have access to the datasheet of the nRF24L01+ and also to the source code of the library, we just need to compare the received data to what can be found in the documentation. No fantasy needed, no mysteries to be solved. The spreadsheet below is showing the commands captured during the RF24SlaveTest. The columns from left to right show the procedure calls used in the application, the invoked procedure calls within the library and the triggered SPI messages starting from column 3 with command, corresponding register and values.
As we can see, calling begin() is already invoking a few commands to the transceiver:
- CRC gets turned on
- Data rate set to 250kbps
- The result is being checked
- Data rate is being set to 1Mbps
- Features are being toggled
- Features are being turned off
- Dynamic payload is being turned off
- Status bits are being cleared
- Channel 76 is being selected
- TX and RX buffers are being flushed
- The transceiver is being powered up
Calling the setChannel(), setDataRate() and setAutoAck() methods are again invoking commands writing to the respective registers.
(almost) no surprises and the proof of concept is worthy of its name. But what’s with the orange line? The command is not documented in the nRF24L01+ datasheet and its even more surprising since it is being mentioned in the datasheets of its cheaper cousins. So who is the hen and who is the egg here? And how come that this undocumented command found its way into the RF24 library? But before losing time on tracking down conspiracies, let’s move on to the next step:
EDIT: As it turned out, the datasheet linked on the sparkfun product page was outdated. Version 2.0 of the datasheet on the nordicsemi page does contain the ACTIVATE command (marked orange in the spreadsheet) on page 46. So let’s move on to the next step:
Step 7: Eavesdropping on the RC SPI communication
As mentioned in part two, I did not have an oscilloscope to verify the pinout of the RC transceiver. I was just hoping that it would be the same as in Dzl’s post. Another thing I had to hope for was the data rate used by the MCU for the SPI interface. If it was too high like in the first tries with the RF24 library in step 5, the whole setup would not work. But I was fortunate enough and all went well.
Similar to the setup used in Step 5, only the MOSI, SCK and CSN got connected to the Arduino Mega. The MISO was left so it would not interfere with the replies sent by the transceiver to the MCU.
There was one thing that came to my mind, though: The RC is working on 3VDC while the Arduino is working on 5VDC. I didn’t want to fry the RC, even though according to the datasheets, the transceiver should be able to withstand 5VDC, but I was not so sure about the MCU. And there was of course the signal levels, so I decided to put a level converter in between the RC and the Arduino. (In the middle of testing, one channel of the converter failed and cost me another night of scratching my head over my software, but that shall not distract us from the main task.)
Another thing I got reminded of during the tests: When it comes to signals and busses, one of the most important things is the ground level! Just like in real life, if you are not on the same level as the person you are trying to communicate with, the conversation might not be working that well, in other words, the RC grounds had to be connected to the Arduino grounds for the SPISpy to receive any messages.
So here is what I got during the RC initializing the transceiver:
Pretty straight forward. The MCU is going through the registers one by one and sets the necessary values. At the end it sets the addresses for the receiver and transmitter, followed by a few settings on “bank1” (which does not seem to exist on the nRF24L01).
What comes after switching back to bank0 is a bit confusing, though: The receiver is being powered on again and then the RX / TX addresses and channel are being set again, the addresses even twice. Guess the code could need some streamlining here…
Then comes the first transmission loop:
Again, the principle is quite simple:
- Flush the TX buffer
- Clear the Status bits
- Send out the first package
- Power up the transceiver (after the sending command?!?)
- Read the Status register (~46 to 54 times in either ~25 or ~45 ms intervals)
- Clear the Status bits
The loop is being repeated 14 times within ~540 ms before the following is happening:
The RC is changing its addresses for RX and TX to 101 101 101 101 101. Then the transmission loop starts again, this time with a different payload:
A closer look reveals that the first four bytes of the payload are actually the same as the address used in the first transmission cycle (and as we will see later, also during the normal operation). The purpose of the other four bytes is presently unknown.
So this is exactly what we were looking for: The setup of the transceiver and the pairing messages!
In part 4 we will look briefly at the normal operation (again, there will be no surprises here) and then move on to create our own RC with an Arduino and the nRF24L01+ transceiver.