nRF24L01 -One Rx unit to receive data from two Rx units - Solved #11

I have a RemoteUnit which is the Central Rx unit and there are three are two Tx which send different data to the Rx.

Individually each Tx is able to send the data to Rx. But when I power up both Tx units together, I have a problem. The Pipe number always gets set to "2" and correspondingly the Switch case never evaluates Case 1 .

This is how the Radio addresses are defined :

byte rf_Address[][6] = {"PRX00", "PTX01", "PTX02"} ;  // Radio pipe addresses for CentralUnit_Rx, MainUnit_Tx, GFMReader_Tx

This is the initialization of the Central_Rx unit :

// Setup and configure radio
  radio.begin();
  radio.setChannel(108);                                       // Above most Wifi Channels
  radio.setDataRate( RF24_250KBPS );
  radio.enableDynamicPayloads();                               // Ack payloads are dynamic payloads
  
  radio.openReadingPipe(1, rf_Address[0]);              // Define address for first Tx unit
  radio.openWritingPipe(rf_Address[1]);
  
  radio.openReadingPipe(2, rf_Address[0]);              // Define address for second Tx unit
  radio.openWritingPipe(rf_Address[2]);
  
  radio.enableAckPayload();
  radio.writeAckPayload(1, &ackDataToMain, sizeof(ackDataToMain)); // pre-load data for first acknowledgement
  radio.writeAckPayload(2, &ackDataToGFM, sizeof(ackDataToGFM));
  
  radio.startListening();

And this is the code inside loop()

void loop(void)
{
  // READ THE PUSH BUTTONS ONCE EVERY SCAN`
  rem_PB_State();
  // RADIO COMMUNICATION SECTION.
  byte pipeRxd ;
  if ( radio.available(&pipeRxd)) {
    Serial.print( " Data on Pipe :");                                       // Print the Pipe number....
    Serial.println(pipeRxd);
    switch (pipeRxd) {
      case 1:
        radio.read( &dataFromMain, sizeof(dataFromMain));               // Get the Payload from MAIN Tx
        lcd.setCursor(0, dataFromMain.cursorRow);                            // and Process it....
        sprintf(lcdMessage, "%s", dataFromMain.recdMsg);
        lcd.print(lcdMessage);
        delay(5);
        radio.writeAckPayload(1, &ackDataToMain, sizeof(ackDataToMain));     // Send Ack Payload to Main Tx
        break;
    
      case 2:
        radio.read(&GFM_PulseData, sizeof(GFM_PulseData));
        lcd.setCursor(0, 2);
        sprintf (lcdMessage, "GFM Pulse = %6ul", GFM_PulseData);
        lcd.print(lcdMessage);
        delay(5);
        radio.writeAckPayload(2, &ackDataToGFM, sizeof(ackDataToGFM));
        break;

      default:
        break;
        }
  }
}

How does the Pipe number get set to "2" always ??

You must not use the same address for more than one (reading) pipe.
You can only open one writing pipe so the first openWritingPipe is senseless.
As you are not sending in the part you posted, probably the second openWritingpipe is senseless also.

radio.openReadingPipe(1, rf_Address[0]);              // Define address for first Tx unit
  radio.openWritingPipe(rf_Address[1]);
 
  radio.openReadingPipe(2, rf_Address[0]);              // Define address for second Tx unit
  radio.openWritingPipe(rf_Address[2]);

Furthermore your addresses are malformed for usage in reading pipe 1 to 5.
The addresses of pipe 1 to 5 have to share the upper 4 bytes.

Pipes.png

byte rf_Address[][6] = {"PRX00", "1-PTX", "2-PTX"} ;  // Radio pipe addresses for CentralUnit_Rx, MainUnit_Tx, GFMReader_Tx

would allow address 1 and 2 to function as expected.

@Mogaraghu, post your complete programs - TX and RX.

You may be interested in the answer I just gave in this other Thread

Have you seen this Simple nRF24L01+ Tutorial

...R

Thanks Whandall and Robin.

I must agree that the Pipe and Address are bit of a confusion for me. And though I did read the detailed spec sheet on address forming, I guess I got tripped on the Endianness.

I would have posted the Rx and Tx codes but they are exceeding the size limits and hence will need to work on that before I do.

In the meanwhile if you can let me know the Address formation and initialization for the Rx unit and one Tx unit I can check them out and revert. As even now independently the Rx works OK with each of the Tx unit and only when I try to switch on both Tx units I have an issue. SO I think my woes are more to do with the way the Pipes are defined.

Without seeing the whole code (or at least the communication parts) it's hard to answer.

You can have six different channels, practically only three of them can have an AckPayload.
Two addresses are free, the other four have to share the highest bytes with one of them.
Each Tx-node could send to any of them, even without answering to any (Tx only).
You can not have more than one node that acknowledges for any channel.

One more note: the channel you are using is above 2.5GHz (IIRC) which is not legal here in Germany.

radio.setChannel(108);  // Above most Wifi Channels

Mogaraghu:
In the meanwhile if you can let me know the Address formation and initialization for the Rx unit and one Tx unit I can check them out and revert.

That's why I posted the link to my tutorial.

...R

I am working on the simple codes to demo the structure I use. In the meanwhile I am also reading the Tutorial by Robin. Just to brush up on basics.

Just a query :

Two libraries are used as below :

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

I normally never use the nRF4L01.h though I have it. Any difference on account of this ??

You have to include files included by libraries to keep the build process happy. IIRC

Whandall:
You have to include files included by libraries to keep the build process happy. IIRC

Understood. But my query was that the build is happening with only RF24.h library right now.

I have been able to create a Tx and Rx code to serve as templates for my requirement of one common Rx unit and Two or Three Tx units sending data to it. So all the pipe number allocation happens ONLY with the Rx unit as in any case the Tx unit ALWAYS writes on Pipe 0?

The code below compiles OK but I have not been to test it as I am not near the hardware right now. Please let me know if this has any flaws in addressing scheme.

The Tx Code :

/*
 Demo code for RF Transmitter for use with RF24_2Ch_RxUnit.
 
 This is for TX01, Code for TX02 is also same with required address changes
 
*/

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

/****************** Radio Config ***************************/
/* Set up nRF24L01 radio on SPI bus plus pins 7,8 */
RF24 radio(7, 8); // First pin (7) is CE and Next pin (8) is CSN
/**********************************************************/

byte radioAddress[6] = {"1-PRX"};                   // Radio pipe Address for Rx unit

long dataTo1PRX ;                                    // Data to 1-PRX
boolean ackDataFrom1PRX ;                            // Ack Payload from 1-PRX

void setup() {

  Serial.begin(9600);
  Serial.println(F("*** STARTING TRANSMITTER *** "));

  SPI.begin();

  // Setup and configure radio
  radio.begin();
  radio.setChannel(108);                                       
  radio.setDataRate( RF24_250KBPS );
  radio.enableAckPayload();                                    
  radio.enableDynamicPayloads();                               
  radio.setRetries(5, 10);                                      
}
//$$$$$$$$$$$$$$$
void loop(void)
{
  sendDataToRx();
  delay(500);                              // Use non blocking in actual code
}
//%%%%%%%%%%%%%%%%%

void sendDataToRx()
{
  boolean writeRslt;
  radio.openWritingPipe(radioAddress);                                 
  writeRslt = (radio.write(&dataTo1PRX, sizeof(dataTo1PRX))); 
  if (writeRslt) {
    if ( radio.isAckPayloadAvailable()) {
      radio.read(&ackDataFrom1PRX, sizeof(ackDataFrom1PRX));
    }
    else {
      Serial.println("Blank Ack from Rx ");
    }
  }
  else {
    Serial.println("  Tx failed");
  }  
}

The Rx code for handling two channels :

/*
  Code for acting as a Rx unit for two numbers Tx units.
  Both Tx units send data and get data back by AckPayLoad

  Sample code for forum- hence not inlcuded any code processing
  data being sent / received
*/


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


/****************** User Config ***************************/
/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 8,7 */
RF24 radio(8, 7);
/**********************************************************/


byte rf_Address[][6] = {"1-PRX", "2-PTX", "3-PTX" } ;  // Radio pipe addresses for common Rx unit and two Tx units

long dataFrom2PTX ;                   // Data from 2-PTX
boolean ackDataTo2PTX ;               // Ack Payload to 2-PTX

long dataFrom3PTX ;                   // Data from 3-PTX
boolean ackDataTo3PTX ;               // Ack Payload to 3-PTX

void setup()
{
  Serial.begin(9600);
  Serial.println(F("*** STARTING 2-CHANNEL RECIEVER *** "));

  // Setup and configure radio
  radio.begin();
  radio.setChannel(108);                                       
  radio.setDataRate( RF24_250KBPS );
  radio.enableDynamicPayloads();                               
  radio.enableAckPayload();

  //Up to 6 pipes can be open for reading at once. 
  //Open all the required reading pipes, and then call startListening().

  radio.openReadingPipe(1, rf_Address[1]);       // Is this addressing correct ??
  radio.openReadingPipe(2, rf_Address[2]);

  radio.startListening();
}
//****************************************************
void loop(void)
{
  byte pipeRxd ;
  if ( radio.available(&pipeRxd)) {

    Serial.print( " Data on Pipe :");                                       // Print the Pipe number....
    Serial.println(pipeRxd);

    switch (pipeRxd) {
      case 1:
        radio.read( &dataFrom2PTX, sizeof(dataFrom2PTX));                // Get the Payload from PTX
        delay(5);
        radio.writeAckPayload(1, &ackDataTo2PTX, sizeof(ackDataTo2PTX)); // Send Ack Payload to PTX
        break;

      case 2:
        radio.read(&dataFrom3PTX, sizeof(dataFrom3PTX));
        delay(5);
        radio.writeAckPayload(2, &ackDataTo3PTX, sizeof(ackDataTo3PTX));
        break;

      default:
        break;
    }
  }
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Rx

byte rf_Address[][6] = {"1-PRX", "2-PTX", "3-PTX" } ;  // Radio pipe addresses for common Rx unit and two Tx units
  radio.openReadingPipe(1, rf_Address[1]);       // Is this addressing correct ??
  radio.openReadingPipe(2, rf_Address[2]);

Tx

byte radioAddress[6] = {"1-PRX"};                   // Radio pipe Address for Rx unit
  radio.openWritingPipe(radioAddress);

So what do you think?
Does the tx station send to an address the rx station listens to?

:astonished:

Hmmm...right now I have stopped thinking :confused:

So then would this be the right way ?

byte radioAddress[6] = {"2-PTX"};
radio.openWritingPipe(radioAddress);

Instead of the way I had done it like below ?

byte radioAddress[6] = {"1-PRX"};
radio.openWritingPipe(radioAddress);

( Frankly this is getting to be messy for me. 2-PTX the transmitter, is trying to send data to 1-PRX the receiver, and so I thought I must provide the address of the Receiver. Now if the revised one is the right way, I feel like a posting a letter addressed to myself !! )

The addressing concept of the NRFs is different to standard Ethernet addressing,
there is no special macaddress, despite the possibility to dedicate addresses to special nodes.

Transmitters do not listen to any address unless acknowledging.

Receiving nodes can listen to up to six addresses (with the mentioned restrictions).
Transmitting nodes can send to any address.
It is easy to switch between both modes, the acknowledgement process does it automatically.

Obviously a receiver should listen (at least) to the address a transmitter is using to see its packet.
In the code you posted that was not the case, in the changed code it could.

Personally I'd avoid tying your code to the specifics of one transceiver chip, it won't be portable, and it might make life harder for you now and in the future if you decide to change link-layer.

Mogaraghu:
So then would this be the right way ?

byte radioAddress[6] = {"aaaaa"};  

radio.openWritingPipe(radioAddress);

No.

If you look at my Tutorial you will see how to define the address. It is NOT a cstring so it is not created with double quotes as in "aaaaa". That cstring would require 6 bytes. It is an array of 5 bytes. You could specify it as {97,97,97,97,97} if you wanted it to be aaaaa but I find more obvious to do it as {'a', 'a' ....} even though it needs more typing.

And the address should be the address of the RX that you want to send the message to.

...R
Edit to add ... This was corrected while @Whandall was typing Reply #14 so I don't think his comment applies

Robin2, your wrong, look again. :wink:

Robin2:
And the address should be the address of the RX that you want to send the message to.

What is the address of a rx node that acknowledges six addresses?

I was correcting it (I hope) while you were typing :slight_smile:

...R

OK so I am a happy man today !!

Thanks to Robin for his super Tutorial and Whandall for the fine points on addressing, I have achieved what I wanted - one common Rx unit getting data from two Tx units ( for now and three Tx units finally ).

The addressing pattern caused all the woes - starting from the Endianness of the byte to the fact that only the Rx unit defines the "Pipe number". All this was a bit hard ( for me ) to get from the data sheet.

Now I need to get down to refining the code to avoid transmitting same data more than once and so on.

I have attached the screen shot of the Rx unit display where it updates the millisecond values from both Tx units. This is just to know that data is in fact arriving...

One last query : In the final setup all three Tx units will be sending updates once every 500ms. I am not building any logic to ensure that all three don't try to update all at once - they are discrete units and I am just banking on the fact that they being asynchronous units ( with each other ) - will not sync to microsecond level while sending.

I know this is not fool proof but "near enough" ?? Any other ideas ??

If your nodes happen to collide, they will retry (after a configurable timeout, up to 15 times).
If even that fails, you will see a failing send and could retry in software.

Whandall:
If your nodes happen to collide, they will retry (you can specified how often, up to 15).
If even that fails, you will see a failing send and could retry in software.

Ok great. SO I will not worry about this too much right now. Anyway I am not launching satellites with this code ( fortunately !)

A count of the failed sends could be useful for debugging. :wink: