NRF24L01, TMRh20, with ack, losing first message every time

Hi

I’m new to NRF24 communication. I’ve managed to get it working pretty ok, with the help of the TMRh20 library examples and reading a lot in the forum.

I still can’t get it to work as I would like.
While, with the ack, all the messages are sent perfect, the sender is always receiving one older message.
I mean - If the sender send ‘1’ and the receiver get the ‘1’ and send it back, the sender will get the ‘1’ back only after he send ‘2’.

Code of sender:

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

RF24 radio(7,8);

byte addresses[][6] = {"1Node","2Node"}; 
int counter = 1; 

void setup(){

  Serial.begin(115200);

  radio.begin();

  radio.enableAckPayload();                     // Allow optional ack payloads
  radio.enableDynamicPayloads();                // Ack payloads are dynamic payloads
  
   radio.openWritingPipe(addresses[0]);
   radio.openReadingPipe(1,addresses[1]);

  radio.startListening();                       // Start listening  

}

void loop(void) {
  
/****************** Ping Out Role ***************************/

    char a1[] = "hello ";
    char a2[18];
    
    radio.stopListening();  
    
    a1[6] = (counter+'0');
    a1[7] = '\0';
                                                            
    if ( radio.write(&a1, 8 )){                         // Send the counter variable to the other radio 
      Serial.print("Sent: ");
      Serial.print(a1);
      if(!radio.available()){                             // If nothing in the buffer, we got an ack but it is blank
          Serial.println("no radio is available");
        }
        else{      
            while(radio.available() ){                      // If an ack with payload was received
                radio.read( &a2, 18 );                  // Read it, and display the response time
                Serial.print("    Got: ");
                Serial.println(a2);
                }
        }
    if (counter <9 ) counter++;
    else (counter  = 0);
    }
    
    else    {        
      Serial.println(F("Sending failed.")); 
      }          // If no ack response, sending failed
    
    delay(1000);  // Try again later

}

Code of receiver:

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

RF24 radio(7,8);

byte addresses[][6] = {"1Node","2Node"};              // Radio pipe addresses for the 2 nodes to communicate.

void setup(){

  Serial.begin(115200);

  radio.begin();

  radio.enableAckPayload();                     // Allow optional ack payloads
  radio.enableDynamicPayloads();                // Ack payloads are dynamic payloads

    radio.openWritingPipe(addresses[1]);        // Both radios listen on the same pipes by default, but opposite addresses
    radio.openReadingPipe(1,addresses[0]);      // Open a reading pipe on address 0, pipe 1

  radio.startListening();                       // Start listening  
  
  radio.writeAckPayload(1,1,1);          // Pre-load an ack-paylod into the FIFO buffer for pipe 1

}

void loop(void) {

/****************** Pong Back Role ***************************/
    byte pipeNo;                          // Declare variables for the pipe and the byte received
    char a1[8];
 
    while( radio.available(&pipeNo)){              // Read all available payloads

      radio.read( &a1, 8 );                   

      Serial.print("Got: ");
      Serial.print(a1);
      Serial.print("   Sent: ");
      Serial.println(a1);
      
      radio.writeAckPayload(pipeNo,&a1, 8 );  // This can be commented out to send empty payloads.

   }
}

Sender’s Monitor:

Sent: hello 1`    Got: 
Sent: hello 2`    Got: hello 1
Sent: hello 3`    Got: hello 2
Sent: hello 4`    Got: hello 3
Sent: hello 5`    Got: hello 4
Sent: hello 6`    Got: hello 5
Sent: hello 7`    Got: hello 6
Sent: hello 8`    Got: hello 7
Sent: hello 9`    Got: hello 8
Sent: hello 0`    Got: hello 9
Sent: hello 1`    Got: hello 0
Sent: hello 2`    Got: hello 1
Sent: hello 3`    Got: hello 2
Sent: hello 4`    Got: hello 3
Sent: hello 5`    Got: hello 4
Sent: hello 6`    Got: hello 5
Sent: hello 7`    Got: hello 6
Sent: hello 8`    Got: hello 7
Got: hello 1   Sent: hello 1
Got: hello 2   Sent: hello 2
Got: hello 3   Sent: hello 3
Got: hello 4   Sent: hello 4
Got: hello 5   Sent: hello 5
Got: hello 6   Sent: hello 6
Got: hello 7   Sent: hello 7
Got: hello 8   Sent: hello 8
Got: hello 9   Sent: hello 9
Got: hello 0   Sent: hello 0
Got: hello 1   Sent: hello 1
Got: hello 2   Sent: hello 2
Got: hello 3   Sent: hello 3
Got: hello 4   Sent: hello 4
Got: hello 5   Sent: hello 5
Got: hello 6   Sent: hello 6
Got: hello 7   Sent: hello 7

As can be seen at the first line of the sender’s monitor - it misses one message.

I could solve it with always sending a message twice, but I would like to know if there’s a more elegant way.

Thanks

Shachar:
While, with the ack, all the messages are sent perfect, the sender is always receiving one older message.
I mean - If the sender send '1' and the receiver get the '1' and send it back, the sender will get the '1' back only after he send '2'.

That is how it is intended to work.

The data that is part of an acknowledgement MUST be loaded before the message arrives.

I find this a perfectly workable system once I recognize how it behaves. It is very simple to implement and there is no risk of the two nRF24s getting out of step - i.e. both listening for the other one.

The alternative is to create your own call and response system such as in the 3rd example in this Simple nRF24L01+ Tutorial. The 2nd example illustrates the ackPayload feature.

...R

I find it great as well. I tried to work the nrf without the ack and it misses too much to be reliable.

With the ack it just works.

I'll just send each message twice.

Thanks for the answer

Shachar:
I'll just send each message twice.

I'm not quite sure why you want the Rx to send the data it has just received back to the Tx from which it came.

The nRF24s have a very sophisticated validation system. If the PTx receives an ack from the PRx you can be sure that the message was received correctly. This is true even with the first example in my Tutorial.

I don't use the ackPayload feature to validate the data, I use it to send data from the PRx to the PTx. In my case I am sending speed and direction data to a battery powered model train and getting back the battery voltage.

...R