NRF24L01 works with 3 slaves, but not 4?

Hi,

My goal is to read sensor data (load cell data) from 4 weighing devices (slaves) on a master device using NRF24L01 modules. I used the ackPayload concept - in which the data (tare button state) from the master is sent to each slave & sensor data from each slave is sent back to the master. It worked perfectly with 3 slaves. But when I included an additional 4th slave, the output just displayed results of first transmission & stopped refreshing after - unless I change the tare button state, but it stops refreshing after as well.

With 4 slaves, except for the first transmission or change of tare button state, radio.available() is false, despite the master transmitting data to every slave. Here’s my code:

MASTER CODE

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

//
const byte slaveTotal = 4;
unsigned long SEND_RATE = 2000; 
unsigned long currentTime; 
unsigned long lastSentTime;

RF24 radio(6,7); 

const byte nodeAddresses[slaveTotal][5] = { 
    {'N','O','D','E','1'},
    {'N','O','D','E','2'},
    {'N','O','D','E','3'},
    {'N','O','D','E','4'}
};
float nodeData[slaveTotal][2] = {{1, 0}, {2, 0}, {3, 0}, {4, 0}}; // simple integer array for data from each slave node: { node_id, returned_count }

boolean tare_data = 0;
float adc_val;
float weight_val;
float totalWgt;

// TARE BUTTON
const int buttonPin = 2; 
boolean buttonState;   
boolean lastButtonState = LOW;
unsigned long lastDebounceTime = 0; 
unsigned long debounceDelay = 50;  
  
//SETUP
void setup() {
  Serial.begin(9600);
  Serial.println("[*][*][*] MASTER START [*][*][*]");
  
  radio.begin();
  radio.setPALevel(RF24_PA_MAX); // set power level of the radio
  radio.setDataRate(RF24_250KBPS); // set RF datarate - lowest rate for longest range capability
  radio.setChannel(0x76); // set radio channel to use - ensure all slaves match this
  radio.setRetries(4, 10);
  radio.enableAckPayload(); // enable ackpayload - enables each slave to reply with data
  
  pinMode(buttonPin, INPUT);
}

//LOOP
void loop() {

  currentTime = millis();
  if (currentTime - lastSentTime >= SEND_RATE){
    multiSlaveTx();
    printSlaveOutput();
  } 
}

//
/*printSlaveOutput()
 *  prints ADC & Weight value from each slave, & total weight
 */
void printSlaveOutput(){
 Serial.print("N0 ");
 Serial.print(nodeData[0][0]);
 Serial.print(" ");
 Serial.print(nodeData[0][1]);

 Serial.print("N1 ");
 Serial.print(nodeData[1][0]);
 Serial.print(" ");
 Serial.print(nodeData[1][1]);
   
 Serial.print("N2 ");
 Serial.print(nodeData[2][0]);
 Serial.print(" ");
 Serial.print(nodeData[2][1]);

 Serial.print("N3 ");
 Serial.print(nodeData[3][0]);
 Serial.print(" ");
 Serial.print(nodeData[3][1]);

 totalWgt = nodeData[0][1] + nodeData[1][1] + nodeData[2][1] + nodeData[3][1];
 Serial.print(" T ");
 Serial.print(totalWgt); 
}

/*multiSlaveTx() 
 *  Transmits tare data to multiple slave & Reads load cell data from each slave
 */
void multiSlaveTx() {

  for (byte node = 0; node < slaveTotal; node++){
    radio.openWritingPipe(nodeAddresses[node]);
    tareButtonPress();
    boolean tx_sent = radio.write( &tare_data, sizeof(tare_data) );

    if (tx_sent){
      if (radio.isAckPayloadAvailable()) {              
        radio.read(&(nodeData[node]), sizeof(nodeData[node]));  
      } 
           
    }
  }
  
  lastSentTime = millis();
}

/*tareButtonPress()
 *  update tare button state
*/
void tareButtonPress(){
  int buttonReading = digitalRead(buttonPin);

  if (buttonReading != lastButtonState) {
    lastDebounceTime = millis();
  }
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // if the button state has changed:
    if (buttonReading != buttonState) {
      buttonState = buttonReading;
      
      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
        tare_data = 1;
      } else{
        tare_data = 0;
      }
    }
  }

  lastButtonState = buttonReading;
}

SLAVE CODE

#include <RF24.h> 
#include <SPI.h> 
#include <nRF24l01.h>
#include <printf.h>
#include <HX711.h>

//
RF24 radio(6,7); 
int NODE_ID = 0; 

const byte nodeAddresses[4][5] = { 
    {'N','O','D','E','1'},
    {'N','O','D','E','2'},
    {'N','O','D','E','3'},
    {'N','O','D','E','4'}
};

float nodeData[4][2] = {{1, 0}, {2, 0}, {3, 0}, {4, 0}}; // simple integer array for data from each slave node: { node_id, returned_count }

float adc_val;
float weight_val;
boolean tare_data = 0;

HX711 loadcell;
int avgSamp = 5; //No of readings taken to be averaged; Tradeoff of processing time & noise/fluctuations
const int LOADCELL_DOUT_PIN = 4;
const int LOADCELL_SCK_PIN = 5;

const long LOADCELL_OFFSET = -166945;
const long LOADCELL_DIVIDER = -20547; 

//SETUP
void setup() {
  Serial.begin(9600);
  Serial.println("[*][*][*] SLAVE START [*][*][*]");
  radio.begin(); 
  radio.setPALevel(RF24_PA_MAX); // set power level of the radio
  radio.setDataRate(RF24_250KBPS); // set RF datarate
  radio.setChannel(0x76); // set radio channel to use - ensure it matches the target host     
  radio.openReadingPipe(1, nodeAddresses[NODE_ID]);
  radio.enableAckPayload(); // enable ack payload - slave replies with data using this feature
  radio.writeAckPayload(1, &nodeData, sizeof(nodeData));
  radio.startListening();    
   
  loadcell.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  loadcell.set_scale(LOADCELL_DIVIDER);
  loadcell.set_offset(LOADCELL_OFFSET);
}

//LOOP
void loop()
{ 
  if ( radio.available() ) {
    tareRead();
    loadcelldataWriteAck();
  }
}

//
/* tareRead()
 *    read tare button press data from Master & tare loadcells;
 */
void tareRead()
{
  radio.read( &tare_data, sizeof(tare_data) );
  //Serial.println(tare_data);
  if (tare_data == 1) {
    loadcell.tare();
  }  
}

/*loadcelldataWriteAck()
 *    Send load cell data to Master as ackPayload
 */
void loadcelldataWriteAck() 
{
  adc_val = loadcell.read_average(avgSamp);
  weight_val = loadcell.get_units(avgSamp); 
  nodeData[NODE_ID][0] = adc_val;
  nodeData[NODE_ID][1] = weight_val;
  radio.writeAckPayload(1, &(nodeData[NODE_ID]), sizeof(nodeData[NODE_ID]));
}

My code is based on Ben Fraser’s & Robin2’s codes:

I appreciate any help, thank you!

If you are having a wireless problem then I suggest you start with the simplest possible program that just sends (and receives) fixed data. In other words leave out all the measurement stuff.

If it was my problem I would use the multi-example in my Simple nRF24L01+ Tutorial and I would not do anything until it works with the required number of slaves. When that worked I would gradually extend the code to deal with the extra stuff testing after every change.

Separately ...

I can see why you have this array in the master

float nodeData[slaveTotal][2] = {{1, 0}, {2, 0}, {3, 0}, {4, 0}};

but I would not have it in my slaves. I would just have a simple 2 element array in the slaves to make things as simple as possible

float nodeData[2] = {1, 0};

What is the purpose of putting

    tareButtonPress();

in the middle of the wireless function multiSlaveTx() ?

To my mind it has no business being there. Keep things simple.

...R

With four clients and a constant packet content all but the very first packets will be considered duplicate,
because the internal ID counter is only two bits long.

Add a counter to the tare packet that gets incremented after each successful send,
that way the crc will be different.

Thanks for your reply Robin2 & Whandall, greatly appreciate your help!

So, I redid my code as advised by Robin2 and it works! Was trying hard not to start from scratch (thinking I would spend more time) but I've been debugging for days. Now, it's definitely cleaner, simpler, faster & way easier to debug.

Previously, I had this array in the master so that I could add the total weight values together by indexing them. Just copied them in the slave without thinking.

float nodeData[slaveTotal][2] = {{1, 0}, {2, 0}, {3, 0}, {4, 0}};

Now, I'm just adding the weight values in the for loop in send() this way. Don't know if it's the best way, but it works!

totalWgt += ackData[1];