NRF24L01 anomalies when sending data from 2 clients to a server

I have been working with the NRF24 for a week. I have reviewed the entire RF24 Library documentation (thrh20), although much of it is still not really understood.
I've gone through Nrf24L01-2.4GHz-HowTo and added the capacitors to assist with the current draw.

I have been able to get 2 arduinos exchanging data (a client sending to a server) referencing this: connecting-and-programming-nrf24l01-with-arduino

The next step was to have 2 clients talking to the server. This is where I am being confused by the anomalies I am seeing with the print out of the transferred data. The data from each client comes across, but sometimes (randomly) client 1's data will print out as tmp2 and client 2's data will print out as tmp1.

My code simply reads a temperature (a number between 0-255) at each client and sends it to the server for printout.

Can someone please review my sketches for what may be causing the anomalies? I am new to all of this, and am not an experienced coder.

This is the Server Sketch

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

RF24 radio(7, 8);

const byte Addr1[6] = "00001";
const byte Addr2[6] = "00002";

byte tmp1;
byte tmp2;



void setup()
{
  while (!Serial);
  Serial.begin(115200);
  radio.begin();
  
radio.setPALevel(RF24_PA_MIN);
radio.setChannel(108);
radio.setDataRate( RF24_250KBPS );
}



void loop()
{
 
radio.openReadingPipe(0, Addr1);
radio.startListening();
  if (radio.available())
 {
  radio.read(&tmp1, 1);
radio.stopListening(); 
 } 
 
 
delay(100);


radio.openReadingPipe(0, Addr2); 
radio.startListening();
  if (radio.available())
   { 
  radio.read(&tmp2, 1);
radio.stopListening();  
   }

   
delay(100);

 Serial.print(tmp1); Serial.print(", "); Serial.println(tmp2); 


}

This is the Client Sketch.
The only difference in the two client sketches is

const byte Addr[6] = "00001"; for client 1
and
const byte Addr[6] = "00002"; for client 2

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>
#include "Adafruit_TMP007.h"

Adafruit_TMP007 tmp007;

RF24 radio(7, 8);

const byte Addr[6] = "00001";

byte tmp;


void setup()
{
  tmp007.begin();
  
  radio.begin();
  
  radio.setPALevel(RF24_PA_MIN);
  radio.setChannel(108);
  radio.setDataRate( RF24_250KBPS );
  radio.setRetries(1, 15);
  radio.openWritingPipe(Addr);
  
  radio.stopListening();
}

void loop()
{
  float objt = tmp007.readObjTempC();
  tmp = (objt)*9/5+32;
  
  radio.write(&tmp, 1);
  
delay(100);
}

You don't have any send sychronization between the two slaves. You must address each slave in turn, tell it to send its data, wait for that data, then go on to the next. The way your code is written the slaves are babbling away on their own.

The concept of master/slave must be implemented in a protocol - and because you are providing the slave intelligence you must write that protocol yourself. Your master must tell the slave when to transmit (using the requestFrom() function), and the slave must transmit only when instructed to.

Look at the code for the sonic range finder application for more ideas.

Listening on one channel, receiving, switching to the second channel is a very inefficient way to run a nRF.

You can open two pipes (up to six) in parallel, no need to change configuration.

You should take a look at the used addresses, while your addresses work on pipe 0 and pipe 1,
they will generate a duplicate if used for pipes 2 to 5.

Better change them to "10000" and "20000".

@julianop: I mostly disagree. There can be active sensors that do not have to be polled, I would not call that babbling.

OK, point taken, but "resource conservation" is a sound discipline that is valuable to have under one's belt when one works on larger systems.

Your architecture proposal is absolutely superior to mine, Whandall, but I was going for the "walk, then run" approach first. Getting something working using a simple, sequential technique is instructive and rewarding.

The nRF24 receivers can listen to multiple pipes and distinguish one from the other using the pipe number. However, if the messages overlap in time then the end result is unknown. The starping sketch is a good example.

You could use something like this for the base station (untested)

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

RF24 radio(7, 8);

const byte Addr1[6] = "10000";
const byte Addr2[6] = "20000";

byte tmp1;
byte tmp2;

void setup()
{
  //  while (!Serial); // uncomment on Leonardo
  Serial.begin(115200);
  radio.begin();
  radio.setPALevel(RF24_PA_MIN);
  radio.setChannel(108);
  radio.setDataRate(RF24_250KBPS);
  radio.openReadingPipe(1, Addr1);
  radio.openReadingPipe(2, Addr2);
  radio.startListening();
}

void loop()
{
  byte pipeNum;
  byte newPacket = false;
  if (radio.available(&pipeNum)) {
    newPacket = true;
    if (pipeNum == 1) {
      radio.read(&tmp1, 1);
    } else {
      radio.read(&tmp2, 1);
    }
  }
  if (newPacket) {
    Serial.print(tmp1);
    Serial.print(F(", "));
    Serial.println(tmp2);
  }
}

Arctic_Eddie:
However, if the messages overlap in time then the end result is unknown.

No. If messages overlap in time, both will be destroyed by the collision and resent.

In addition, the ACK function won't work with the same end result.

Whandall, I tried your code and it indeed works. I believe I understand the process.

One downside I see is that the data is not refreshing quick enough. There is a few second delay between when the sensor actually sees a temp increase and when it shows up in the display as changed.

If I do this, am I then limited to 6 clients (6 pipes on the NRF)? I ultimately need 12 clients.
Reading the RF24 doc's and looking at various examples, it is very confusing with regard to Pipes and Addresses. I thought I understood it to be 6 pipes and up to 255 addresses per pipe. So on a single pipe, I could have 255 separate sensors...

It's no problem to send from many clients to a single pipe (with acks).
I would embed a 'source' byte in the packet and let all nodes write to a single pipe.
If you want to have more than one station listen to the same address, you can not use acks on that pipe.

6 pipes can be active at the same time, each listening to one address (5 of the addresses have to share 4 bytes).

I normally use different pipes for different functionalities, but that is a matter of taste.

OK, I am confused again. Am I correct in understanding that the code example you wrote above does indeed use 2 of the 6 pipes to parse the data? You are reading tmp1 over pipe 1 and tmp2 over pipe 2?

And am I correct that since I ultimately need to read 12 separate sensors, I should use 1 pipe and 12 Addresses (like in my original code)... only with some sort of request to the client and a response back to the server for parsing? If so, that is the type of sketch example that I have not been able to locate.

I also searched for the sonic range finder example as suggested, but did not find any coded for wireless communication.

da40flyer:
OK, I am confused again. Am I correct in understanding that the code example you wrote above does indeed use 2 of the 6 pipes to parse the data? You are reading tmp1 over pipe 1 and tmp2 over pipe 2?

Yes. I tried to show how to receive on two different pipes.

da40flyer:
And am I correct that since I ultimately need to read 12 separate sensors, I should use 1 pipe and 12 Addresses (like in my original code)... only with some sort of request to the client and a response back to the server for parsing?

For a simple sensor to master/collector/gateway communication I would use a single address that all sensors send to and only the master listens to.
If you want to control each sensor individually, a personal address for the module is handy, but you could also open one common pipe on all sensors (in this case without ack) and look for something like a 'target' in the packet comming in to all clients in parallel.
You could make the master poll all sensors, it's a matter of taste.

  radio.setChannel(108);

The setting gives a frequency of 2.508 MHz, is this legal in your country?

  radio.setRetries(1, 15);

If you want to use ack-payloads and 250kbps, you will have to raise the first value (ARD). (see table on page 34 of the datasheet)

I was going to ask what units are used for the retry delay number. After looking at pg 34 and the RF24.cpp file, it appears to be multiples of 250usec with zero at 250usec. The argument is of type uint8_t but only the lower four bits are selected via a 0xF mask then shifted into the correct position for the register store. The OP would need a value of 0x05 which yields 1500usec.

Thank you for mentioning this as my project is using a value way too high. I thought it was actual usec from some of the docs I found earlier.

Whandall:

  radio.setChannel(108);

The setting gives a frequency of 2.508 MHz, is this legal in your country?

  radio.setRetries(1, 15);

If you want to use ack-payloads and 250kbps, you will have to raise the first value (ARD). (see table on page 34 of the datasheet)

I removed the radio.setChannel from my code. Was playing with it during initial troubleshooting.

Will change Retries to (5,15) and see what improves.

Does anyone have a simple example of a Master making a request to a Client, and then the Client responding by sending a single byte of data?

da40flyer:
Will change Retries to (5,15) and see what improves.

There will only be a difference (working / not-working) if you start to use acknowledge-payloads.

I remember a time when software was written, not collected. :wink:

Whandall:
I remember a time when software was written, not collected. :wink:

Settle comfortably in the rocking chair with your pipe and glass of single malt. And have you a rug on your knees to keep away the draught. :slight_smile: :slight_smile:

...R

Whandall:
There will only be a difference (working / not-working) if you start to use acknowledge-payloads.

I remember a time when software was written, not collected. :wink:

Someone had to teach you what you have learned... You had to have collected the knowledge from somewhere... I don't believe that one day you just woke up and knew all of this stuff ::slight_smile:

I have spent weeks reading and researching trying to educate myself, in what is a brand new world to me. There is a lot of terminology used here that is second nature to all of you, but I just have not grasped yet.

I am definitely not looking for someone to write my code for me. I want to write it myself. I am just looking for guidance in a way I can understand... and for me, that is by example.

Thank you for the continued help you have provided. I do appreciate it.