nRF24 - One RX and three TX modules

The setup consists of one Rx unit which links with three Tx units in Call Response mode.

All three Tx units send data to the Rx unit once every 0.5 sec and get data back.

Just before doing the whole code i just want to clarify ... it appears that in this scheme the Rx node address is never used. I am just providing only the address part for clarity and the Write / Read functions for clarity... kind of pseudo code.

Rx module code segments :

byte rf_Address[][6] = {"1_PRX", "2_PTX", "3_PTX", "4_PTX"} ; 

setup()
{
  radio.begin(); 
 
  radio.openReadingPipe(1, rf_Address[1]);        
  radio.openReadingPipe(2, rf_Address[2]);
  radio.openReadingPipe(3, rf_Address[3]);
  radio.enableAckPayload();
  radio.startListening();                         
}

loop
{
   byte pipeRxd;

   if ( radio.available(&pipeRxd)) {

   switch (pipeRxd) {

      case 1:   
        
       radio.read( &dataFromTx1, sizeof(dataFromTx1));  // Return boolean checked in actual code
       radio.writeAckPayload(1, &ackDataToTx1, sizeof(ackDataToTx1)); 

       break;

       case 2:   
        
       radio.read( &dataFromTx2, sizeof(dataFromTx2));  // Return boolean checked in actual code
       radio.writeAckPayload(2, &ackDataToTx2, sizeof(ackDataToTx2)); 
      
       break; 

       case 3:

       radio.read( &dataFromTx3, sizeof(dataFromTx3));  // Return boolean checked in actual code
       radio.writeAckPayload(3, &ackDataToTx3, sizeof(ackDataToTx3)); 
      
       break; 

       default :
       break;
 }
}
}

And following is the Tx code ( rest two Tx are identical )

byte rf_Address[][6] = {"1_PRX", "2_PTX"} ;  

setup()
{

 radio.begin();

}

loop
{

  radio.openWritingPipe(rf_Address[1]);

 if ( radio.write(&dataToRx1, sizeof(dataToRx1) )
   {
      if ( radio.isAckPayloadAvailable()    
        {
      
           radio.read(&ackFromRx1, sizeof(ackFromRx1)); 
      
         }
     }

delay(500); 
}

If you see the code above nowhere the Rx address is made use of .. I have a demo of the above setup working ok. But i just want to be sure that i am not missing out anything before the code is firmed up for a new project.

Did you ask a similar question in an earlier Thread? If so please post a link to the earlier Thread.

...R
Simple nRF24L01+ Tutorial

Robin2:
Did you ask a similar question in an earlier Thread? If so please post a link to the earlier Thread.

...R
Simple nRF24L01+ Tutorial

Yes ... i think it is this and its pretty old : nRF24L01 radio modules for three channels RF link ( Solved #16) - Networking, Protocols, and Devices - Arduino Forum

But i guess the context was different and now my main query in the set up as described by me there is no need for the Rx address to be used. So just wanted to make sure that my addressing scheme is robust.

Thanks

@Robin

I again visited your tutorial and i guess i can now exactly describe my issue :

Your example : ( To avoid confusion i am not using Master and Slave nomenclature )

Rx unit Address : {'R','x','A','A','A'}
Tx unit Address : No address.

On the Tx unit you write : radio.openWritingPipe('R','x','A','A','A');
On the Rx unit you receive : radio.openReadingPipe(1, 'R','x','A','A','A' );

So you transmit to the Rx address and the and the Rx unit opens a pipe on its OWN address.

My Example for one Rx-Tx pair : This is working too !

Rx unit Address : "1_PRX" // But not used !!
Tx unit Address : "2_PTX"

On the Tx unit I write : radio.openWritingPipe("2_PTX");
On the Rx unit I receive : radio.openReadingPipe(1, "2_PTX");

As you can see in your case you write to the Rx address and the Rx unit reads it on its own address. You don't use the Tx unit address.

In my case i write to Tx address itself and the Rx unit reads it on Tx address too. I don't use the Rx unit address.

The only difference as i can see is : You are using the one Tx unit sending to many Rx units.

In my case I am using many Tx units sending to one Rx unit.

You may wonder why i am asking all this when the setup is working. I have seen in many cases that such SOCs work even when not optimally configured and so i wanted to be sure i am doing the right thing.

Whats your take on this ??

Mogaraghu:
Your example : ( To avoid confusion i am not using Master and Slave nomenclature )

Rx unit Address : {'R','x','A','A','A'}
Tx unit Address : No address.

On the Tx unit you write : radio.openWritingPipe('R','x','A','A','A');
On the Rx unit you receive : radio.openReadingPipe(1, 'R','x','A','A','A' );

I don't know that I have the stamina to go through all this again.

You will need to post the program(s) that contain the code that corresponds to the above part I have printed in blue.

Because an nRF24 is a transceiver it is much easier to refer to master and slave rather than Rx and Tx. What is Rx one moment can be Tx the next moment.

When you post a birthday card to a friend you put the friend's house-name on the envelope. If an envelope comes through the friend's letter box that has his name and address on it he will know it is intended for him. nRf24 addresses work the same.

...R

Robin2:
I don't know that I have the stamina to go through all this again.

You will need to post the program(s) that contain the code that corresponds to the above part I have printed in blue.

Because an nRF24 is a transceiver it is much easier to refer to master and slave rather than Rx and Tx. What is Rx one moment can be Tx the next moment.

When you post a birthday card to a friend you put the friend's house-name on the envelope. If an envelope comes through the friend's letter box that has his name and address on it he will know it is intended for him. nRf24 addresses work the same.

...R

I am sorry Robin. Of course it must be pretty exhausting even for a very patient individual like you. I appreciate your intention to support still.

Since the unit is both a Tx and Rx in a Call Response mode, to me Master and Slave are pretty confusing. So I kind of avoided it.

Incidentally the blue tagged statements were picked up from your well documented tutorial and hence I am not posting it here.

As in your tagline let me go back and spend some hours to see if all this finally makes sense !!

Ok that half hour spent on old thread makes it clear :

See posts #10 to 12 in this thread : nRF24L01 -One Rx unit to receive data from two Rx units - Solved #11 - Programming Questions - Arduino Forum

Whandall has explained it well. It that kind of fixes it. Its the regular addressing schemes that we are very used to, that creates this issue.

Mogaraghu:
Incidentally the blue tagged statements were picked up from your well documented tutorial and hence I am not posting it here.

I need you to post it so I can see exactly what you are referring to.

...R

// 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'}

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


#define CE_PIN   9
#define CSN_PIN 10

const byte numSlaves = 2;
const byte slaveAddress[numSlaves][5] = {
        // each slave needs a different address
                            {'R','x','A','A','A'},
                            {'R','x','A','A','B'}
                        };

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

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

void setup() {

    Serial.begin(9600);
    Serial.println(F("Source File = /mnt/SGT/SGT-Prog/Arduino/ForumDemos/nRF24Tutorial/MultiTxAckPayload.ino "));
    Serial.println("SimpleTxAckPayload 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");
    }

    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 - 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, -4000}; // the two values to be sent to the master
bool newData = false;

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

void setup() {

    Serial.begin(9600);

    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] < -4009) {
        ackData[1] = -4000;
    }
    radio.writeAckPayload(1, &ackData, sizeof(ackData)); // load the payload for the next time
}

Thank you.

So you transmit to the Rx address and the and the Rx unit opens a pipe on its OWN address.

The way you had expressed that I thought you were saying that the two lines of code were in the same program.

Yes, the receiving nRF24 must check the address of the incoming message to make sure it is the intended recipient. All messages on a channel will be heard by all nRF24's listening on that channel. If the address is wrong the message is ignored.

...R

Robin2:
Thank you.The way you had expressed that I thought you were saying that the two lines of code were in the same program.

Yes, the receiving nRF24 must check the address of the incoming message to make sure it is the intended recipient. All messages on a channel will be heard by all nRF24's listening on that channel. If the address is wrong the message is ignored.

...R

I guess this is the final layman version of it.

The Tx unit 1 opens a writing pipe : radio.openWritingPipe(Timbaktu);
The Tx unit 2 opens a writing pipe : radio.openWritingPipe(Chennai);
The Tx unit 3 opens a writing pipe : radio.openWritingPipe(Plymouth);

The Rx unit opens three reading pipes :
radio.openReadingPipe(1, Timbaktu);
radio.openReadingPipe(2, Chennai);
radio.openReadingPipe(3, Plymouth);

That's all. You are done.

Mogaraghu:
That's all. You are done.

Or they could all write to the same address. Just the same as one person can receive birthday cards from several friends.

Even if you use 3 different addresses your slaves may only talk one at a time.

The only reason to use 3 addresses is so that the master knows which slave sent which message. But you can just as easily (more easily IMHO) deal with that by including a slaveID within the message.

...R

Robin2:
Or they could all write to the same address. Just the same as one person can receive birthday cards from several friends.

Even if you use 3 different addresses your slaves may only talk one at a time.

The only reason to use 3 addresses is so that the master knows which slave sent which message. But you can just as easily (more easily IMHO) deal with that by including a slaveID within the message.

...R

Robin... I am not sure if all the Tx units can write to the same address. As far as I could gather from the data sheet, in the multiciever mode ( which is my case ) the Rx unit stores the Address of the incoming packet on a specific pipe so that it can send back the AckPayload to the same address.

SO going back to my example if all the 3 Tx units do this :

radio.openWritingPipe (Timbaktu)

Will the Rx send the AckPackets based on PipeNumbers ? ( Oh the pipe numbers :o )

You can receive all the messages using a single pipe or you can use multiple pipes. The choice is yours. IMHO life is simpler if you just use 1 pipe.

Maybe this will help - or did I write it for your earlier Thread?

The idea of pipes can be a bit confusing. Think of them as 6 shelves onto which the mail for different residents in an apartment block can be placed. All the letters come through the same mail slot (the radio receiver listening on Channel N) and when they fall on the floor someone picks them up, looks at the name of the recipient (the address the message was sent to) and puts them on the correct shelf (the pipe that has been assigned the same address as the message) or shreds them if they are for a recipient who lives in another block (i.e. if they are for an address that is not assigned to any of the pipes on this nRF24)

...R

@Robin2

Now all three pipes are pushing data , but arbitration seems to be an issue. I know you always advise to two things :

  1. Use the Master as Transmitter and request data from Slaves.
  2. Use one pipe instead of multiple.

OK my problem is :

  1. Using the Slaves as Receivers means I need to put them in Listen mode and that drains the small batteries they have.
  2. No clue how to do this .

Now the Central receiver awaits data from all three slaves and all of them pump about 10 to 25 Bytes of data each once every 500ms. What happens is this : Each slave by itself is perfect to send. But when pipe "1"is sending I switch on pipe "3", pipe "1"is blocked forever and needs a power cycling to resume. That s a bit odd..

// RF24 RADIO COMMUNICATION ROUTINE

void getSendRfData ()
{
  byte pipeRxd ;

  if ( radio.available(&pipeRxd)) 
{
    Serial.print( " Data on Pipe :");                                          // Print the Pipe number....
    Serial.println(pipeRxd);
    
    if ( pipeRxd == 1)                                                             // SLAVE UNIT 01 RF DATA LINK
    {
      radio.read( &dataFromSlave01, sizeof(dataFromSlave01));                          
      radio.writeAckPayload(1, &ackDataToSlave01, sizeof(ackDataToSlave01));  
     }

    if ( pipeRxd == 2)                                                             // SLAVE UNIT 02RF DATA LINK .
    {
      radio.read(&dataFromSlave02, sizeof(dataFromSlave02));                          
      radio.writeAckPayload(2, &ackToSlave02, sizeof(ackToSlave02));              
    }


    if (pipeRxd == 3)                                                              // SLAVE UNIT 03RF DATA LINK
    {
      radio.read(&dataFromSlave03, sizeof(DataFromSlave03));                          
      radio.writeAckPayload(3, &ackDataToSlave03, sizeof(ackDataToSlave03));
    }

  }
}

Of course a Switch ... case is a better and elegant method. Tried that also.. not that I expected that to solve my issue !

Mogaraghu:
But when pipe "1"is sending I switch on pipe "3", pipe "1"is blocked forever and needs a power cycling to resume. That s a bit odd..

I don't understand that.

You can't send on Pipe3. Pipe0 is the only pipe that can transmit. In any case there is no transmit code in the snippet you posted.

Also, you can only have a max of 3 ackPayloads pending at any one time.

I understand why you don't want to poll the slaves. And if it was my project and I was listening for all the slaves all the time I don't think I would use ackPayload at all. I think I would just use 2-way send and receive as in my 3rd example. If you have a byte in the message to identify the sender the master can easily send a subsequent message to that slave. The slave will know to listen for a short period (x millisecs after it sends a message) to allow time for the master to send its reply. Then the slave can go to sleep again. All this could be done with a single pipe on the master.

...R

Robin2:
I don't understand that.

You can't send on Pipe3. Pipe0 is the only pipe that can transmit. In any case there is no transmit code in the snippet you posted.

Also, you can only have a max of 3 ackPayloads pending at any one time.

I understand why you don't want to poll the slaves. And if it was my project and I was listening for all the slaves all the time I don't think I would use ackPayload at all. I think I would just use 2-way send and receive as in my 3rd example. If you have a byte in the message to identify the sender the master can easily send a subsequent message to that slave. The slave will know to listen for a short period (x millisecs after it sends a message) to allow time for the master to send its reply. Then the slave can go to sleep again. All this could be done with a single pipe on the master.

...R

thanks for your post.

Again the problem with pipes and address !! OK what I meant was : When Slave 01 is sending, if Slave 03 also starts sending then Slave 01 is blocked and is not read even after Slave 03 goes off line.

As days pass .... I realize I have bitten too much to swallow. I have to use AckPayloads as three Slaves are using the central Master to pass interactive data among themselves. And so why cant they directly interact ? Its the physical distance between them. The Master sits in the middle helping to halve the distance.

I think its clearly a case of the central node being overburdened with too much data from all three slaves and it locks up. Let me try to first reduce the data throughput and see how it helps. This is the last hold up in a large project and is very frustrating.

Mogaraghu:
Again the problem with pipes and address !! OK what I meant was : When Slave 01 is sending, if Slave 03 also starts sending then Slave 01 is blocked and is not read even after Slave 03 goes off line. in a large project and is very frustrating.

That does not sound like a pipe or an address problem. If two devices transmit at the same time on the same channel the signals will be garbage regardless of pipes or addresses.

Why don't you write a description in English (no computer code, no computer concepts) of what you want your project to do.

...R

Instead of writing I am attaching a preliminary project sketch I made for the setup.

I really appreciate your willingness to help. Kudos.

I'll think some more about this.

May I ask why the Mega is not the "central" unit with the handheld unit another slave - like this

MegaSlaves.png

...R