[SOLVED]nrf24 and crazy number 4

Hello,
here is a crazy thing!
I have a master module with an nrf24 which communicates with "slave" modules, the number of which can change and whose code is as follows

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

//NRF24L01
RF24 radio(8, SS);                // Micro nRF24L01 (CE,CSN)
char dataToSend = 'm'; //Message envoyé pour collecte datas à chaque capteur
char ackData[32]; //Réponse avec datas des capteurs
uint8_t nbrCapteurs = 0;

const uint8_t numSlaves = 13;
const uint8_t slaveAddress[numSlaves][5] = {
  {'R', 'x', 'A', 'A', 'A'},
  {'R', 'x', 'A', 'A', 'B'},
  {'R', 'x', 'A', 'A', 'C'},
  {'R', 'x', 'A', 'A', 'D'},
  {'R', 'x', 'A', 'A', 'E'},
  {'R', 'x', 'A', 'A', 'F'},
  {'R', 'x', 'A', 'A', 'G'},
  {'R', 'x', 'A', 'A', 'H'},
  {'R', 'x', 'A', 'A', 'I'},
  {'R', 'x', 'A', 'A', 'J'},
  {'R', 'x', 'A', 'A', 'K'},
  {'R', 'x', 'A', 'A', 'L'},
  {'R', 'x', 'A', 'A', 'M'}
};



void setup() {
  Serial.begin(115200);
  SPI.begin(); //Communication RF
  delay(50);
  radio.begin();
  radio.setChannel(120);
  radio.setDataRate(RF24_2MBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.enableAckPayload();
  radio.setRetries(5, 3); // delay, count
  Serial.println("Choisir le nombre de capteurs");
  while (nbrCapteurs == 0) {
    if (Serial.available()) {
      nbrCapteurs = Serial.read() - '0';
    }
  }
}

void loop() {
  for (uint8_t n = 0; n < nbrCapteurs; n++) {
    radio.openWritingPipe(slaveAddress[n]);
    if (radio.write(&dataToSend, sizeof(dataToSend))) {
      if (radio.isAckPayloadAvailable()) {
        radio.read(&ackData, sizeof(ackData));
        Serial.println(ackData);       
      }
    }
  }
}

The code of the "slaves" to whom it requests information

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

#define CE_PIN   10
#define CSN_PIN SS

const uint8_t address[5] = {'R', 'x', 'A', 'A', 'A'};

RF24 radio(CE_PIN, CSN_PIN);

char dataReceived[10]; // this must match dataToSend in the TX
char ackData[32]; // the two values to be sent to the master
bool newData = false;

int test;

void setup() {
  radio.begin();
  radio.setChannel(120);
  radio.setDataRate(RF24_2MBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.openReadingPipe(1, address);
  radio.enableAckPayload();

  radio.startListening();

  radio.writeAckPayload(1, &ackData, sizeof(ackData)); // pre-load data
}

void loop() {
  getData();
  showData();
}

void getData() {
  if ( radio.available() ) {
    radio.read( &dataReceived, sizeof(dataReceived) );
    updateReplyData();
    newData = true;
  }
}

//================

void showData() {
  if (newData == true) {
    Serial.print("Data received ");
    Serial.println(dataReceived);
    Serial.print(" ackPayload sent ");
    Serial.println(ackData);
    newData = false;
  }
}

//================

void updateReplyData() {
  test++;
  if (test > 100){
    test = 0;
  }
  sprintf(ackData, "i0/%i/%s/%s/%s", test, "01", "01", "01");
  radio.writeAckPayload(1, &ackData, sizeof(ackData)); // load the payload for the next time
}

Well it works with 3, 5, 6, etc sensors, but not with 4! : o
I have been desperately looking for a solution for a week.
Any help or suggestion is welcome.
Thank you

You have to make your tx packet non-constant.

There is an internal mechanism to detect duplicate packets
that uses a two bit sequence counter (pid) and the CRC,
so the second packet with the same pid and CRC in a row
makes the receiver believe it got a duplicate.

Screenshot_2020-03-02 ProductSpec Duchess fm - nRF24L01P_Product_Specification_1_0 pdf.png
It will ack the packet, but will not signal a reception.

Add a counter to the packet that gets incremented after each successful write and your done. :wink:
A byte will do, the goal is to change the CRC between two packets hitting one receiver,
because it is very hard to keep track of the pid (packet id) in a multitarget application.

Two packets directly adjacent to the same target should work also the pid should increment after the
ack issued by the duplicate detection.

Screenshot_2020-03-02 ProductSpec Duchess fm - nRF24L01P_Product_Specification_1_0 pdf.png

This has caught me out in the past.

@Whandall's explanation is better than what I was drafting.

...R

Thanks for your answers, but I'm having trouble following you

That is disappointing, I tried to be a clear as possible.

Add a counter (byte) to the (tx) packet that (the counter byte) gets incremented after each successful transmit.

Where is the problem?

You could also adjust the rx sides to the correct packet size,
I think requesting more data than there is available, is at least inefficient.

sorry Whandall, it's just that I'm French and that understanding English is sometimes difficult for me.
From what I understand, I changed my code as follows for the tx

byte dataToSend = 0; //Message envoyé pour collecte datas à chaque capteur
void loop() {
  for (uint8_t n = 0; n < nbrCapteurs; n++) {
    radio.openWritingPipe(slaveAddress[n]);
    dataToSend++;
  if (dataToSend > 9) {
    dataToSend = 0;
  }
    if (radio.write(&dataToSend, sizeof(dataToSend))) {
      if (radio.isAckPayloadAvailable()) {        
        radio.read(&ackData, sizeof(ackData));
        Serial.println(ackData);        
      }
    }
  }
}

and now it works well. thank you very, very, very, very, much for that!!!

But what do you mean by

You could also adjust the rx sides to the correct packet size

Incrementing the one byte sent is alternative to adding a counter.
You don't evaluate the return code of the write, but if you don't care if a packet is transmitted, so be it.

You are only posting part of your changed program, but the

  if (dataToSend > 9) {
    dataToSend = 0;
  }

is strange, in your last sketch its starting value was 'm'.
You are not using the value, just increment it and let it overflow.

From the rx side

char dataReceived[10]; // this must match dataToSend in the TX
    radio.read( &dataReceived, sizeof(dataReceived) );

The comment is not correct , it's not a must, but more a should.
Nevertheless, sending one byte and reading 10 seems (at least) inefficient.

You don't evaluate the return code of the write, but if you don't care if a packet is transmitted, so be it.

yes I prefer to keep an acquisition speed even if I lose some data

in your last sketch its starting value was 'm'.

that's right, I changed that

char dataToSend = 'm';

to that

byte dataToSend = 0;

and for that,

char dataReceived[10]; // this must match dataToSend in the TX
    radio.read( &dataReceived, sizeof(dataReceived) );

it's just a copy paste from the excellent robin2 tutorial, who becomes

char dataReceived;

and who, if I understood correctly, receives the values of the byte counter that you advised me so that the rx does not expect to receive a duplicate?

zjbjbjbz:
yes I prefer to keep an acquisition speed even if I lose some data that's right

Nonsensical. You will not get data, if the transmit fails.

zjbjbjbz:
becomes

char dataReceived;

and who, if I understood correctly, receives the values of the byte counter that you advised me so that the rx does not expect to receive a duplicate?

Yes.

I would have added a counter field, but as you are not using the single byte you can just increment it.

Ok, with byte dataToSend = 0 in declarations, my new code is

void loop() {
  for (uint8_t n = 0; n < nbrCapteurs; n++) {
    radio.openWritingPipe(slaveAddress[n]);
    if (radio.write(&dataToSend, sizeof(dataToSend))) {
      if (radio.isAckPayloadAvailable()) {
        radio.read(&ackData, sizeof(ackData));
        Serial.println(ackData);
        if (dataToSend < 120) { //data changing between transmissions
          dataToSend++;
        } else {
          dataToSend = 1;
        }
      }
    }
  }
}

is that better?

Now you only increment the data if there is a payload available,
I think successful transmission would be better.

Why don't you just increment the data?
What is that magical 120 you are introducing?
Why will you only use 0 once as sent data?

I would use

void loop() {
  for (uint8_t n = 0; n < nbrCapteurs; n++) {
    radio.openWritingPipe(slaveAddress[n]);
    if (radio.write(&dataToSend, sizeof(dataToSend))) {
      dataToSend++;
      if (radio.isAckPayloadAvailable()) {
        radio.read(&ackData, sizeof(ackData));
        Serial.println(ackData);
      }
    }
  }
}

Why don't you just increment the data?
What is that magical 120 you are introducing?

Because a byte stores an 8-bit unsigned number, from 0 to 255, so I chose a value less than 255.

zjbjbjbz:
Because a byte stores an 8-bit unsigned number, from 0 to 255, so I chose a value less than 255.

Why?

What's bad about the 0 and the values from 121 to 255 ?

okay. I could have done

if (dataToSend < 254) { //data changing between transmissions
          dataToSend++;
        } else {
          dataToSend = 0;
        }

if I increment a byte to more than 255, it starts again at 0?

zjbjbjbz:
if I increment a byte to more than 255

Then it is not a byte in the Arduino sense, 8-bit unsigned variables have a range from 0-255.

You can just increment it, it will wrap around automatically.

thanks again.
I put the topic in resolved!

Good that you figured it out. :slight_smile: