nRF24L01+ module not sending data

Hi there,

I am currently having problems with my homebrew code to send data via an nRF24L01+ module. I want to write my own, lightweight library, which just sends without acknowledge packets and on the other end only receives, for this module and am currently trying to solve an issue with transmitting data. The interrupt for "data sent" will not trigger whenever I write data to the TX FIFO. I can write to the registers and read from them, so the SPI setup shouldn't be the problem here. I have attached some code which I am currently using to debug and an image of the state diagram from the data sheet. Maybe my configuration of the module is just wrong but here it comes:

#include <SPI.h>
#include <nRF24L01.h>

//declaration of variables
int CSNpin = 10;
int CEpin = 9;
int IRQ = 2;

//packet to send
byte packet[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, };
volatile bool isSent = 0;

void myISR(){ 
  //reset IRQ flag in status
  spiTransfer((STATUS | W_REGISTER), {0x7E}, 1);
  //set sent status to true
  isSent = 1;
  //let LED light up
  digitalWrite(LED_BUILTIN, HIGH);

  //force CE pin low
  digitalWrite(CEpin, LOW);

void spiTransfer(byte registry, byte data[], int _length) {
  digitalWrite(CSNpin, LOW);

  if(_length < 0){
    byte* intermediate = data;
    SPI.transfer(intermediate, -1*_length);
  }else if(_length != 0){
    SPI.transfer(data, _length);
  digitalWrite(CSNpin, HIGH);

void setup() {
  //set in and output pin modes
  pinMode(CEpin, OUTPUT);
  pinMode(CSNpin, OUTPUT);
  pinMode(IRQ, INPUT);
  //begin SPI with normal settings

  //de-select radio and set chip-enable to 0
  digitalWrite(CEpin, LOW);
  digitalWrite(CSNpin, HIGH);

  //give the chip time to power up

  spiTransfer((EN_AA | W_REGISTER), {0x00}, 1); //disable all auto-achnowledge pipes
  spiTransfer((CONFIG | W_REGISTER), {0x52}, 1);  //configure as TX and disable all IRQs instead of data sent
  spiTransfer((FEATURE | W_REGISTER), {0x01}, 1); //enable W_TX_PAYLOAD_NO_ACK command word
  //set an interrupt pin to be IRQ and to call my ISR
  attachInterrupt(digitalPinToInterrupt(IRQ), myISR, LOW);

void loop() {
  // put your main code here, to run repeatedly:
  //transfer the payload, which is packet in this case to the TX FIFO and disable auto-acknowledge for this packet
  spiTransfer(W_TX_PAYLOAD_NO_ACK, packet, -32);

  //set sent status false
  isSent = 0;

  //while packet is not sent pulse the CE pin to make module send next entry in TX FIFO
  while(isSent == 0){
    digitalWrite(CEpin, HIGH);
    digitalWrite(CEpin, LOW);


  digitalWrite(LED_BUILTIN, LOW);

Here a link to the full datasheet:

Thanks everyone for reading.

Just curious, why do you want to write a 'lightweight' library ?

The standard, well tested ones, seem to work well enough.

For three reasons mainly.

1st is to see if I can and have the gratification if I do.

2nd is to gather some experience.

3rd is to make it easier to use than the standard ones. I know the standard ones aren't all that difficult to use but I want to make mine even simpler.

That's a strange length to trigger some pointer copy, why?

void spiTransfer(byte registry, byte data[], int _length) {

  if(_length < 0){
    byte* intermediate = data;
    SPI.transfer(intermediate, -1*_length);

The configuration without any CRC seems very dangerous to me.

  spiTransfer((CONFIG | W_REGISTER), {0x52}, 1);  //configure as TX and disable all IRQs instead of data sent

If you intend to make something more understandable,
it should not contain so many magic numbers.

Some of the PIC libraries/code examples for interfacing to the NRF24L01 are very basic and work at a level very close to the register description in the data sheet.

And reset the other two interrupt reasons also,
as a special gimmick, write a magic 0xE to read only bits.

If you want to take the "read only bits don't care" approach,
a 0x7F would have made it more obvious, that you want to clear all interrupts.
(had to change that, the topmost bit is labeled R/W in the datasheet)

I just want to send my packet without it being altered. In the spiTransfer function you can see that if you use a negative length, the intermediate is used so the packet data is not altered.

Why? As far as I understand CRC only checks if you sent this packet already or not. If I want to send a packet twice, because my control inputs aren't changing, I want it to really send and receive twice.

This is a fair point .

This is also true. I will make sure to make my code more obvious in future.

I have an update to the whole project. The nano stops doing anything after

I tried blinking the on-board LED before and after SPI.begin() but only before did the LED do something. Can I not use SPI.begin in the setup?

That is not what you accomplish by copying the pointer to the object,
it will still be a pointer to the very same object.

So your understanding is simply wrong, regarding the "only".
The CRC allows you to check for invalid packets and transmission errors.
Without a CRC, you will receive any junk that happens to match preamble + pipe adr.
Errors that change the content of your packets will go unnoticed without a CRC.

Do you still have pin 10 set as OUTPUT?
With proper configuration and a properly connected SPI bus,
there is no problem calling spi.begin(); in setup.

1 Like

Perhaps checkout the datasheet for the NRF24L01, it tells a different story .....................

Now that you say that I recognize that this is really stupid.

Thanks very much. This could explain a lot.

Yes, I do. I will try with a different MCU when I can get my hands on one in a few days. I currently have only ATmega328 on my hands. I will report back as soon as I tried it.

Thank you so so much already. You helped me out quite much.

Have to admit that I did not do that properly.

Easy enough to check though, reading datasheets etc.

1 Like