Simulating synchronous messaging with asych nRF24 devices

I was a bit surprised to find that the acknowledgement message (automatic payload reply) following a send from one nRF24 device to another was NOT matched. I have yet to be proved wrong or have a rational explanation to this issue see http://forum.arduino.cc/index.php?topic=178511.0

Originally assuming that an acknowledgment matched the message that was sent I was intending to simulate client and server loading at different parts of the sketches with simple delay()s, and observe the behaviour of the sketch and adjust accordingly.

In the above topic I kept the sample code simple to show the problem but once the simulated loading was added to the server end it was possible to be in excess of seven messages sent before an acknowledgement was received, processed and returned. In the meantime six messages had been sent but not processed. Generally this is not a good thing. Sometimes you won’t notice and neither will it be important.

When you need a synchronous call on an asynchronous transport layer you need to do something extra. The sketches below show the client/sender and receiver/server sketches that match reply to mesage. They are the bare bones logic with some debug output to Serial and some places where you can simulate loading to test/ensure that a reply is being matched up with it message.

This code is based on the library RF24: RF24 Class Reference which I found to be the most robust out of four I have now tried. The only issue I found was that this acknowledgement mismatch issue is not discussed with which still leaves me wondering if I’ve overlooked something.

If I have made a mistake understanding/interpreting the acknowledgement behaviour of nRF24s please let me know.

I will also be trying something similar without the autoPayload feature of the nRF24… another day :expressionless:

SENDER/CLIENT sketch

#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 iSent = 1;
int iReceived;
int iRetry;
int mode = 0;
unsigned long ulMillis;

void loop(void)
{

  if (mode == 0) // sending
  { 
    if (iRetry == 0){  // only for first try
      ulMillis = millis();  // time of first attempt sending message 
      printf("\n\nSending %d", iSent);  // debug only the first one
    }
    radio.write(&iSent, sizeof(int));  // first and subsequent sends 
    mode = 1;  // swap into waiting for acknowledgement mode

    // simulate load - things to do before checking for acknowledgement
    delay(50);
  }

  if (mode == 1) // waiting for ack
  {
    mode = 0;  // will go back to sending or resending immediately
    // comment out the above to experiment with only resending if 
    // the ack is the same as the send message

    iRetry++;  // lets count how many times we retry

    if (radio.isAckPayloadAvailable())
    {
      radio.read(&iReceived, sizeof(int));
      //printf("\n\rGot Ack: [%d] ", iReceived);
      if(iReceived == iSent)  // at last the replay to the message
      {
        //mode = 0; // uncomment to control return to sending for experimental purposes

        printf("\n\t%lu mS for \t%d tries\t HOORAY ack = send", millis() - ulMillis, iRetry);
        iSent++;  // and the neext message please :)
        iRetry = 0;
      }
    }
    else
    {
      //printf("\nWaiting for %d", i); // uncomment if you don't mind loads of debug output
    }
    
    //mode = 0; // uncomment to experiment with returning to sending mode
    
    // simulate load - work to do before being able to get on to retry
    //delay(500);

  }
}

RECEIVER/SERVER sketch

#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "_util.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)
{
  // simulate load - work to do before checking for incoming message
  delay(500);
  
  if (radio.available())
  {
    // simulate load - work to do after receiving incoming message
    // and before being able to read the incoming message
    delay(500);
    
    bool done = false;
    while (!done)
    {
      done = radio.read( &i, sizeof(int) );
      
      // simulate load - work to do after reading incomming message
      // but before acknowledgement can be sent
      delay(250);
      
      printf("\nReceived %d", i);

      radio.writeAckPayload( 1, &i, sizeof(int) );
      printf("\nWrite %d", i);
      
      // simulate load - work to do between readings incase there are
      // multiple mesages waiting
      delay(500);
    }
  }
}

Sample output from Sender

GOGO


Sending 1
	1938 mS for 	18 tries	 HOORAY ack = send

Sending 2
	3770 mS for 	33 tries	 HOORAY ack = send

Sending 3
	3703 mS for 	32 tries	 HOORAY ack = send

Sending 4
	3771 mS for 	33 tries	 HOORAY ack = send

Sending 5
	3697 mS for 	32 tries	 HOORAY ack = send

Sending 6
	3771 mS for 	33 tries	 HOORAY ack = send

Sending 7
	3701 mS for 	32 tries	 HOORAY ack = send

Sending 8
	4323 mS for 	34 tries	 HOORAY ack = send