RF24 Multiple Tx One Rx and Ack

I have one receiver and 2 transmitters. My code send data to receiver successfully, but the ack packet is not receiving by Tx1. Tx2 receive it as the value is analogWrite to pin 22 which is hooked LED, it works. What could be the particular reason for this?

Receiver Code

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

RF24 radio(8,10);         

uint8_t address[][6] = {"1Node", "2Node"};

typedef struct {
  uint8_t id;
  uint8_t feedback;
}
AckloadStruct;
AckloadStruct ackload;

typedef struct {
  int8_t joyX;
  int8_t joyY;
  uint8_t header;
  uint8_t finger1;
  uint8_t finger2;
  uint8_t finger3;
  uint8_t finger4;
  uint8_t finger5;
  uint8_t buttonJoy;
}
GeneralPayloadStruct;   

int Lfingers[5];
int Rfingers[5];

int rightJoyX = 0;
int rightJoyY = 0;
int rightJoyButton = false;

int leftJoyX  = 0;
int leftJoyY  = 0;
int leftJoyButton = false;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  
  radio.begin();  

  radio.openReadingPipe(0, address[0]);
  radio.openReadingPipe(1, address[1]);
  
  radio.setDataRate(RF24_2MBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.setRetries(5, 15);

  ackload.feedback = 0;
  ackload.id = 1;
  radio.writeAckPayload(1, &ackload, sizeof(ackload));
  radio.writeAckPayload(0, &ackload, sizeof(ackload));
  radio.startListening();

  delay(500);
}

void loop() {
    uint8_t pipe;
    if (radio.available(&pipe)) {
      GeneralPayloadStruct received;

      uint8_t bytes = radio.getDynamicPayloadSize();
      radio.read(&received, sizeof(received));
      ackload.feedback++;
      analogWrite(LED_BUILTIN, ackload.feedback);

      switch(received.header){
        case 1:
          Lfingers[0] = received.finger1;
          Lfingers[1] = received.finger2;
          Lfingers[2] = received.finger3;
          Lfingers[3] = received.finger4;
          Lfingers[4] = received.finger5;
          leftJoyButton = received.buttonJoy;
          leftJoyX = received.joyX;
          leftJoyY = received.joyY;
          radio.writeAckPayload(0, &ackload, sizeof(ackload));
        break;
        case 2:
          Rfingers[0] = received.finger1;
          Rfingers[1] = received.finger2;
          Rfingers[2] = received.finger3;
          Rfingers[3] = received.finger4;
          Rfingers[4] = received.finger5;
          rightJoyButton = received.buttonJoy;
          rightJoyX = received.joyX;
          rightJoyY = received.joyY;
          radio.writeAckPayload(1, &ackload, sizeof(ackload));
        break;
      }
   }
}

Tx1 Code

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

uint8_t address[][6] = {"1Node", "2Node"};

RF24 radio(7,8); //7,8

unsigned long stime = 0;

typedef struct {
  uint8_t id;
  uint8_t feedback;
}
AckloadStruct;
AckloadStruct received;

typedef struct {
  int8_t  joyX;
  int8_t  joyY;
  uint8_t header;
  uint8_t finger1;
  uint8_t finger2;
  uint8_t finger3;
  uint8_t finger4;
  uint8_t finger5;
  uint8_t buttonJoy;
}
GeneralPayloadStruct;

GeneralPayloadStruct gpStruct;

void setup() 
{
  Serial.begin(115200);
  delay(500);

  radio.begin();  

  radio.openWritingPipe(address[0]);

  radio.setDataRate(RF24_2MBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.setRetries(5, 15);

  pinMode(22, OUTPUT);

  for(int i = 0; i < 5; i++){
    digitalWrite(22, HIGH);
    delay(500);
    digitalWrite(22, LOW);
    delay(500);
  }

  Serial.println("Left Node Started!");

  stime = millis();
}

void loop() 
{
  //if(!digitalRead(PWR_BUTTON_PIN)) return;
  
  if(millis() - stime > 50){
    sendData();
    stime = millis();
  }
}

void sendData()
{
  gpStruct.header     = 1;
  gpStruct.finger1    = 0;
  gpStruct.finger2    = 0;
  gpStruct.finger3    = 0;
  gpStruct.finger4    = 0;
  gpStruct.finger5    = 0;
  gpStruct.buttonJoy  = 0;
  gpStruct.joyX       = 50;
  gpStruct.joyY       = 50;

  bool report = radio.write(&gpStruct, sizeof(gpStruct));

    if(radio.available()){
      radio.read(&received, sizeof(received));
      if(received.id == 1){
        analogWrite(22, received.feedback);
      }
  }
}

Tx2 Code

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

uint8_t address[][6] = {"1Node", "2Node"};

RF24 radio(7,8); //7,8

unsigned long stime = 0;

typedef struct {
  uint8_t id;
  uint8_t feedback;
}
AckloadStruct;
AckloadStruct received;

typedef struct {
  int8_t  joyX;
  int8_t  joyY;
  uint8_t header;
  uint8_t finger1;
  uint8_t finger2;
  uint8_t finger3;
  uint8_t finger4;
  uint8_t finger5;
  uint8_t buttonJoy;
}
GeneralPayloadStruct;

GeneralPayloadStruct gpStruct;

void setup() 
{
  Serial.begin(115200);
  delay(500);

  radio.begin();  

  radio.openWritingPipe(address[1]);

  radio.setDataRate(RF24_2MBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.setRetries(5, 15);

  pinMode(22, OUTPUT);

  for(int i = 0; i < 5; i++){
    digitalWrite(22, HIGH);
    delay(500);
    digitalWrite(22, LOW);
    delay(500);
  }

  Serial.println("Left Node Started!");

  stime = millis();
}

void loop() 
{
  //if(!digitalRead(PWR_BUTTON_PIN)) return;
  
  if(millis() - stime > 50){
    sendData();
    stime = millis();
  }
}

void sendData()
{
  gpStruct.header     = 2;
  gpStruct.finger1    = 0;
  gpStruct.finger2    = 0;
  gpStruct.finger3    = 0;
  gpStruct.finger4    = 0;
  gpStruct.finger5    = 0;
  gpStruct.buttonJoy  = 0;
  gpStruct.joyX       = 50;
  gpStruct.joyY       = 50;

  bool report = radio.write(&gpStruct, sizeof(gpStruct));

    if(radio.available()){
      radio.read(&received, sizeof(received));
      if(received.id == 1){
        analogWrite(22, received.feedback);
      }
  }
}

I've only had a quick look through your code and I do it another way using RF24Network library and I have 3TX units an 1 RX unit all talking to each other without any issues so far.

Have a look here it may be of interest/help
https://howtomechatronics.com/tutorials/arduino/how-to-build-an-arduino-wireless-network-with-multiple-nrf24l01-modules/

You don't have to typedef structs, they are already types.

struct AckloadStruct {
  uint8_t id;
  uint8_t feedback;
} ackload;

That looks cleaner to me and allows you to define your single object in one shot.

What purpose has the delay in setup?

You read the pipe, but you don't use its value,
why would you rely on the packet to specify the pipe that has to be reloaded?

You read the true packet length and read some other length, why?

In the receivers, you don't even bother to retrieve the true size.

Likewise, you should move the reloading up to the earliest possible moment
to minimize the time without a waiting ack payload.

Basically, your sketch should work.

Which Arduinos are you using? What NRF modules do you use? How are they powered?

BTW checking for failed sends is a good idea that you should embrace.

1 Like

Thanks. I was using the RF24Network but I couldn't setup it to get ack data, as I didn't want to manually change the role.

Hi,
Thanks for the optimization :blush:
I added a delay if somehow I'd miss packets on Rx.
About packet length, I tried using the packet length on radio.read to see if there was something up with it.
Thanks! I will correct the code.

I use these modules

Modules

For Receiver is a Leonardo
Transmitters are Teensy 3.1 and Teensy 4.0

You should pack the structures, the Teensys use 32 bit alignment IIRC.
Your current GeneralPayloadStruct is bigger than 32 bytes,
and the Leonardo uses a different packet layout (no alignment).

see NRF24L01 example code with Arduino Uno and OpenCM - #14 by Whandall

1 Like

Thank you! I will let know after some testings.