nRF24L01+ and RF24 lib ack failure

I've got a bunch of Arduinos connected together using the RF24 library (http://maniacbug.github.com/RF24/index.html) and nRF24L01+ transceivers.

It's basically working, but I have a strange problem:

Between some pairs of Arduinos, when I send a message the message arrives, but the radio.write() method returns a false (failure) status code. I think this means that the outgoing message was received, but the sender did not receive an ack from the receiver.

The problem is quite widespread; usually, write() succeeds but returns false. Occasionally, when I start up two Arduinos I don't see this problem; the write() returns true (success) every time for hours on end. And then for no apparent reason, it will start returning false.

When problem is occurring, I will occasionally (perhaps 1 in 100 messages) see a successful ack.

I don't think it is a hardware problem - the problem affects all my Arduinos and they are all capable of sending and receiving.

I don't think it is a signal strength problem - I see the problem when the radios are two feet apart or twenty feet apart.

I see the problem with just two Arduinos running - I don't think it is a collision/overlap problem.

I have a suspicion it may be due to internal behaviour of the RF24 library. Sometimes I have an Arduino that is acting as an intermediary between two other Arduinos, and it will consistently fail to send messages that are initiated locally but it will successfully send a message that is being forwarded (i.e. radio.read() immediately followed by radio.write()) and then subsequent write()s will fail.

I have briefly turned on debug within the RF24 library, got a welter of information that I didn't understand and quickly turned it off again.

Here are two small sketches that demonstrate the problem:
Sender code:

#include <SPI.h>
#include <OneWire.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"

//
// Hardware configuration
//

RF24 radio(9, 10);
#define RADIO_GROUND 8

const uint64_t receiverAddress = 0x0000000000000001LL;

unsigned long lastMessageMillis;
#define MESSAGE_INTERVAL_MILLIS 10000

unsigned int message = 0;

void setup(void)
{
  //
  // Print preamble
  //

  Serial.begin(9600);
  printf_begin(); // enable stdout to Arduino host serial stream
  printf("\n\rSimpleSender\n\r");

  //
  // Setup and configure radio
  //
  setupRadio();

  lastMessageMillis = 0;
}

void setupRadio()
{
  pinMode(RADIO_GROUND, OUTPUT);
  digitalWrite(RADIO_GROUND, LOW);
  delay(500);
  
  radio.begin();
  radio.setPALevel(RF24_PA_HIGH);
  radio.setRetries(15, 15); // set the number of retries and interval between retries to the maximum
}

void loop(void)
{
  if((millis() - lastMessageMillis) > MESSAGE_INTERVAL_MILLIS)
  {
    lastMessageMillis += MESSAGE_INTERVAL_MILLIS;
    //radio.stopListening();
    radio.openWritingPipe(receiverAddress);
  
    if( radio.write(&message, sizeof(message)) )
    {
      printf("Sent %d\r\n", message);
    }
    else
    {
      printf("Send %d failed\n\r", message);
      // radio.printDetails();
    }
    //radio.startListening();

    message++;
  }
}

Sender output:

SimpleSender
Send 0 failed
Send 1 failed
Send 2 failed
...

Receiver code:

#include <SPI.h>
#include <OneWire.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"

//
// Hardware configuration
//

RF24 radio(9, 10);
#define RADIO_GROUND 8

const uint64_t receiverAddress = 0x0000000000000001LL;

void setup(void)
{
  //
  // Print preamble
  //

  Serial.begin(9600);
  printf_begin(); // enable stdout to Arduino host serial stream
  printf("\n\rSimpleReceiver\n\r");

  setupRadio();
}

void setupRadio()
{
  pinMode(RADIO_GROUND, OUTPUT);
  digitalWrite(RADIO_GROUND, LOW);
  delay(500);

  radio.begin();
  radio.setRetries(15, 15); // set the number of retries and interval between retries to the maximum

  radio.openReadingPipe(1, receiverAddress);
  radio.startListening();
}

void loop(void)
{
  // if there is data ready
  uint8_t pipe;
  unsigned int message;
  
  if ( radio.available(&pipe) )
  {
    if( radio.read(&message, sizeof(message)) )
    {
        printf("RECEIVED %d\r\n", message);
    }
    else
    {
      printf("Radio read failed\n\r");
//      radio.printDetails();
    }
  }
}

Receiver output:

SimpleReceiver
RECEIVED 0
RECEIVED 1
RECEIVED 2
RECEIVED 3
...

Hardware connections are:

1 GND connected to nano pin D8
2 VCC connected to nano pin 3V3
3 CE connected to nano pin D9
4 CSN connected to nano pin D10
5 SCK connected to nano pin D13 =SCK
6 MOSI connected to nano pin D11 =MOSI
7 MISO connected to nano pin D12 =MISO
8 IRQ not connected

Any idea what I'm doing wrong?

PeterH:
I have a suspicion it may be due to internal behaviour of the RF24 library.

Well, that may very well be! :slight_smile: Radio timing can be complex, or it may be a straight-up bug. Thanks for the example code, I'll try it out.

What rev of the library do you have? If you downloaded it from the "download" link, it's in the name of the zip file. If you cloned it from git, do "git log -1".

First thing I would try is a different pipe. Try to balance the 1's and 0's in the pipe. The examples from Nordic have pipes like that, e.g. A8A8E1F0C6, etc.

EDIT: Could you also try it with the radio ground tied permanently to the Nano's ground?

Ok, as soon as I made this change, it started working fine.

const uint64_t receiverAddress = 0xABCDABCD71LL;

Wow, that was a pretty inspired guess!

Yes, when I use that address it seems to be 100% reliable. I'm intrigued to know what difference that made, perhaps it upset the radio's framing algorithm? Anyway it seems to have solved the problem which is all that matters.

Thanks for the help.

I don't suppose it matters now, but in case you're interested the version of RF24 I'm running comes from maniacbug-RF24-d437163.zip.

PeterH:
Wow, that was a pretty inspired guess!

Yes, when I use that address it seems to be 100% reliable. I'm intrigued to know what difference that made, perhaps it upset the radio's framing algorithm? Anyway it seems to have solved the problem which is all that matters.

Thanks for the help.

Cool, glad it's working. I was always wondering if that was superstition or really needed, but I never bothered to test it. Until now!

PeterH:
I don't suppose it matters now, but in case you're interested the version of RF24 I'm running comes from maniacbug-RF24-d437163.zip.

Cool. The Dec 19th version has some speed improvements, but the Nov 2nd version you have should be stable.

Hi guys,

I am so happy to have found this thread. I was experiencing the same problem and changing the pipe address in my sketch made all acks work fine! :slight_smile:

However, I would like to set up the pipe address randomly.

My question is: How can I create pipe addresses randomly in a way that could avoid such ack problems?

Do you guys know another way of solving this problem?

Thank you!

danielcugler:
However, I would like to set up the pipe address randomly.

That seems like an unlikely thing to want to do. What is your underlying objective? I had a possibly similar requirement which was to enable each RF24 device to find an address which was unique within the set of devices near it. In my case I did that by setting up a self-organising tree of devices, where each device tried to identify the devices around it and find which one had the best route to the root of the tree, and arrange for that parent to allocate it a unique address. The idea was that this functions as a loose mesh network that would each device to act as a range extender for the root node. My approach using the ack payloads as a return channel to avoid each device from having to keep pipes open for other devices trying to connect to it.

Hello all !

Really glad I found this topic! I have a question about the library as well, as well as about your code.

I have tried the GettingStarted example, which comes with the RF24 library. Both my NRF24L01+ modules are working and the ping example works.

Then I uploaded your code to my arduinos, and get no response whatsoever. I used the latter address (0xABCDABCD71LL - btw what does 'LL' mean ? ). Your code doesn't seem to differ from the example, so I don't understand. I added the radio.printDetails for debugging. Results below.

Sender:

Receiver:

(For some reason I can't copy the text from the serial window to anywhere else; can the printf statement make weird characters which prevent copying text?)

So my question is: why do I not get this code to work, while the original example is working?

Thanks in advance,

Jack

To copy code from the serial monitor, put the cursor over the text you want to copy and click the left button to set focus on that window, press ctrl+A to select all the text and ctrl+C to copy it all to the clipboard. To post it into a post, click the right mouse button and select 'paste' from the context menu. I recommend pasting the output inside code tags to ensure the forum software doesn't mangle it.

If your sketch doesn't behave the same as the example then there is some difference between them. I suggest comparing the two files to find what the difference is.

Ah, I see I haven't been too clear about what I did. I did try to ctrl+A, ctrl+C and then ctrl+V. If I try to paste what is shown in the window above I only get:

SimpleSender

STATUS		 = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1

and that is either all there is in the clipboard, or for some reason it stops pasting after that point. Mind: after RC_ADDR_PO-1 it pastes a TAB, and then it ends.

I was comparing the sketch you posted above to the GettingStarted sketch which is provided with the library you use. I could not find a functional difference. My guess is the above sketches are working for you, and I was wondering whether you made certain choices in programming that sketch which differ from the examples provided.

Cheers and thanks in advance,

Jack

I suggest you try copying from the serial monitor and pasting to notepad to see whether it's the copy which is going wrong, or the paste.

Notepad gives the same results as the forum editor.

Does all the text highlight when you press ctrl-A? Note that you have to put the focus in the text area by clicking the mouse in it, first. You should also be able to drag-select the text using the mouse. With the text selected, ctrl+c should copy it to the clip-board.

I have found the culprit I think:

void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty)
{
  char extra_tab = strlen_P(name) < 8 ? '\t' : 0;
  printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab);

  while (qty--)
  {
    uint8_t buffer[5];
    read_register(reg++,buffer,sizeof buffer);

    printf_P(PSTR(" 0x"));
    uint8_t* bufptr = buffer + sizeof buffer;
    while( --bufptr >= buffer )
      printf_P(PSTR("%02x"),*bufptr);
  }

  printf_P(PSTR("\r\n"));
}

So if name is more than 8 characters extra_tab gets the value 0. printf will attempt to print this in the line:

  printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab);

so a zero gets added to the string. This is not visible in the serial monitor window, but will cut of the string in the clipboard. CTRL+A, CTRL+C and CTRL+V will not work in that case.

Cheers,

Jack

Hi Everyone :slight_smile:
I have a problem with delay(); if I increase the delay on Sender ,so the receiver cant get any messages.
Such as a code from "PeterH" above

On Sender
unsigned long lastMessageMillis;
#define MESSAGE_INTERVAL_MILLIS 100 ///// If I use more than 100 , the receiver cant get any messages. :~

thanks for the help.
Map

Hello all,

I have a similar issue with ACK failures. The transmission always makes it through, but the ACK only makes it back about 20% of the time.

I am using the address mentioned above, and otherwise I'm using the stanleyseow examples for connecting an arduino and a raspberry pi using nrf24's.

Is there anything I can try other than setting that particular address? I've tried varying the data rate and retries, to no avail.

PCaponetti:
I have a similar issue with ACK failures. The transmission always makes it through, but the ACK only makes it back about 20% of the time.

I registered just for replying to this. Probably the original poster won't find this useful any more, but maybe somebody else will.

I had similar "not all acks arriving" problems when I was using voltage boosters on my battery powered sensor. The solution was to add a bigger capacitor right across the radio's VCC and GND pins. In my case previously I was using a 4.7uF capacitor, with that I had about 30% ACK failures, now a 68uF one brought that down to 0.5%. According to this forum ( Efficiency of Voltage Boosters | MySensors Forum ), a Low ESR, high capacitance one (>100uF) can bring it down to zero or very close to zero.

This all boils down to properly smoothing out the voltage for the radio for a stable operation. ( Decoupling capacitor - Wikipedia )

This thread is still very high in the Google search list for problems with RF24 library acknowledgment, so it may be useful for others to be aware of another cause for failed acknowledgments.

I had 100% acknowledgment failure but excellent message transmission success using an Arduino Uno R3 and a custom board based on the Uno. Acknowledgment was working well using two Unos but failed with my custom board. I naturally suspected a hardware problem but found no issue. Finally, I encountered this issue report about a race condition with changes to the register during the write function. I'm not sure why different hardware causes the problem, but in the report it was first noticed using the Teensy.

I was using a 2013 verison of ManiacBug’s library, so I tried the fork by TMRh20 that includes a small wait in the write function. This version of the library addressed the issue. I now have >95% ack success.

Hi,

I'm having the exact same issue. Send messages are received on the opposite side, but the radio.write() procedure returns false. I just cannot get the reliability high enough. These are some code fragments :

#define PIN_CE              7
#define PIN_CSN             8

byte pipes[6] { 0xF0F0F0F0LL, 0xF0F0F0F1LL, 0xF0F0F0F2LL, 0xF0F0F0F3LL, 0xF0F0F0F4LL, 0xF0F0F0F5LL };
RF24 radio(PIN_CE, PIN_CSN);

struct masterData
{
  byte source_node;
  long message;
  long value;
  unsigned long messageDelay;
};

struct responseData
{
  byte source_node;
  bool success;
};
bool BroadcastRadioMessage(byte targetNode, long message, long value){
  int tries = 0;
  masterData radioPayload = { current_node, message, value, 0 };
  radio.openWritingPipe(pipes[targetNode]);
  bool success = radio.write( &radioPayload, sizeof(masterData));
  while ((!success) && (tries < 10))
  {
    radioPayload.messageDelay = millis() - startTime;
    success = radio.write( &radioPayload, sizeof(masterData));
    tries++;
  }
  return success;
}