nRF24L01+ - Disabling auto ack in favor of manual ack payloads?

Background
I am trying to build a basic project involving 3 nRF24L01+ modules. One will act as a transmitter, the other two receivers. The transmitter will send identical payloads to both receivers at all times, and as such all devices share the same address and pipe.

However, one of the receivers is far for important than the other, and I want to be absolutely sure that this receiver has gotten every message (or at least as many as possible). The other receiver is best-effort, it would be nice if it got most of them but I don’t care if it misses any.

The Problem
In normal operations with autoAcks, it seems to be whichever receiver gets the message first will send an auto ack and the transmitter will cease any additional retries or write attempts. This gets me in a scenario where the important receiver is missing many messages because the 2nd answered first. I would like to make it so that the transmitter will only listen to acks from the first receiver, and once its good, will stop writing. If the other receiver has gotten the message as this point, great. If not, I don’t care.

My Idea
To accomplish this, my thought was to disable autoAcks (radio.setAutoAck(false)) on all devices and have the important receivers manually write its own ackPayload. The transmitter would then read this ack payload to know when to move on. The 2nd, less important receiver sends no ack payload at all and gets whatever it can.

However, as soon as I disable autoAcks, the receivers stops getting any messages of any kind. Even with a manual retry scheme of sending the writing the same message up to 10 times, there is never an ack payload present and no data is ever received on the receiver. As soon as I re-enable autoAcks, everything works again and I can see the ackPayloads on the transmitter.

Transmitter

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

RF24 radio(9, 10);  // CE, CSN
const byte address[5] = {'0', '1', '2', '3', '4'};

const float LOOP_INTERVAL = 16.666;  //60Hz in ms
const char text[] = "Hello World";

long previousMillis = 0;
int16_t ackData = 0;

void setup() {
    Serial.begin(9600);
    radio.begin();
    radio.setChannel(115);
    radio.setPALevel(RF24_PA_MAX);
    radio.setDataRate(RF24_250KBPS);
    radio.openWritingPipe(address);
    radio.enableDynamicPayloads();
    radio.enableAckPayload();
    radio.setRetries(3, 15);
//  radio.setAutoAck(false);  //This breaks it
    radio.stopListening();
    Serial.println("starting up...");
}

void loop() {
    if((millis() - previousMillis) < LOOP_INTERVAL) {
        return;
    }
    previousMillis = millis();
    
    ackData = 0;
    radio.write(&text, sizeof(text));

    if(radio.isAckPayloadAvailable()) {
        radio.read(&ackData, sizeof(ackData));
        Serial.println("ack");
    } else {
        Serial.println("no ack");
    }
}

Important Receiver code (other receiver would be identical with the ack payload stuff removed).

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

RF24 radio(35, 34); // CE, CSN
const byte address[5] = {'0', '1', '2', '3', '4'};

const int16_t ACK_DATA = 1;

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.setChannel(115);
  radio.setPALevel(RF24_PA_MAX);
  radio.setDataRate(RF24_250KBPS);
  radio.openReadingPipe(0, address);
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.writeAckPayload(0, &ACK_DATA, sizeof(ACK_DATA));
//  radio.setAutoAck(false);  //This breaks it
  radio.startListening();
}
void loop() {
  if (!radio.available()) {
      return;
  }
  
  char text[32] = "";    
  radio.read(&text, sizeof(text));
  radio.writeAckPayload(0, &ACK_DATA, sizeof(ACK_DATA));
  Serial.println(text);
}

I’m using the RF24 library: http://tmrh20.github.io/RF24/index.html
My transmitter is an Arduino Nano
My receivers are Teensy 4.1s (These devices work fine under normal circumstances so it is likely not a compatibility issue, rather a misunderstanding of the library or these radios on my part).

Questions
My questions are:

  1. Why does disabling autoAck seem to affect manual ackPayloads?

  2. Is there a better way than this to accomplish my original goal (ensure one particular receiver out of two gets the message). I thought about putting the two receivers on different addresses, but in the interest of speed I want to minimize the number of write calls.

An acknowledgement with or without a payload is send automatically when a message is received. There is no such thing as a manual ackPayload.

It seems to me you have a few options.

You could disable acknowledgements on all except one of the slaves - in other words leave it working for the important one. Then if several slaves are listening on the same address only one of them will send an acknowledgement.

You could give each slave a different address and get the master to send the message to each of them in turn. A message plus acknowldegement only takes a few millisecs so there would be very little time delay between the first and last slave receiving the message.

And a more complicated solution is to disable acknowledgements but get one of the slaves (the important one) to switch from listening to sending to send a message to the master which, of course, must have switched from talking to listening at the right time. The third example in this Simple nRF24L01+ Tutorial illustrates this - although acknowledgements are enabled in the tutorial code.

...R

Hi Robin, thank you for your response. I think I see now where my misunderstanding was.

Just to make sure I understand, acknowledgements are handled automatically by the radios, and the ack payload is simply a way to append additional information onto this ack, but not replace it. Because of this, setting autoAck to false would disable acknowledgements altogether, so there would be no way for my ack payloads to ever work. Is that correct?

I like the suggestions you outlined. The first one of disabling acknowledgements on the other receiver sounds simple and the closest to what I was trying to accomplish so I’ll try that. If it doesn’t work I’ll give the different addresses a shot.

jmattingley23:
Just to make sure I understand, acknowledgements are handled automatically by the radios, and the ack payload is simply a way to append additional information onto this ack, but not replace it. Because of this, setting autoAck to false would disable acknowledgements altogether, so there would be no way for my ack payloads to ever work. Is that correct?

Yes

...R