Expectations with nRF24L01, acknowledgement payloads and MANICBUG library

I am trying out the nRF24L01 with manicbug library RF24: Driver for nRF24L01(+) 2.4GHz Wireless Transceiver. I am wondering if my expectations are wrong (rather than a coding issue... )

I am using the pingpair_pl.pde as the basis for this test although I have also rewritten the PDE as a simple state machine just to get a different perspective on the issue but get the same results.

The essence of the supplied example PDE is that a message is sent from a ‘sender’ to a ‘listener’ and the listener then responds with an acknowledgement that it has received the message. The sender waits for this acknowledgement immediately after sending .

Suspicious chap that I am, I replaced the data sent from sender to receiver with a simple counter that increments for each send. The receiver simply echoes this number back in the acknowledgement.

I expect that when I send out the numbers 1, 2, 3, 4 etc the reply would be the same BUT it isn’t.

After sending anything, except the first send, the acknowledgment is the previous number. So sending 6 I get back 5, sending 7 I get back 6 and so on. Only the first send doesn't match this pattern. If the receiver has been doing its job on a previous ‘run’, and say it was just about to return 32 when it stopped, that will be the value in the first acknowledgement to the sender in the next test run. It seems to have been floating in the air somewhere...

I would expect to send 16, get 16 back and then be onto sending 17 etc not send 16 get back 15, send 17 get back 16 and so on.

I’m not looking for advice on how to recode/redesign or to check my code to avoid this - I’m looking for opinions and ideally someone who has already experienced this or not.

I have also been tracing the internals of the library to assist in understanding and have made all methods and properties public so I can add them to debug output in the PDE. Internal flags such as ack_payload_available seem to flip/flop correctly...

The only interesting thing I can find is in bool RF24::write( const void* buf, uint8_t len ) method at line 488 in RF24.cpp where it says if ( ack_payload_available ) so almost seems to be expecting the acknowledgement immediately after sending (IMMEDIATLEY?!?!) but doesn't subsequently apeear do anything that looks ‘useful’.

I have read the data sheets and my weekend brain hasn't worked it out. I've looked inside three other libraries and they haven't helped me ‘see the light’.

What should happen on an acknowledgment?

Sketches and sampe output below

OUTPUT - note ack 17 right at the beginning of the output and the acks one behind the send

Sending 1<1><0><1>
Got Ack: [17] 

Sending 2<1><0><1>
Got Ack: [1] 

Sending 3<1><0><1>
Got Ack: [2] 

Sending 4<1><0><1>
Got Ack: [3] 

Sending 5<1><0><1>
Got Ack: [4] 

Sending 6<1><0><1>
Got Ack: [5] 

Sending 7<1><0><1>
Got Ack: [6]

SENDER - rewritten from pingpair ping code but original code also behaves badly

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

RF24 radio(9,10);

// Single radio pipe address for the 2 nodes to communicate.
const uint64_t pipe = 0xE8E8F0F0E1LL;
void setup(void)
{
  Serial.begin(115200);
  printf_begin();

  radio.begin();
  radio.setRetries(15, 15);
  radio.enableAckPayload();
  radio.openWritingPipe(pipe);
  radio.setAutoAck(true);
  //radio.printDetails();
  printf("\nGOGO\n");
}

int i = 0;
int j;
int mode = 0;

void loop(void)
{
 
  if (mode == 0) // sending
  {
    i++; 
    printf("\n\nSending %d",i);
    radio.write( &i, sizeof(int) );
    mode = 1;
    delay(500);
  }

  if (mode == 1) // waiting for ack
  {
    if ( radio.isAckPayloadAvailable() )
    {
      radio.read(&j,sizeof(int));
      printf("\n\rGot Ack: [%d] ",j);
      mode = 0; 
      delay(500);

    }
  }
}

RECEIVER - essentialy the pong of the pingpair_pl code cut down to have just what is needed to return the value i

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

RF24 radio(9,10);

// Single radio pipe address for the 2 nodes to communicate.
const uint64_t pipe = 0xE8E8F0F0E1LL;

void setup(void)
{
  Serial.begin(115200);
  printf_begin();

  radio.begin();
  radio.setRetries(15, 15);
  radio.enableAckPayload();
  radio.setAutoAck(true);
  radio.openReadingPipe(1,pipe);
  radio.startListening();

  radio.printDetails();
}

int i;

void loop(void)
{
  if (radio.available())
  {
    bool done = false;
    while (!done)
    {
      done = radio.read( &i, sizeof(int) );

      printf("\nReceived %d", i);

      radio.writeAckPayload( 1, &i, sizeof(int) );
      printf("\nWrite %d", i);
    }
  }
}

As far as I know, the ACK reply is sent immediatly as soon as a message is received by the radio module, it is not dependent upon when the microcontroller actually 'reads' the message.

In your RECEIVER sketch when you use the 'writeAckPayload' you are telling the RF24 transceiver to use this payload for consequent ACK replies, you are NOT telling the radio to send an ACK for the message that you just read. So what you are seeing is actually correct, you send '1', tell the radio to use '1' as ACK payload and the next time the radio receives something it sends an ACK with value '1'.

That ties in with what is happening.

I wonder what the use of having a 32byte ackPayload is... seems a lot for what could be conceivably be just a 'checksum' at most... what can it be used for if you don't know how many messages the RECEIVER may have missed (see my sketch example below)?

I have reworked the SENDER sketch so it can be used to get a reply but it obviously inefficient as it will always have to send a second message, at least, to get the reply to the one that has been sent. And in practice with a loaded server and response slowed down by having to do work it may take even longer.

You can see my test sketch here http://forum.arduino.cc/index.php?topic=178600.0 for synching the send and the reply... hard work (for the processors/devices).

Do you have a solution for synchronous type messaging? I will try send and loop while waiting for the message to arrive that has my reply in it (tomorrow probably).

Two more NRF24 arrived... now have one breaduino as a server and three clients (a Nano, UNO and MEGA).

Mega wouldn't send/receive unless I powered the NRF24 from a separate power supply (another UNO doing nothing else). Much written about power supply noise from the MEGA stopping stuff from working.

But more pain... one of the two new devices has turned out to be a non plus version :frowning:

I wonder how many resellers are pushing stuff they can't check and don't expect the customers to check... this is the sort of device people who fail with will easily give up and perhaps buy another one thinking they have fried the first... or just give up

HI, I have found also the same problem as you.

I think about this:

  1. in the receiver node use writeAckPayload "more often" than the server send data (in this example, each 500 o 100 ms)
  2. in the server node, write data in a loop (for example with a 1000 ms delay)
  3. IT IS NECESSARY in the receiver node to call FLUSHTX before writeAckPayload (the TX FIFO only have a 3 level buffer)

I think that with this method you can get your last value (only for sensors, not for you want about writting back the data that you have read).

I don't know if you could "delay" the ackResponse in the RF24 module itself by changing some register.

Do you think that the method that I wrote is usefull to write back, for example, room's temperature ?

Best regards

It's written on a reference manual of RF24 as follows.

"Write an ack payload for the specified pipe.
The next time a message is received on pipe, the data in buf will be sent back in the acknowledgement."

writeAckPayload in RECEIVER is before more than read.

void loop(void)
{
if (radio.available())
{
radio.writeAckPayload( 1, &i, sizeof(int) );
printf("\nWrite %d", i);
bool done = false;
while (!done)
{
done = radio.read( &i, sizeof(int) );

printf("\nReceived %d", i);

}
}
}