NRF24L01 - Receiving two pipes messed u data

Hi fellow coders,

I am facing a really challenging problem with my NRF modules.

2 Transmitters - 1 Receiver / Arduino Uno all of them.

Everything is working, communication and all data is coming to Rx, diplayed to the display, BUT with a twist.

I display " Room temp 1: "
" Room temp 2: "

data is coming ok, but from times to times, room 1 is showing the value of temp2 and vice versa.

I have done lots of research, but it’s not clear whether the sync is a problem of missing code in terms of setting the pipeNum of the channel.

you can see my codes here

Tx 2

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

#define ONE_WIRE_BUS 3

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

 float Celcius=0;
 float Fahrenheit=0;

RF24 radio(9,10);

const byte rxAddr[3] = {00001,00002,00003} ;


void setup()
{
  sensors.begin();
  Serial.begin(9600);
  radio.begin();
  //radio.setRetries(15, 15);
  radio.openWritingPipe(rxAddr[1]);
  radio.stopListening();
  Serial.println(rxAddr[1]);
}

void loop()
{
  sensors.requestTemperatures(); 
  Celcius=sensors.getTempCByIndex(0); 
 

 float temp2= Celcius;
 Serial.println("temp2 = ");
 Serial.println(temp2);
 radio.write(&temp2, sizeof(temp2));
  
}

Tx 1

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

#define ONE_WIRE_BUS 3

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

 float Celcius=0;
 float Fahrenheit=0;

RF24 radio(9,10);

const byte rxAddr[3] = {00001,00002,00003} ;

void setup()
{
  sensors.begin();
  Serial.begin(9600);
  radio.begin();
  //radio.setRetries(15, 15);
  radio.openWritingPipe(rxAddr[2]);
  radio.stopListening();
  Serial.println(rxAddr[2]);
}

void loop()
{
  sensors.requestTemperatures(); 
  Celcius=sensors.getTempCByIndex(0); 
 

 float temp1= Celcius;
 Serial.println("temp1 = ");
 Serial.println(temp1);
 radio.write(&temp1, sizeof(temp1));
  
}

Rx

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

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// If using software SPI (the default case):
#define OLED_MOSI   5
#define OLED_CLK   6
#define OLED_DC    3
#define OLED_CS    12
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

float temp1;
float temp2;
  
RF24 radio(9, 10);
  
const byte rxAddr[3] = {00001,00002,00003};

void setup()
{
  while (!Serial);
  Serial.begin(9600);
  
  radio.begin();
  radio.openReadingPipe(1, rxAddr[1]);
  radio.openReadingPipe(2, rxAddr[2]);
  radio.startListening();
  Serial.println(rxAddr[1]);
  
  Serial.println(rxAddr[2]);
  
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
    display.begin(SSD1306_SWITCHCAPVCC);
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
  // init done
 
  byte 
}

void loop()
{
  if (radio.available())
  {   
    //radio.read(&temp1, sizeof(temp1));
    //radio.read(&temp2, sizeof(temp2));
    //Serial.println("temp1 is");
    //Serial.println(temp1);
    //Serial.println("temp2 is");
    //Serial.println(temp2);

    
    display.clearDisplay();

    display.setCursor(0,0);  
    display.println("Room 1 temp is:");
    radio.read(&temp1, sizeof(temp1));
    display.setCursor(95,0);
    display.println(temp1);
    display.display();
    

    //delay(1000);

    //display.clearDisplay();
    display.setCursor(0,10);  
    display.println("Room 2 temp is:");
    radio.read(&temp2, sizeof(temp2));
    Serial.println("temp2 is");
    Serial.println( temp2);
    display.setCursor(95,10);
    display.println(temp2);
    display.display();

    //Serial.println(temp2);
    //display.display();
    delay(1000);

    Serial.println("temp1 is");
    Serial.println(temp1);
    Serial.println("temp2 is");
    Serial.println(temp2);   
  }
}

Thanks in advance

PS. I have read the famous two tutorials dozens of times :stuck_out_tongue:

There is an overload of the available() function that returns the pipe number of the data available.

From the RF24 library:

/**
   * Test whether there are bytes available to be read
   *
   * Use this version to discover on which pipe the message
   * arrived.
   *
   * @param[out] pipe_num Which pipe has the payload available
   * @return True if there is a payload available, false if none is
   */
bool available(uint8_t* pipe_num);

For an example of use see this post reply #10.

This Simple nRF24L01+ Tutorial may help.

There is probably no need to use more than a single pipe. Just include in each message an ID code (perhaps 'A' or 'B') so that the receiver know which transmitter sent the data.

Note that if both transmitters send at the same time both messages will be garbled.

...R

const byte rxAddr[3] = {00001,00002,00003};

You do not understand the pipe addresses.

They are 5 bytes long each (normally), not 3 bytes for 3 addresses.

Why do you want to use octal notation for numbers below 8?

You are using pipe 1 and 2, so both addresses have to have the same 4 byte top part, which are bytes 1 to 4, so something like the following may work.

const byte rxAddr[][6] = {"10000", "20000", "30000"};

Much simpler to use 1 pipe and 1 address :)

...R

Hi guys,

Thank you for your replies!

Busy days as usual at work, I will work on my Arduino Project in the evening.

From what I read though, I have not understood how pipes and addresses are working. The reason why I used these addresses and pipes, is a combination of making my code work, and online reading, testing and debugging etc.

I will study again when I get home.

sorry for the silly question.

As per my understanding,

const byte rxAddr[3] = {00001,00002,00003} means;

I declare an array of length 3, with values 00001, 00002, 00003.

The first Tx will have the 00001 address the second Tx the 00002

and the Rx will be the 00003.

This is SO wrong, right?

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)

I hope the method of creating an address in my tutorial will make the concept of addresses easier to understand.

...R

Whandall: const byte rxAddr[][6] = {"10000", "20000", "30000"};

Can you please explain to my what does this line mean?

Like; " I am declaring a dimensional array of type byte, with 3 values "?

Robin, I fully understood your example with the shelves but in the tutorial there it's not clear where is the part of adding new code, and how to scale out to multiple "nodes".

Chris_v: const byte rxAddr[3] = {00001,00002,00003} means;

I declare an array of length 3, with values 00001, 00002, 00003.

The first Tx will have the 00001 address the second Tx the 00002

and the Rx will be the 00003.

I have to ask you again, why do you specify values below 8 in octal notation? (If by any means you think adding zeros to the left right of a number makes it somehow longer, you are wrong).

The pipe addresses are 5 bytes long, your whole array has only 3 bytes for all 3 addresses.

So you use addresses 0: 0x01, 0x02, 0x03, 0x??, 0x?? 1: 0x02, 0x03, 0x??, 0x??, 0x?? 2: 0x03, 0x??, 0x??, 0x??, 0x??

These addresses are illegal* for use in pipes 1 and some of pipes 2 to 5 (the last 4 bytes of the address not identical) and quite hard to match on the other sketch, which will probably have a different memory layout.

You are reading memory that does not belong to you, which can (and often does) show unexpected behaviour.

  • You can get away with such addresses if you are lucky or know exactly what you (and the library) are doing, but having the same upper part for all addresses used for pipes 1 to 5 will always work. Only pipe 1 address is filled with five bytes, pipe address register 2 to 5 are only 1 byte long and are expanded by the content of pipe 1 address.

Chris_v: const byte rxAddr[][6] = {"10000", "20000", "30000"};

Can you please explain to my what does this line mean?

I'm declaring an array of three addresses.

Despite the fact that the addresses only need to have 5 bytes, I declare them with 6 to have the convenience of initializing the addresses with ASCII characters without warning as a string.

The additional 0x00 to terminate the strings/addresses is also convenient for display or manipulation with functions that expect a 0x00 as an end delimiter.

Because the addresses are stored LSB, you can see that my addresses all have the same upper 4 bytes, namely "0000".

Whandall:
I have to ask you again, why do you specify values below 8 in octal notation?
(If by any means you think adding zeros to the left right of a number makes it somehow longer, you are wrong).

I thought it was a hexadecimal system.

whandal, mercy on me :stuck_out_tongue:

I will try to deeper understand how pipes and addresses are working / differentiating from each other.

It’s still not obvious to me, what is an address and a pipe.

Chris_v:
I will try to deeper understand how pipes and addresses are working / differentiating from each other.

It’s still not obvious to me, what is an address and a pipe.

So you should study the datasheet, there are all the details and some good pictures.

Chris_v: It's still not obvious to me, what is an address and a pipe.

Did you read Reply #7?

If it did not make sense to you I would welcome your feedback so I can improve it.

...R

https://howtomechatronics.com/tutorials/arduino/arduino-wireless-communication-nrf24l01-tutorial/

I found this document here back in the days - I guess it's wrong, isn't it?

const byte addresses[][6] = {"00001", "00002"};

// at the Transmitter radio.openWritingPipe(addresses[1]); // 00001 radio.openReadingPipe(1, addresses[0]); // 00002

// at the Receiver radio.openWritingPipe(addresses[0]); // 00002 radio.openReadingPipe(1, addresses[1]); // 00001

Also, is addresses [0] = 00002?

The comments are wrong, in syntax as well as as in content.

The code uses only pipe 0 and pipe 1, so no restrictions apply, hence it is not wrong, only misleading.

There is a difference between "00001" and 00001.

"00001" is a five character text which uses 6 bytes if stored as a zero terminated string.

00001 is an octal constant with the value 1 which fits in a single byte.

So an address 00001 would have an internal representation of 0x01, 0x00, 0x00, 0x00, 0x00. The address used here is "00001" represented as 0x30, 0x30, 0x30, 0x30, 0x31 or '0', '0', '0', '0', '1'.

Ok, I see what you mean.

Still, I eliminated the 2nd Tx in order to understand and play with only one.

The problem persists.

It is displaying in "Room 2 Temp is: " the temp1 variable.

I am missing something big here, I know.

Chris_v:
I eliminated the 2nd Tx in order to understand and play with only one.
The problem persists.

It is displaying in "Room 2 Temp is: " the temp1 variable.

I am missing something big here, I know.

Something like reposting the codes you have running now? :wink:

Rx Code

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

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// If using software SPI (the default case):
#define OLED_MOSI   5
#define OLED_CLK   6
#define OLED_DC    3
#define OLED_CS    12
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

float temp1;
float temp2;
  
RF24 radio(9, 10);
  
const byte rxAddr[][6] = {"00001","00002","00003"};

void setup()
{
  while (!Serial);
  Serial.begin(9600);
  
  radio.begin();
  radio.openReadingPipe(1, rxAddr[1]);
  //radio.openReadingPipe(2, rxAddr[2]);
  radio.startListening();
  //Serial.println(rxAddr[1]);
  
  //Serial.println(rxAddr[2]);
  
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
    display.begin(SSD1306_SWITCHCAPVCC);
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
  // init done

}

void loop()
{
  if (radio.available())
  {   
    //radio.read(&temp1, sizeof(temp1));
    //radio.read(&temp2, sizeof(temp2));
    //Serial.println("temp1 is");
    //Serial.println(temp1);
    //Serial.println("temp2 is");
    //Serial.println(temp2);

    
    display.clearDisplay();

    display.setCursor(0,0);  
    display.println("Room 1 temp is:");
    radio.read(&temp1, sizeof(temp1));
    display.setCursor(95,0);
    display.println(temp1);
    display.display();
    

    //delay(1000);

    //display.clearDisplay();
    display.setCursor(0,10);  
    display.println("Room 2 temp is:");
    radio.read(&temp2, sizeof(temp2));
    Serial.println("temp2 is");
    Serial.println( temp2);
    display.setCursor(95,10);
    display.println(temp2);
    display.display();

    //Serial.println(temp2);
    //display.display();
    delay(1000);

    Serial.println("temp1 is");
    Serial.println(temp1);
    Serial.println("temp2 is");
    Serial.println(temp2);   
  }
}

Tx 1

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

#define ONE_WIRE_BUS 3

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

 float Celcius=0;
 float Fahrenheit=0;

RF24 radio(9,10);

const byte rxAddr[][6] = {"00001","00002","00003"} ;

void setup()
{
  sensors.begin();
  Serial.begin(9600);
  radio.begin();
  //radio.setRetries(15, 15);
  radio.openWritingPipe(rxAddr[2]);
  radio.stopListening();
  //Serial.println(rxAddr[2]);
}

void loop()
{
  sensors.requestTemperatures(); 
  Celcius=sensors.getTempCByIndex(0); 
 

 float temp1= Celcius;
 Serial.println("temp1 = ");
 Serial.println(temp1);
 radio.write(&temp1, sizeof(temp1));
  
}

Tx 2

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

#define ONE_WIRE_BUS 3

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

 float Celcius=0;
 float Fahrenheit=0;

RF24 radio(9,10);

const byte rxAddr[][6] = {"00001","00002","00003"} ;


void setup()
{
  sensors.begin();
  Serial.begin(9600);
  radio.begin();
  //radio.setRetries(15, 15);
  radio.openWritingPipe(rxAddr[1]);
  radio.stopListening();
  //Serial.println(rxAddr[1]);
}

void loop()
{
  sensors.requestTemperatures(); 
  Celcius=sensors.getTempCByIndex(0); 
 

 float temp2= Celcius;
 radio.write(&temp2, sizeof(temp2));
  
}

Supposing Room 1 temp is 10 and Romm 2 temp is 20;
Screen is displaying;

Second 1;

Room 1 temp is 10.0
Room 1 temp is 20.0

Second 2;

Room 1 temp is 10.0
Room 1 temp is 10.0

Second 3;

Room 1 temp is 10.0
Room 1 temp is 0.0

Second 4;

Room 1 temp is 20.0
Room 1 temp is 10.0

Second 5;

Room 1 temp is 10.0
Room 1 temp is 10.0

etc.

There is no pattern, I just typed out a random sequence.

What a mess.

The addresses are still invalid for usage in pipe 1 and 2 to 5.

Your reception method is without any sensibility. (Oh, there is a packet, lets read two packets and suppose the first came from TX 1, which can not send anything because nobody listens to its pipe address, then TX 2 data gets overwritten by junk because there is most likely only one packet available.)

Basically you need something like:

void loop() {
byte pipe = 7;
  if (radio.available(&pipe)) {
    if (pipe == 1) {
      radio.read(&temp1, sizeof(temp1));
      Serial.print(F("temp1 is "));
      Serial.println(temp1);
    } else if (pipe == 2) {
      radio.read(&temp2, sizeof(temp2));
      Serial.print(F("temp2 is "));
      Serial.println(temp2);
    }
  }
}