Method for rapidly receiving from multiple radios using nRF24 Library?

I started by taking a look at this nRF24 multiple transmitters example on github for simply connecting multiple transmitters to a single receiver:

But I notice that when both of my transmitters are sending then the receiver seems to favor only one of them and communication becomes sluggish. The only way to get consistent updates from either radio is to turn the other radio off completely.

I need my receiver to consistently get updates from my two transmitters pretty much as fast as possible.

I am afraid to add synchronization and don’t want to introduce any added latency.

But at this point I’m desperate to simply receive regular updates from both radios and can’t figure it out on my own.

  • Am I trying to send too much data causing one radio to be shoved out of the conversation?
  • Should I have two receivers on separate channels for this?
  • Are there any other good examples showing how to receive data from multiple radios at high rates?

Transmitter (multiple instances)

#include <SPI.h>

#include "nRF24L01.h"

#include "RF24.h"

int transmitterId;

// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
//Contacts from the radio to connect NRF24L01 pinamnam -> Arduino

//SCK -> 13
//MISO -> 12
//MOSI -> 11
//CSN -> 10
//CE -> 9

RF24 radio(9, 10);

// this is not the channel address, but the transmitter address
const uint64_t pipe = 0xE8E8F0F0E1LL;

//button connected to these pins

int buttonPin1 = 2;

void setup(void) {

  // CHANGE THIS PER EACH TRANSMITTER, from 0 to 4
  transmitterId = 1;

  radio.begin();

  // the following statements improve transmission range
  radio.setPayloadSize(2); // setting the payload size to the needed value
  radio.setDataRate(RF24_250KBPS); // reducing bandwidth

  radio.openWritingPipe(pipe); // set the transmitter address

}

void loop(void) {

  //until the button (buttonPin1) pressed send the package (id) to receiver Arduino
  //if (digitalRead(buttonPin1) == HIGH) {

    // some implementations automatically shut down the radio after a transmission: this
    // ensures the radio is powered up before sending data
    radio.powerUp();

    // read and write expect a reference to the payload (& symbol)
    // second argument is the packet length in bytes (sizeof(int) == 2)
    radio.write(&transmitterId, 2);
  //}
}

Receiver

#include <SPI.h>

#include "nRF24L01.h"

#include "RF24.h"

int senderId;

// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
//Contacts from the radio to connect NRF24L01 pinamnam -> Arduino

//SCK -> 13
//MISO -> 12
//MOSI -> 11
//CSN -> 10
//CE -> 9

RF24 radio(9, 10);

// this is not the channel address, but the transmitter address
const uint64_t pipe = 0xE8E8F0F0E1LL;

//LEDs connected to these pins
// ENSURE YOU HAVE THE RIGHT DIGITAL PINS HERE
int LEDpins[5] = { 2, 3, 4, 5, 6 };

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

  radio.begin();

  // the following statements improve transmission range
  radio.setPayloadSize(2); // setting the payload size to the needed value
  radio.setDataRate(RF24_250KBPS); // reducing bandwidth

  radio.openReadingPipe(1, pipe); // Open one of the 6 pipes for reception

  radio.startListening(); // begin to listen

  // Enable all the LED pins as output
  for (int i = 0; i < 5; i++) {
    pinMode(LEDpins[i], OUTPUT);
    digitalWrite(LEDpins[i], LOW); // this is unnecessary but good practice nonetheless
  }

}

void loop(void) {

  // Turns off all the LEDs
  for (int i = 0; i < 5; i++) {
    digitalWrite(LEDpins[i], LOW);
  }

  if (radio.available()) {


    radio.read(&senderId, 2); //done = radio.read(&senderId, 2);


    //Light up the correct LED for 50ms
    digitalWrite(LEDpins[senderId], HIGH);
    Serial.print("LED ");
    Serial.print(senderId);
    Serial.println(" On");
    delay(50);
  }
}
    delay(50);

That has NO place in code that needs to "get updates from my two transmitters pretty much as fast as possible.".

A receiving nRF24 has only one wireless so if two or more nRF24s transmit to it at the same time all the messages are likely to be garbled.

If the transmitting devices transmit frequently at random times there will almost certainly be data collisions and using auto-retries is likely to make matters worse rather than better.

One effective way to deal with this is to get the master to poll the slaves in turn and use the auto-acknowledgement feature to enable the slave to send data when it is polled. Have a look at the second example in this Simple nRF24L01+ Tutorial

If a polling system is not practicable then you will need a complex system to recognize and deal with data collisions. And that will probably slow the throughput.

How many messages per second do you want to receive?

It will be much easier to help if you describe the project you are trying to create.

...R

Robin2:
One effective way to deal with this is to get the master to poll the slaves in turn and use the auto-acknowledgement feature to enable the slave to send data when it is polled. Have a look at the second example in this Simple nRF24L01+ Tutorial

Thank you, this was helpful and I think I have managed to get a decently quick and synchronized multi-radio setup working.

SimpleTx - the master or the transmitter

// SimpleTxAckPayload - the master or the transmitter

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


#define CE_PIN   9
#define CSN_PIN 10

const byte slaveAddress1[5] = {'R','x','A','A','A'};
const byte slaveAddress2[5] = {'R','x','A','A','B'};

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 = 1; // send once per second

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

void setup() {

    Serial.begin(115200);
    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

    radio.openWritingPipe(slaveAddress1);//set an initial condition

}

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

void loop() {

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

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

bool flipFlop = false;
void send() {


    radio.openWritingPipe(flipFlop ? slaveAddress1 : slaveAddress2);
    flipFlop = !flipFlop;

    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("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");
    }


    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;
}

SimpleRx.ino - endpoint module #1

// SimpleRxAckPayload- the slave or the receiver

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

#define CE_PIN   9
#define CSN_PIN 10

const byte thisSlaveAddress[5] = {'R','x','A','A','A'};

RF24 radio(CE_PIN, CSN_PIN);

char dataReceived[10]; // this must match dataToSend in the TX
int ackData[2] = {109, -1000}; // the two values to be sent to the master
bool newData = false;

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

void setup() {

    Serial.begin(115200);

    Serial.println("SimpleRxAckPayload Starting");
    radio.begin();
    radio.setDataRate( RF24_250KBPS );
    radio.openReadingPipe(1, thisSlaveAddress);

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

    radio.startListening();
}

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

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.print(ackData[0]);
        Serial.print(", ");
        Serial.println(ackData[1]);
        newData = false;
    }
}

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

void updateReplyData() {
    ackData[0] -= 1;
    ackData[1] -= 1;
    if (ackData[0] < 100) {
        ackData[0] = 109;
    }
    if (ackData[1] < -1009) {
        ackData[1] = -1000;
    }
    radio.writeAckPayload(1, &ackData, sizeof(ackData)); // load the payload for the next time
}

SimpleRx.ino - endpoint module #2

// SimpleRxAckPayload- the slave or the receiver

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

#define CE_PIN   9
#define CSN_PIN 10

const byte thisSlaveAddress[5] = {'R','x','A','A','B'};

RF24 radio(CE_PIN, CSN_PIN);

char dataReceived[10]; // this must match dataToSend in the TX
int ackData[2] = {209, -2000}; // the two values to be sent to the master
bool newData = false;

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

void setup() {

    Serial.begin(115200);

    Serial.println("SimpleRxAckPayload Starting");
    radio.begin();
    radio.setDataRate( RF24_250KBPS );
    radio.openReadingPipe(1, thisSlaveAddress);

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

    radio.startListening();
}

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

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.print(ackData[0]);
        Serial.print(", ");
        Serial.println(ackData[1]);
        newData = false;
    }
}

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

void updateReplyData() {
    ackData[0] -= 1;
    ackData[1] -= 1;
    if (ackData[0] < 200) {
        ackData[0] = 209;
    }
    if (ackData[1] < -2009) {
        ackData[1] = -2000;
    }
    radio.writeAckPayload(1, &ackData, sizeof(ackData)); // load the payload for the next time
}