NRF24L01 problem with multiple receivers

Hello,

I have a serious problem with my nrf24l01 setup. I need up to eight wirless quizbuttons connected to one master. I followed Robin2's "Two way transmission, auto ack" examples and wrote my code according to them. All works well with the current code (see below). However when I connect more than 3 buttons, the systems doesn't work anymore. When I connect 4 or more buttons, the radio.available in the loop inside the buttons constantly returns false. I have no clue why this is. I've tried multiple things, like setting different setRetries, flush_tx, delaying the send rate of the loop which constantly sends to the buttons. But none of the things I tried worked.
The code:

MASTER:

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

RF24 radio(7,8);

int n_active_buttons = 0;
const byte number_of_buttons = 8;
const byte BUTTONS[number_of_buttons][5] = {
                                              {'R', 'x', '0', '0', '1'},
                                              {'R', 'x', '0', '0', '2'},
                                              {'R', 'x' ,'0', '0', '3'},
                                              {'R', 'x', '0', '0', '4'},
                                              {'R', 'x', '0', '0', '5'},
                                              {'R', 'x', '0', '0', '6'},
                                              {'R', 'x', '0', '0', '7'},
                                              {'R', 'x', '0', '0', '8'}
                                           };


bool result = false;

unsigned long tempReceiveTime = 0;
unsigned long startTime = 0;

bool PRESS = false;

int test = 0;

struct initDataStruct{
  char ID;
  int value;
  bool wasPressed;
}INIT_ACK_DATA;

struct DataStruct{
  char wasButtonPressed;
}ACK_DATA;

struct transmitData{
  char askVar = 'o';
}TX_DATA;

class quizButton
{
  public:
    bool lock = false;
    unsigned long startTime;
    unsigned long receivedTime;
    int score = 0;
    byte pipeID[5] = {'R','x','0','0','9'};
};

quizButton buttons[number_of_buttons];

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

  radio.begin();
  radio.setPALevel(RF24_PA_HIGH);
  radio.setDataRate(RF24_1MBPS);
  radio.setChannel(0x66);
  radio.setRetries(3,5);
  radio.enableAckPayload();
  radio.stopListening();
 
  Serial.println("GettingStarted TRANSMITTER");
 
  identifyButtons();
}

void loop()
{
  Serial.print("> Wachten op start...");
  Serial.read();
  while(!Serial.available());
  Serial.println("Starten...");
  Serial.read();
 
  startTime = micros();

  int x = 0;
  bool OK = false;

  while(true)
  {
    for(int i=0; i<n_active_buttons; i++)
    {
      radio.openWritingPipe(buttons[i].pipeID);
      radio.stopListening();
     
      OK = radio.write(&TX_DATA, sizeof(TX_DATA));
      buttons[i].startTime = micros();
     
      if(OK && radio.isAckPayloadAvailable())
      {
        radio.read(&test, sizeof(test));
       
        Serial.print(char(buttons[i].pipeID[4]));
        Serial.print(": ");
        Serial.println(test);
               
        tempReceiveTime = micros();

       
        if(ACK_DATA.wasButtonPressed && !buttons[i].lock)
        {
          buttons[i].lock = true;
          buttons[i].receivedTime = tempReceiveTime;
          x++;
        }
       
      }
    }


    if(x == n_active_buttons)
    {
      break;
    }
  }

  Serial.print("\n");
  Serial.println("DONE");
  Serial.print("Universal starttime: ");
  Serial.println(startTime);
  for(int i=0; i<n_active_buttons; i++)
  {
    Serial.print("Button " + String(i+1) + ": ");
    Serial.print("tStart: " + String(buttons[i].startTime) + ", ");
    Serial.println("tReceive: " + String(buttons[i].receivedTime) + "\n");
  }
}

void identifyButtons()
{
  char SEND_ID = '1';
  char DONE = 'A';
  bool OK = false;
  bool thisButtonIsConfigured[8] = {false, false, false, false, false, false, false, false};

  char youCanStart = 'Y';

  Serial.println("> Voer karakter in om te starten...");

  while(true)
  {
    for(int i=0; i<number_of_buttons; i++)
    {
      if(!thisButtonIsConfigured[i])
      {
        radio.openWritingPipe(BUTTONS[i]);
        radio.stopListening();

        OK = radio.write(&SEND_ID, sizeof(SEND_ID));
       
        if(OK && radio.isAckPayloadAvailable())
        {
          radio.read(&INIT_ACK_DATA, sizeof(INIT_ACK_DATA));
          thisButtonIsConfigured[i] = true;
          buttons[i].pipeID[4] = INIT_ACK_DATA.ID;
          SEND_ID++;
          radio.openWritingPipe(BUTTONS[i]);
          radio.stopListening();
          radio.write(&DONE, sizeof(DONE));
        }
      }else{
        radio.openWritingPipe(BUTTONS[i]);
        radio.stopListening();
        radio.write(&DONE, sizeof(DONE));
      }
    }

    Serial.read();
    if(Serial.available() > 0)
    {
      Serial.read();

      n_active_buttons = int(SEND_ID - '0');
      n_active_buttons--;

      Serial.print(n_active_buttons);
      Serial.println(" knoppen zijn geconfigureerd");

      for(int x=0; x<16; x++)
      {
        for(int i=0; i<n_active_buttons; i++)
        {
          radio.openWritingPipe(buttons[i].pipeID);
          radio.stopListening();
          radio.write(&youCanStart, sizeof(youCanStart));
        }
      }
     

      break;
    }
  }
}

The code below is uploaded to each button with each a different value for {'R','x','0','0','1'};

#include <SPI.h>
#include <nRF24L01.h> //TMRh20
#include <RF24.h>
#include <TimerOne.h>
#include <printf.h>

#define BUTTON 2
#define LED 4

RF24 radio(7,8);

byte MASTER[5] = {'R','x','0','0','4'};

int ackData = 0;

bool buttonWasPressed = false;

char myID;

bool PRESS = false;
int YES = 1111;
int NO = 9999;

struct dataStruct{
  char wasButtonPressed;
}ACK_DATA;

struct receiveData{
  char askVar;
}RX_DATA;

void setup()
{
  Serial.begin(115200);
 
  printf_begin();

  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);

  radio.begin();
  radio.setPALevel(RF24_PA_HIGH);
  radio.setDataRate(RF24_1MBPS);
  radio.setChannel(0x66);
  radio.setRetries(3,5);
  radio.openReadingPipe(1, MASTER);
  radio.enableAckPayload();

  radio.startListening();

  Serial.println("GettingStarted RECEIVER");

  configureID();
}

void loop()
{
  digitalWrite(LED, PRESS);

  if(!PRESS)
  {
    PRESS = !digitalRead(BUTTON);
  }
 
  if(radio.available())
  {
    radio.read( &RX_DATA, sizeof(RX_DATA) );
    if(PRESS)
    {
      radio.writeAckPayload(1, &YES, sizeof(YES));
    }else{
      radio.writeAckPayload(1, &NO, sizeof(NO));
    }
  }

 
}

void configureID()
{
  char obtainedID = ' ';
 
  while(true)
  {
    if(radio.available())
    {
      radio.read( &obtainedID, sizeof(obtainedID) );

      if(obtainedID != 'A')
      {
        myID = obtainedID;
        radio.writeAckPayload(1, &MASTER[4], sizeof(MASTER[4]));
      }else if(obtainedID == 'A')
      {
        break;
      }
    }
  }

  flashID();
}

void flashID()
{ 
  int ID = myID - '0';
  char iCanStart = 'N';

  int i = 0;

  bool ledState = true;

  unsigned long curMil = 0;
  unsigned long prevMil = 0;
  unsigned long interval = 0;

  unsigned long curMilF = 0;
  unsigned long prevMilF = 0;
  unsigned long intervalF = 200;

  interval = (ID * intervalF * 2) + 1000;

  Serial.print("interval: ");
  Serial.println(interval);

  goto STARTHERE;
 
  while(iCanStart != 'Y')
  {
    curMil = millis();
    if(curMil - prevMil > interval)
    {
      prevMil = curMil;
      while(i < ID*2)
      {
/*--->*/STARTHERE:
        curMilF = millis();
        if(curMilF - prevMilF > intervalF)
        {
          prevMilF = curMilF;
          digitalWrite(LED, ledState);
          ledState = !ledState;
          i++;
        }

        if(radio.available())
        {
          radio.read(&iCanStart, sizeof(iCanStart));
        }
      }
      i = 0;
    }
    if(radio.available())
    {
      radio.read(&iCanStart, sizeof(iCanStart));
    }
  }
  digitalWrite(LED,LOW);
}

All the setup like the configureID() functions works. I can setup 8 buttons if I want, but the code in the void loop() of program doesn't work above 3 buttons.
Also, the nrf24l01's are connected to an arduino nano. I'm tapping the 3.3V off of the nano. The circuit is mounted on a real pcb. (not perfboard). I have a 100uF capacitor across the 3.3V rail. I'm powering everything from 4 AA batteries.

Thanks in advance.

I suggest you put your own code to one side for a moment and just get all of your nRF24s working with my demo code completely unchanged - apart from the addresses.

It will be easier to debug with simpler code.

...R

Yes I Will try that tomorrow, then I can rule out if it’s software or hardware. Other then that, have you tried your code with more than 3 Devices?

kaivanveldhoven:
Other then that, have you tried your code with more than 3 Devices?

No. I didn't have enough Arduinos. Actually I think I have a couple more now so I may try later today

...R

I've tested the code with 4 devices. It works...
I've refactored the tx code a bit to count how many times a tx fails over a set amount of tries. I've tested this with multiple different tx treis and also different txIntervals. I have noticed that de 3rd and 4th device fail a lot more then the 1st and 2nd one. In fact the 1st and 2nd one almost never fail and the 3rd and 4th a lot.

Here are some test results:

Number of tx tries: 500
txInterval: 10ms
Number of fails per device:
Device A: 0
Device B: 0
Device C: 11
Device D: 115

Number of tx tries: 1000
txInterval: 10ms
Number of fails per device:
Device A: 1
Device B: 7
Device C: 0
Device D: 183

Number of tx tries: 500
txInterval: 5ms
Number of fails per device:
Device A: 0
Device B: 0
Device C: 73
Device D: 51

Number of tx tries: 1000
txInterval: 5ms
Number of fails per device:
Device A: 0
Device B: 0
Device C: 416
Device D: 585 <-- More then 50% of the time it fails? :o :o

Also with 1 second interval device D fails a lot....
I haven't tried giving the different devices different ID's and retesting as I got no time for that left today. But the results from this are pretty interesting. Also I haven't tried different retries settings, different dataspeeds or different channels..

Here's the code so you can try yourself:

// SimpleTxAckPayload - the master or the transmitter

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


#define CE_PIN 7
#define CSN_PIN 8

const byte slaveAddress[4][5] = {
                                  {'R','x','A','A','A'},
                                  {'R','x','A','A','B'},
                                  {'R','x','A','A','C'},
                                  {'R','x','A','A','D'}
                                };


RF24 radio(CE_PIN, CSN_PIN); // Create a Radio

char dataToSend[10] = "Message 0";
char txNum = '0';
int ackData[2] = {-1, -1}; // to hold the two values coming from the slave
bool newData = false;

unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 5; // send once per second

int i = 0;

int A_FAIL = 0;
int B_FAIL = 0;
int C_FAIL = 0;
int D_FAIL = 0;

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

void setup() {

    Serial.begin(9600);
    Serial.println(F("Source File /mnt/sdb1/SGT-Prog/Arduino/ForumDemos/nRF24Tutorial/SimpleTxAckPayload.ino"));
    Serial.println("SimpleTxAckPayload Starting");

    radio.begin();
    radio.setDataRate( RF24_250KBPS );

    radio.enableAckPayload();

    radio.setRetries(3,5); // delay, count
    
}

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

void loop() {

  for(int x=0; x<500; x++)
  {
    while(i<4)
    {
        currentMillis = millis();
        if (currentMillis - prevMillis >= txIntervalMillis) {
            Send(i);
            i++;
        }
        showData();
    }
    i = 0;
  }

  Serial.print("A_FAIL = ");
  Serial.println(A_FAIL);
  Serial.print("B_FAIL = ");
  Serial.println(B_FAIL);
  Serial.print("C_FAIL = ");
  Serial.println(C_FAIL);
  Serial.print("D_FAIL = ");
  Serial.println(D_FAIL);

  while(1);
}

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

void Send(int num) {

    bool rslt;

    Serial.print("Sending to: ");
    Serial.println(char(slaveAddress[i][4]));

    radio.openWritingPipe(slaveAddress[i]);
    
    rslt = radio.write( &dataToSend, sizeof(dataToSend) );
        // Always use sizeof() as it gives the size as the number of bytes.
        // For example if dataToSend was an int sizeof() would correctly return 2

    Serial.print("Data Sent ");
    Serial.print(dataToSend);
    if (rslt) {
        if ( radio.isAckPayloadAvailable() ) {
            radio.read(&ackData, sizeof(ackData));
            newData = true;
        }
        else {
            Serial.println("  Acknowledge but no data ");
        }
        updateMessage();
    }
    else {
        Serial.println("  TX FAILED..................");
        switch(num)
        {
          case 0: A_FAIL++; break;
          case 1: B_FAIL++; break;
          case 2: C_FAIL++; break;
          case 3: D_FAIL++; break;
        }
    }

    prevMillis = millis();
 }


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

void showData() {
    if (newData == true) {
        Serial.print("  Acknowledge data ");
        Serial.print(ackData[0]);
        Serial.print(", ");
        Serial.println(ackData[1]);
        Serial.println();
        newData = false;
    }
}

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

void updateMessage() {
        // so you can see that new data is being sent
    txNum += 1;
    if (txNum > '9') {
        txNum = '0';
    }
    dataToSend[8] = txNum;
}

I have now had my tutorial code working with 4 slaves. The master was an Uno and the slaves were an Uno, a Mega, a Leonardo and an 8Mhz Atmega 328 on stripboard.

This is the master program - IIRC the only change from the tutorial was to add more addresses. And there was no change to the slave programs except the change of address.

// python-build-start
// action, upload
// board, arduino:avr:uno
// port, /dev/ttyACM0
// ide, 1.6.3
// python-build-end


// MultiTxAckPayload - the master or the transmitter
//   works with two Arduinos as slaves
//     each slave should the SimpleRxAckPayload program
//       one with the adress {'R','x','A','A','A'}
//         and the other with {'R','x','A','A','B'}

// This version to try with more slaves

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


#define CE_PIN   9
#define CSN_PIN 10

const byte numSlaves = 6;
const byte slaveAddress[numSlaves][5] = {
        // each slave needs a different address
                            {'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'}
                        };

RF24 radio(CE_PIN, CSN_PIN); // Create a Radio

//~ char dataToSend[10] = "Message 0";
char dataToSend[10] = "ToSlvN  0";
char txNum = '0';
int ackData[2] = {-1, -1}; // to hold the two values coming from the slave
bool newData = false;

unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 2000; // send once per second

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

void setup() {

    Serial.begin(9600);
    Serial.println("ManyTxAckPayload Starting");

    radio.begin();
    radio.setDataRate( RF24_250KBPS );

    radio.enableAckPayload();

    radio.setRetries(3,5); // delay, count
        // radio.openWritingPipe(slaveAddress); -- moved to loop()
}

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

void loop() {

    currentMillis = millis();
    if (currentMillis - prevMillis >= txIntervalMillis) {
        send();
    }
        // showData(); -- moved into send()
}

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

void send() {

        // call each slave in turn
    for (byte n = 0; n < numSlaves; n++){

            // open the writing pipe with the address of a slave
        radio.openWritingPipe(slaveAddress[n]);

            // include the slave number in the message
        dataToSend[5] = n + '0';

        bool rslt;
        rslt = radio.write( &dataToSend, sizeof(dataToSend) );
            // Always use sizeof() as it gives the size as the number of bytes.
            // For example if dataToSend was an int sizeof() would correctly return 2

        Serial.print("  ========  For Slave ");
        Serial.print(n);
        Serial.println("  ========");
        Serial.print("  Data Sent ");
        Serial.print(dataToSend);
        if (rslt) {
            if ( radio.isAckPayloadAvailable() ) {
                radio.read(&ackData, sizeof(ackData));
                newData = true;
            }
            else {
                Serial.println("  Acknowledge but no data ");
            }
            updateMessage();
        }
        else {
            Serial.println("  Tx failed");
        }
        showData();
        Serial.print("\n");
    }
    Serial.print("\n\n\n");

    prevMillis = millis();
 }


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

void showData() {
    if (newData == true) {
        Serial.print("  Acknowledge data ");
        Serial.print(ackData[0]);
        Serial.print(", ");
        Serial.println(ackData[1]);
        Serial.println();
        newData = false;
    }
}

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

void updateMessage() {
        // so you can see that new data is being sent
    txNum += 1;
    if (txNum > '9') {
        txNum = '0';
    }
    dataToSend[8] = txNum;
}

If I turned off a slave the master showed a fail for it, if I turned the slave on again the master started showing its data again.

...R

OP: I see that you’ve modified the library defaults with higher speed and higher output power. If the units are physically close together, these settings can cause receiver overload and lost packets.

If units are physically close, within a few meters of each other, use the LOW power setting and the slowest baud rate. I’ve had at least eight units working in a small system without issue after finding a clear channel (the default was noisy) and lowering output power.

I still can't get my code to work with more then 3 receivers. I seriously don't understand what's wrong. With 2 or 3 receivers my code works perfectly and as intended. But as soon as I use 4 receivers the radio.available() function returns true only once and then continuously returns false while the transmitter's radio.write() and radio.isAckPayloadAvailable() continuously return true. I seriously don't understand... With robin2's code it works with 4 receivers but I can't find the real difference in the send and receive part in his and mine code...

I also changed the settings to lower values like avr_fred said.

kaivanveldhoven:
I still can't get my code to work with more then 3 receivers.

Have you tried my code?

If that works (and I expect it will) then evolve that to take account of your extra requirements a little bit at a time - testing after every change.

...R

I finally fixed it. The problem seemed to be that I wasn't changing the value of the variable I was sending everytime. My code works now!

Thanks for the update.

...R