Deciphering SPI commands given to RF module of RC heli

The transmitter of my cheap radio-controlled helicopter uses an RF module with an SPI interface. I'm trying to understand the data given to the RF module, with the end goal of removing the original controller from the picture and having my computer give commands to the RF module directly to fly the heli.

The pins on the RF module were actually labelled, but too small to read and obscured by solder etc. Fortunately after removing it from the main board I discovered labelling underneath it which saved me a lot of trouble:

I connected these to my Arduino Uno...

...and set it to be an SPI slave with this sketch:

void setup (void)
{
  Serial.begin (115200);
  
  pinMode(10, INPUT); // SS
  pinMode(11, INPUT); // MOSI
  //pinMode(12, OUTPUT); // MISO (not used)
  pinMode(13, INPUT); // SCK

  // enable SPI
  SPCR |= (1<<SPE);
  
  // this arduino is slave
  SPCR &= ~(1<<MSTR);

  // enable SPI interrupt on SS
  SPCR |= (1<<SPIE);
}

// SPI interrupt routine
ISR (SPI_STC_vect)
{  
  byte thisByte = SPDR;
  
  Serial.print(thisByte);
  Serial.print(" ");
  
  if ( thisByte == 255 )
    Serial.println();
}

void loop (void)
{
}

Note how the MISO is not used. Before I commented out that line, the resulting output was an infinitely repeating "7 0 7 0 7 0 7 0 7 0 7 0 7 0 7 0 " etc.

After commenting out the line for MISO output, I got a much more coherent stream of data, and it responds to movements I make with the control sticks on the transmitter unit. You can see a couple of snippets of the output here: http://www.iforce2d.net/spiSlaveDataMSB.txt (no stick movements are made in those snippets)

However, there is something really strange about it, that makes me think my setup is not getting the data correctly. Firstly, there are still a bunch of "7 0 " sequences in there, which I don't think belong in this data. The occurrence of these "7 0 " sequences is fairly uniform for the most part, but never completely consistent - occasionally there are even multiple consecutive "7 0 " strings together.

Secondly, there seems to be a hiccup every now and then, where the byte that seems like it should be a 255, is something else - usually a larger number indicating many bits set. Is it possible that the data could be slipping a few bits?

The RC transmitter is a 4 channel radio, so it should be possible to send sufficient information to fly the heli with just four bytes, and then one byte as a delimiter which is what I'm assuming the 255 is for. Apart from the interfering 7's and 0's, it does indeed look like a 5 byte 'packet' format right?

At the receiving end, the heli is able to pick up this data stream from any point (ie. there is no special initialization sequence or syncing necessary). I checked this by disconnecting the clock wire, the leds blink on the heli to indicate it has lost input, then after reconnecting the clock the heli can continue flying just fine without needing to restart the transmitter again.

I'm certain there are no bad solder or shorted connections because if I plug the RF module into the other side of the breadboard instead of the Arduino, I can fly the heli as normal.

As well as the interrupt method shown above, I also tried the other method which uses a while loop:

void loop(void)
{
   while ( ! (SPSR & (1<<SPIF) ) );
   byte thisByte = SPDR;
   ... etc
}

... but that just gave me the same results.

I also played around with some other settings for data sampling (shown commented out here, but I tried each possible setting for all of these):

  // data clock idle state
  //SPCR &= ~(1<<CPOL);
  
  // sample which edge
  //SPCR |= (1<<CPHA);
  
  // LSBF/MSBF
  //SPCR |= (1<<DORD);

The first two of these did not make a damn bit of difference which was kinda surprising. The last one did make a difference, but brought up yet more strange results. If you check the output snippets above (using MSBF), you can see that there is a 255 quite frequently, roughly every 6-10 bytes in most cases. Unless I'm very much mistaken, 255 has every bit set so it should be the same regardless of which order (least significant bit first or most significant bit first) is used.... right? But in the output when using LSBF, the occurrence of a 255 is much less frequent: http://www.iforce2d.net/spiSlaveDataLSB.txt

Finally, another reason I'm certain that something is going wrong, is that I can get the exact same output by moving different control sticks on the transmitter. For example, moving the throttle a tiny bit upward I can get a fairly consistent reading of "0 68 0 0 63 7 255" on each line. If I return the throttle to fully down, and then move other stick to roll a little bit, I can get the exact same reading. This cannot possibly be correct because there is no way for the receiver to distinguish between throttle and roll.

So you can see I have a lot of questions...! If anybody has some ideas they could share I'd be happy to hear them.

// SPI interrupt routine
ISR (SPI_STC_vect)
{  
  byte thisByte = SPDR;
  
  Serial.print(thisByte);
  Serial.print(" ");
  
  if ( thisByte == 255 )
    Serial.println();
}

Writing something to the serial interface inside an interrupt handler is a bad idea. You'll end in a dead lock in no time. Save the data in a buffer and write it to the serial interface in the standard loop() routine.

pylon:
Writing something to the serial interface inside an interrupt handler is a bad idea.

Thanks for the tip! I think I did actually try storing and writing the data separately, but I'll give it a go again, to be sure.

Have you tried different data modes and bit orders?
I would assume that as the arduino is a slave that it does not also need the clock divider setting.

I tried storing the data in the interrupt handler and writing it to serial separately in the loop function. I had actually tried that before, but only with a very small buffer. This time I tried it with a 1kb buffer and it helped a lot to figure a few things out:

  • the "7 0 " sequences are still there, but with enough consistency now that I've decided they are supposed to be there
  • a data 'packet' is actually 25 bytes long, but a lot of the content seems to be fixed and never changes
  • when moving the control sticks, the resulting control values now make perfect sense :slight_smile:
  • trim values are included in the packet
  • there is a checksum byte in the packet calculated from all control values and trim values
  • one byte of the packet seems to be a timer of sorts, it is always different between consecutive packets even when nothing else changes
  • the first 194 bytes sent after turning the transmitter on is a fixed sequence of data

So after many hours of staring at a wall of numbers, some small progress has been made :slight_smile: Now I'm kinda stuck again...

I have set up a second arduino as a master, and programmed it to send the same sequence of data as I got from the RC transmitter. Judging from the serial output from the slave arduino, the data it sees looks the same and it cannot tell the difference between the real transmitter and the master arduino. But unfortunately, when connecting the master arduino to the RF module, the heli just sits there and blinks, unable to detect any signal.

I must be missing something, still a lot more head-scratching to come with this I think...

I gave up on this for a while, then today I was reading about RF module usage in general, and learned that the process is more complicated than just streaming bytes into the slave. The timing of SS changes is important because it delineates messages of multiple bytes. I think I really need to get a datasheet for this module if I'm to get anything done with it.

So this forum is probably not the best place for my next question, but I'll put it here anyway to keep this little story going... I searched all over the net for any clues about the identity of this RF module, but couldn't find anything. Maybe somebody more experienced would have a better clue about identifying it. The printed text on the board appears to say:

BTL-RFF0-01 V1.4 HJ29A94V8
2013 9 16
4013

... but as you can see, some of it is a bit unclear:

On the RF chip itself, the printing is much clearer but far too small to take a photo of (with any camera that I have at least). It says:

2402E
5325.1
AD1311

If anybody has a better suggestion for a forum to ask this in, please let me know.

This is my first post so... hello :slight_smile: I know this is pretty old topic but... I have generic solution. Maybe it'll help someone (even if an author totally gave up on this ;))

So... I have transmitter (from MALi Racing RC car) with similar PCB. As you probably know you need at least 4 things to get any packet from this device:

  • RX address
  • air data rate (1M, 2M or 250K)
  • channel (0-127)
  • CRC bytes count

Getting RX address is the hardest part because there are 240 addresses (more than IPv4). You have the following options to find this address:

  • brutal force search (takes too long)
  • listening for packets in promisc mode - there is no such mode in nrf24l01 compatible chips but we can fake this (Promiscuity is the nRF24L01+'s Duty)
  • read address (and other parameters) from RF initialized by original PCB

I tried all of this but finally found right address using the last one :slight_smile: This one is simple but you probably should unsolder RF. And before you start you need to write some code (for Arduino) to read and display on PC screen the RF registers. Then connect RF to external power supply and SPI lines to transmitters PCB, power up and (after a few secods) power down the transmitter. Now connect SPI lines to Arduino and just read registers :slight_smile:

I've done this using RPi and BK2423 RF chip so can't give code for Arduino but it's rather simple to write yourself if you look at RF chip datasheet :slight_smile: In my case the transmitter uses address 0x66 0x88 0x68 0x68 0x68 (LSByte first), 1Mbit, channels around 20-50 and 2B CRC. Maybe yours uses the same but if not... you know how to get this :slight_smile: Last thing you'll have to do is packet structure analysis but in my opinion it is the easiest part :slight_smile: