Part 3: The NRF24L01 Nightmare
The NRF24L01 Nightmare: Clone Chips, Bad Wires, and Broken Registers
This is the post where everything goes wrong. Part 3 of building a flight controller from scratch.
I had SPI working. The loopback test was passing. Time to connect an actual device, the NRF24L01+PA+LNA radio module I bought from AliExpress for about 4 euros. How bad could it be?
The module
My NRF module is the long-range version with an SMA antenna connector. It's bigger than the standard NRF24L01, has a power amplifier (PA) for stronger transmission and a low noise amplifier (LNA) for better reception.
It also has zero markings on the PCB telling you which pin is which.
The pinout problem
Every diagram I found online showed this pinout for the NRF24L01:
GND [ 1 ][ 2 ] VCC
CE [ 3 ][ 4 ] CSN
SCK [ 5 ][ 6 ] MOSI
MISO [ 7 ][ 8 ] IRQ
So I wired it up, wrote the driver, tried to read the STATUS register and got... nothing. The SPI transfer hung. No response from the chip.
I checked the wiring. Checked it again. Swapped wires. Nothing. Then I found a schematic for my specific module online, and the pinout was different:
V+ [ 1 ][ 2 ] GND
CSN [ 3 ][ 4 ] CE
MOSI [ 5 ][ 6 ] SCK
IRQ [ 7 ][ 8 ] MISO
CE and CSN are swapped. MOSI and SCK are swapped. Basically everything except power was in the wrong place. No wonder it didn't work.
After rewiring with the correct pinout, I read STATUS and got 0x0E. First successful SPI communication with the radio. That felt great.
The wire problem
Except it wasn't consistent. Sometimes I'd get 0x0E, sometimes 0xFF, sometimes 0x00, sometimes random garbage like 0xD5 or 0xC9. The values changed every read.
I was using 30cm male-to-female jumper wires. Turns out long wires on a breadboard act as antennas and pick up electromagnetic noise. SPI runs at megahertz frequencies and those signals are getting corrupted over 30cm of unshielded wire.
I tried slowing down the SPI clock. Went from /16 prescaler to /128, then /256 (the maximum). Still garbage. The wires were just too long.
The fix was cutting the wires shorter. I didn't have short cables, so I cut my 30cm wires with scissors (this is what happens when you want to finish something on a Sunday and no stores are open). Then I realized I couldn't strip the insulation because the wires are so thin. Ended up using my soldering iron to melt the insulation off the cut ends, then soldered them back together at about 8cm length.
After that: stable 0x0E every single read.
I also learned that the PA+LNA modules are power hungry. They need a 10µF capacitor between VCC and GND right at the module. Without it, the voltage dips and the chip resets. The capacitor was the difference between getting all zeros (chip not powered) and getting actual data.
The register that doesn't work
With stable SPI communication, I wrote the full driver: register read/write, payload TX/RX, initialization sequence. The init writes to a bunch of configuration registers: disable auto-acknowledgment, set the channel, set the data rate, set the payload size, power on.
But something was weird. After init, I'd read back the registers to verify and most of them were correct. CONFIG was right. RF_SETUP was right. EN_AA was right. But RF_CH (register 0x05, the radio channel) was always 0x00, no matter what I wrote to it.
I tried everything:
- Writing in powered-down mode
- Writing different values (0x01, 0x55, 0xFF)
- Writing with raw SPI calls instead of through the driver
- Sending the "activate" command that some clone chips need
Nothing worked. I could write to every other register: CONFIG, EN_AA, RF_SETUP, RX_PW_P0, SETUP_RETR, EN_RXADDR. They all accepted writes and read back correctly. Just not RF_CH.
I also noticed that the default values didn't match the NRF24L01 datasheet. EN_AA should default to 0x3F (all auto-ack enabled), but mine read 0x00. RF_CH should default to 0x02, mine was 0x00.
After some research, I confirmed what I suspected: this isn't a genuine Nordic NRF24L01. It's a Si24R1 clone. The Si24R1 is a Chinese clone that's register-compatible with the NRF24L01 for basic operation but has different default values and, apparently on my particular unit, a defective channel register.
I considered buying a genuine module, but channel 0 (2.400 GHz) is a perfectly valid frequency. Both the aircraft and ground station will use channel 0, and everything will work. The only thing I lose is the ability to change channels, which means frequency hopping (the last step in my plan) won't work with this module. That's a problem for future me.
What actually worked
After all the debugging, the NRF driver works well. Here's what the init looks like now:
- Disable auto-ack (I handle reliability in the application layer)
- Set channel to 0 (would set it to something else if the register worked)
- Set data rate to 1 Mbps, TX power to 0 dBm
- Set payload size to 32 bytes (matches my packet protocol)
- Flush TX and RX FIFOs
- Clear IRQ flags
- Power on in RX or TX mode
- Set CE high to start listening (in RX mode)
The driver supports send(), available(), and receive() functions. I can't test actual radio communication yet because I need a second board and a second NRF module, but all the register-level stuff is verified working.
Lessons from this chapter
Check the pinout of YOUR specific module. Don't trust generic diagrams. Find the datasheet or schematic for the exact board you have. Or just use a multimeter to identify GND (should have continuity with the antenna shield) and work from there.
Keep wires short. For SPI, anything over 10cm is asking for trouble. Cut your wires to the minimum length needed.
Decoupling capacitors matter. Especially for PA+LNA modules that draw bursts of current. A 10µF cap between VCC and GND solved my power stability issues.
Cheap AliExpress parts mostly work, but expect surprises. My NRF module works fine for basic communication. It just has one broken register. At 4 euros, I'm not complaining.
Debug systematically. When the driver hung, I narrowed it down by testing raw SPI transfers (worked), then single-register reads (worked), then writes (hung). That pointed me to the memcpy bug in the FreeRTOS task system, not the NRF itself. If I'd just assumed "the NRF is broken" I'd still be debugging.
Next up
Part 4: Reading the gyroscope and spinning a motor. The MPU6050 that turned out to be an MPU6500, complementary filter math, and the ESC that wouldn't beep until I realized the motor IS the speaker.
Code: GitHub.