nRF24L01 ackPayload [SOLVED]

Hi all,

I am (again) stuck with my nRF24L01. I am trying to build a sort of feedback system, where some people have senders with buttons to vote for and they all send their vote after a certain time to a receiving node. I wanted to use the ack Feature of the nRF24L01 with the RF24 library. However, I can not get the ack answer to work. My master sends the time and a start signal to all nodes on one address. It then changes addresses and is supposed to ask every individual node for their vote, for example after 10 seconds. Thats where the code fails: the sender receives the request to send the vote, but the ack Payload (the vote) is not being successfully transmitted. I will post the full code for understanding, however the important parts are in both sketches at the bottom.
This is the sender(master/receiver):

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <stdio.h>
#define CE_PIN   9
#define CSN_PIN 10

RF24 radio(CE_PIN, CSN_PIN);

unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 2;
char startAbfrageDauer[2] = "10"; // Zeit in Sekunden
char startAbfrageSendung[2] = "st";
char endeAbfrageSendung[2] = "en";
char frageAntwort[2] = "na";

int abstimmung = 5;

byte broadcastAdresse[5] = {'0','0','0','0','0'};
byte senderAdresse[5] = {'0','1','0','0','0'};
bool abfrageBeginnen = false;

void setup() {
  
  Serial.begin(9600);
  radio.begin();
  radio.setDataRate( RF24_2MBPS );
  radio.enableAckPayload();
  radio.setRetries(3,5);
  
}

void loop() {  

  abfrageZyklus();
  Serial.println("abfrageZyklus fertig");

}

void abfrageZyklus() {

    startAbfrage();
    warteZeit();
    frageNachAntwort();
    
}

void startAbfrage() {
    radio.openWritingPipe(broadcastAdresse);
    Serial.println("startAbfrage");
    radio.write(&startAbfrageSendung, sizeof(startAbfrageSendung));
    delay(20);
    radio.write(&startAbfrageDauer, sizeof(startAbfrageDauer));
    radio.stopListening();
    //delay(20);
    
}


void warteZeit() {
  radio.powerDown();
  //avr_enter_sleep_mode();
  unsigned int warteZeit;
  sscanf(startAbfrageDauer, "%d", &warteZeit);
  delay(warteZeit*1000+3000);
  Serial.println("ende Wartezeit");
  radio.powerUp();
}

void frageNachAntwort () {
  Serial.println("start frageNachAntwort");
  radio.openWritingPipe(senderAdresse);
  Serial.println("öffne neu");
  bool rslt;
  rslt = radio.write(&frageAntwort, sizeof(frageAntwort));
  Serial.println("radio write");
  if (rslt) {
    if (!radio.available()){ 
        Serial.println(F("Blank Payload Received.")); 
      }
      else {
        while(radio.available() ){
          radio.read( &abstimmung, 1 );
          Serial.println(abstimmung);
        }
      }
    Serial.println("ack aber keine Daten");
  }
  Serial.println("failed");
}

This is the voter:

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

#define CE_PIN   9
#define CSN_PIN 10
#define NUM_BUTTONS 4 

const uint8_t BUTTON_PINS[4] = {2, 3, 4, 5};
Bounce * buttons = new Bounce[NUM_BUTTONS];

byte masterBroadcastAdresse[5] = {'0','0','0','0','0'};
byte dieserSenderAdresse[5] = {'0','1','0','0','0'};

RF24 radio(CE_PIN, CSN_PIN);

char dataReceived[2];
char dataToSend;

bool starteDenTaster;
bool starteZeit = false;
bool tasterFertig = false;

unsigned int warteZeit;
unsigned long currentMillis;
unsigned long empfangenMillis = 0;
unsigned long buttonMillis = 0;

void setup() {
  
    Serial2.begin(9600);
    
    for (int i = 0; i < NUM_BUTTONS; i++) {
      buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );       //setup the bounce instance for the current button
      buttons[i].interval(1);              // interval in ms
     }
    
    radio.begin();
    radio.setDataRate( RF24_2MBPS );
    radio.enableAckPayload();
    //radio.setAutoAck(1);
    radio.setRetries(3,5);
    
    

}

void loop() {
  
  wartenAufTimer();
  if (starteZeit == true && starteDenTaster == true) {
    radio.stopListening();
    radio.closeReadingPipe(1);
    empfangenMillis = millis();
    while (millis() - empfangenMillis <= warteZeit*1000) {
    Taster();
    tasterFertig = true;
    }
    radio.openReadingPipe(1, dieserSenderAdresse);
    radio.startListening();
    while (tasterFertig == true) {
      antwortenAusgeben();
    }
  }
}

void wartenAufTimer() {

  radio.openReadingPipe(1, masterBroadcastAdresse);
  radio.startListening();
  if (radio.available()) {
    radio.read(&dataReceived, sizeof(dataReceived));
    Serial2.println(dataReceived);

    if (dataReceived[0] == 'e' && dataReceived[1] == 'n') {
      Serial2.println("Zeit abgelaufen: en"); 
      starteDenTaster = false;
    }
    if (dataReceived[0] == 's' && dataReceived[1] == 't') {
      Serial2.println("st ist da");
      starteDenTaster = true;
    }
    else {
       Serial2.println("Zahl");
       Serial2.println(starteDenTaster);
       sscanf(dataReceived, "%d", &warteZeit);
       warteZeit = warteZeit;
       if (starteDenTaster == true) {
         starteZeit = true;
       }
    }
  }
} 
void Taster() {
  
  Serial2.println("starteTaster");
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    Serial2.println("frage ab");
    buttons[i].update();
    if ( buttons[i].fell() ) {
          Serial2.println(F("Knopf gedrückt"));
      switch (i) {
          case 0:
          dataToSend = '1';
          break;
          case 1:
          dataToSend = '2';
          break;
          case 2: 
          dataToSend = '3';
          break;
          case 3:
          dataToSend = '4';
          break;
          buttonMillis = millis();
          Serial2.println(dataToSend);
      }
    }
  }
}

void antwortenAusgeben() {
    Serial2.println(dataToSend);
    Serial2.println("startListening");
    if (radio.available()){
      Serial2.println("is daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
      radio.read(&dataReceived, sizeof(dataReceived));
      if (dataReceived[0] == 'n' && dataReceived[1] == 'a') {
        Serial2.println("habs bekommmmmmmmmmmmmxxxxxxxxxxxxxxxxxxxxxxxxxxxxen"); 
        char data = '1';
        Serial2.println(dataToSend);
        radio.writeAckPayload(data, &dataToSend, 1);
        tasterFertig = false;
      }
    }
}

I am happy about any advice!

Have you tried the ackPayload example in my Simple nRF24L01+ Tutorial?

...R

Hi Robin,

yes, I have tried many sketches from the RF24 library and they all work fine. I have been playing around with the module quite a while and have been using the writeAckPayload function.
I am not sure where exactly the issue is, but everything works except sending the ack payload. The initial sending works, only transmitting the Payload fails. The ack itself seems to be transmitted since the sender says “Blank payload received”. That is our course false.

Larry_2:
Hi Robin,

yes, I have tried many sketches from the RF24 library and they all work fine

..............

but everything works except sending the ack payload

That does not make sense as a response to my question.

Let me ask it again. Have you tried the ackPayload example in my Tutorial?

If you did and it did not work then tell me in detail what happened when you tried it.

...R

Oh sorry, I think this is a misunderstanding. The library AND your SimpleAckPayload sketches work properly. Also I have been using this feature in my own sketches.

Larry_2:
I am not sure where exactly the issue is, but everything works except sending the ack payload. The initial sending works, only transmitting the Payload fails. The ack itself seems to be transmitted since the sender says “Blank payload received”. That is our course false.

Here, I was referring to the code I posted above, which is not working as expected.

Thanks for the help.

Larry_2:
Oh sorry, I think this is a misunderstanding. The library AND your SimpleAckPayload sketches work properly.

....

Here, I was referring to the code I posted above, which is not working as expected.

So how does your code differ from my working example?

That sort of comparison is how I debug my own problems.

...R

Ok, so I changed the code that it completely matches your AckPayload example. I am still getting the same result.
Code from the voter/slave/receiver:

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

#define CE_PIN   9
#define CSN_PIN 10
#define NUM_BUTTONS 4 

const uint8_t BUTTON_PINS[4] = {2, 3, 4, 5};
Bounce * buttons = new Bounce[NUM_BUTTONS];

byte masterBroadcastAdresse[5] = {'0','0','0','0','0'};
byte dieserSenderAdresse[5] = {'0','1','0','0','0'};

RF24 radio(CE_PIN, CSN_PIN);

char dataReceived[2];
int dataToSend;

bool starteDenTaster;
bool starteZeit = false;
bool tasterFertig = false;

unsigned int warteZeit;
unsigned long currentMillis;
unsigned long empfangenMillis = 0;
unsigned long buttonMillis = 0;

void setup() {
  
    Serial2.begin(9600);
    
    for (int i = 0; i < NUM_BUTTONS; i++) {
      buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );       //setup the bounce instance for the current button
      buttons[i].interval(1);              // interval in ms
     }
    
    radio.begin();
    radio.setDataRate( RF24_2MBPS );
    radio.enableAckPayload();
    //radio.setAutoAck(1);
    radio.setRetries(3,5);
    
    

}

void loop() {
  
  wartenAufTimer();
  if (starteZeit == true && starteDenTaster == true) {
    radio.stopListening();
    radio.closeReadingPipe(1);
    empfangenMillis = millis();
    while (millis() - empfangenMillis <= warteZeit*1000) {
    Taster();
    tasterFertig = true;
    }
    radio.openReadingPipe(1, dieserSenderAdresse);
    radio.startListening();
    while (tasterFertig == true) {
      antwortenAusgeben();
    }
  }
}

void wartenAufTimer() {

  radio.openReadingPipe(1, masterBroadcastAdresse);
  radio.startListening();
  if (radio.available()) {
    radio.read(&dataReceived, sizeof(dataReceived));
    Serial2.println(dataReceived);

    if (dataReceived[0] == 'e' && dataReceived[1] == 'n') {
      Serial2.println("Zeit abgelaufen: en"); 
      starteDenTaster = false;
    }
    if (dataReceived[0] == 's' && dataReceived[1] == 't') {
      Serial2.println("st ist da");
      starteDenTaster = true;
    }
    else {
       Serial2.println("Zahl");
       Serial2.println(starteDenTaster);
       sscanf(dataReceived, "%d", &warteZeit);
       warteZeit = warteZeit;
       if (starteDenTaster == true) {
         starteZeit = true;
       }
    }
  }
} 
void Taster() {
  
  Serial2.println("starteTaster");
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    Serial2.println("frage ab");
    buttons[i].update();
    if ( buttons[i].fell() ) {
          Serial2.println(F("Knopf gedrückt"));
      switch (i) {
          case 0:
          dataToSend = 1;
          break;
          case 1:
          dataToSend = 2;
          break;
          case 2: 
          dataToSend = 3;
          break;
          case 3:
          dataToSend = 4;
          break;
          buttonMillis = millis();
          Serial2.println(dataToSend);
      }
    }
  }
}

void antwortenAusgeben() {
    Serial2.println(dataToSend);
    Serial2.println("startListening");
    if (radio.available()){
      Serial2.println("is daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
      radio.read(&dataReceived, sizeof(dataReceived));
      if (dataReceived[0] == 'n' && dataReceived[1] == 'a') {
        Serial2.println("habs bekommmmmmmmmmmmmxxxxxxxxxxxxxxxxxxxxxxxxxxxxen"); 
        char data = '1';
        Serial2.println(dataToSend);
        radio.writeAckPayload(1, &dataToSend, sizeof(dataToSend));
        tasterFertig = false;
      }
    }
}

Code from the sender/master:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <stdio.h>
#define CE_PIN   9
#define CSN_PIN 10

RF24 radio(CE_PIN, CSN_PIN);

unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 2;
char startAbfrageDauer[2] = "10"; // Zeit in Sekunden
char startAbfrageSendung[2] = "st";
char endeAbfrageSendung[2] = "en";
char frageAntwort[2] = "na";

int abstimmung = 5;

byte broadcastAdresse[5] = {'0','0','0','0','0'};
byte senderAdresse[5] = {'0','1','0','0','0'};
bool abfrageBeginnen = false;

void setup() {
  
  Serial.begin(9600);
  radio.begin();
  radio.setDataRate( RF24_2MBPS );
  radio.enableAckPayload();
  radio.setRetries(3,5);
  
}

void loop() {  

  abfrageZyklus();
  Serial.println("abfrageZyklus fertig");

}

void abfrageZyklus() {

    startAbfrage();
    warteZeit();
    frageNachAntwort();
    
}

void startAbfrage() {
    radio.openWritingPipe(broadcastAdresse);
    Serial.println("startAbfrage");
    radio.write(&startAbfrageSendung, sizeof(startAbfrageSendung));
    delay(20);
    radio.write(&startAbfrageDauer, sizeof(startAbfrageDauer));
    radio.stopListening();
    //delay(20);
    
}


void warteZeit() {
  radio.powerDown();
  //avr_enter_sleep_mode();
  unsigned int warteZeit;
  sscanf(startAbfrageDauer, "%d", &warteZeit);
  delay(warteZeit*1000+3000);
  Serial.println("ende Wartezeit");
  radio.powerUp();
}

void frageNachAntwort () {
  Serial.println("start frageNachAntwort");
  radio.openWritingPipe(senderAdresse);
  Serial.println("öffne neu");
  bool rslt;
  rslt = radio.write(&frageAntwort, sizeof(frageAntwort));
  Serial.println("radio write");
  if (rslt) {
    if ( radio.isAckPayloadAvailable() ) {
      
            radio.read(&abstimmung, sizeof(abstimmung));
            //newData = true;
            Serial.println(abstimmung);
        }
        else {
            Serial.println("  Acknowledge but no data ");
        }
  }
  Serial.println("failed");
}

I am really not getting anywhere, I have no clue why just that little part is not working...

Thanks for all your help so far.

Larry_2:
Ok, so I changed the code that it completely matches your AckPayload example.

My German is not good enough to see how your code matches mine.

I see radio.stopListening() in the slave code. Why ever would you do that? It should be listening all the time.

The key to using ackPayload is that the data to be sent by the slave MUST be in the ackPayload buffer BEFORE the master sends a message.

...R

Robin2:
My German is not good enough to see how your code matches mine.

I see radio.stopListening() in the slave code. Why ever would you do that? It should be listening all the time.

I thought I would need that in order to open a new readingPipe with a different address.

Robin2:
The key to using ackPayload is that the data to be sent by the slave MUST be in the ackPayload buffer BEFORE the master sends a message.

I am aware of that but not exactly sure where my code does not do that. Is the issue, that I have an if statement with comparing the incoming data from the master before writing the AckPayload?

Robin2:
My German is not good enough to see how your code matches mine.

If you need me to, I can translate everything.

Further, just to narrow the issue down: All of the sketches code works, just in the slaves code

 void antwortenAusgeben() {Serial2.println(dataToSend);
    Serial2.println("startListening");
    if (radio.available()){
      Serial2.println("is daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
      radio.read(&dataReceived, sizeof(dataReceived));
      if (dataReceived[0] == 'n' && dataReceived[1] == 'a') {
        Serial2.println("habs bekommmmmmmmmmmmmxxxxxxxxxxxxxxxxxxxxxxxxxxxxen"); 
        char data = '1';
        Serial2.println(dataToSend);
        radio.writeAckPayload(1, &dataToSend, sizeof(dataToSend));
        tasterFertig = false;
      }
    }}

I am having problems, since the receiver only gets the ack, but no payload. The part of the code on the master/receiver side would be

void frageNachAntwort() {Serial.println("start frageNachAntwort");
  radio.openWritingPipe(senderAdresse);
  Serial.println("öffne neu");
  bool rslt;
  rslt = radio.write(&frageAntwort, sizeof(frageAntwort));
  Serial.println("radio write");
  if (rslt) {
    if ( radio.isAckPayloadAvailable() ) {
      
            radio.read(&abstimmung, sizeof(abstimmung));
            //newData = true;
            Serial.println(abstimmung);
        }
        else {
            Serial.println("  Acknowledge but no data ");
        }
  }
  Serial.println("failed");}

Alright, thank you so much Robin2!

Robin2:
The key to using ackPayload is that the data to be sent by the slave MUST be in the ackPayload buffer BEFORE the master sends a message.

...R

This was indeed the key to get it to work. Thank you very much, you have helped me a lot. Also your tutorials are really great. Have a nice day!

Robin2:
The key to using ackPayload is that the data to be sent by the slave MUST be in the ackPayload buffer BEFORE the master sends a message.

Yes.

So you should change your example to do that for the very first packet too.

startListening clears the tx buffer, so the preload should obviously happen after that call not before it.

 void RF24::startListening(void)
  697 {
  698  #if !defined (RF24_TINY) && ! defined(LITTLEWIRE)
  699   powerUp();
  700  #endif
  701   write_register(NRF_CONFIG, read_register(NRF_CONFIG) | _BV(PRIM_RX));
  702   write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
  703   ce(HIGH);
  704   // Restore the pipe0 adddress, if exists
  705   if (pipe0_reading_address[0] > 0){
  706     write_register(RX_ADDR_P0, pipe0_reading_address, addr_width);  
  707   }else{
  708     closeReadingPipe(0);
  709   }
  710 
  711   // Flush buffers
  712   //flush_rx();
  713   if(read_register(FEATURE) & _BV(EN_ACK_PAY)){
  714     flush_tx();
  715   }
  716 
  717   // Go!
  718   //delayMicroseconds(100);
  719 }
    radio.enableAckPayload();
    radio.writeAckPayload(1, &ackData, sizeof(ackData)); // pre-load data

    radio.startListening();

Larry_2:
I thought I would need that in order to open a new readingPipe with a different address.

I can understand that the Master needs to change addresses so it can contact different slaves but I would expect a slave always to use the same address.

...R