RF24network Write/Read Buffer?

This is more of a "why is that" question as I have figured out a solution, right one or not it worked. And more for my own education.

I am making a Bee Hive monitoring system using NRF24L01 modules to send the data from the hive to a base unit. I had it working with one hive module using just the radio functions. I now am putting that module on the RF24network so I can have multiple hive units sending to the common base.

Originally I had the hive writing to the network every 2 seconds and the base reading every 5 seconds, I wanted to have updated data for each read. The base would read the data but after about 10 reads it wouldn't update, just the same readings each time.

So I changed the hive to also write every 5 seconds. That seemed to fix the problem except after about 3 minutes it started to fall behind. I set up a incremental counter at each write. Monitoring both the hive and the base I could see the base falling behind a couple of counts, hive sending 30, base reading 28.

Next I changed the read time in the base to 4.8 seconds, hive still writing at 5. This works fine. It's been running for over an hour and the numbers are in sync.

My assumption is there's a buffer, cache, bit bucket, that is not being cleared. My thought is that it's in the hive side. The hive node writes to this buffer and when the base node reads it that clears it. Or the buffer is in the base. Is that a valid assumption? Am I close?

Thanks
John

I would have thought that you would need the receiver (base) reading all the time and leave it to the transmitter to control intervals. Post your code for better help.

That's the part I don't understand and am trying to learn more as to how these function. It obviously is some kind of timing function. Here's my code. The first one is the hive node. My base has 3 tabs, the main sketch is "Hive_base_7", the is a display control tab, "display_cntrl" and the tab that reads the network, "readHive" I couldn't post the complete code as it exceeded the max length to I've just put the function that reads the network

Thanks

Hive node

// Hive 1 transmitter, reads BME280 for temp and pressure and battery voltage
// added RF24nework

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24Network.h>
#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C        
RF24 radio(7, 8); // 10, 9 CE, CSN
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 01;   // Address of this node in Octal format ( 04,031, etc)
const uint16_t base_node = 00;   // Address of base node

float c_Temp = 0; // temp in C' from BMP280
int batt_In = A0; // Battery circuit divider input
float batt_V = 0; // battery voltage
float batt_Map = 0;
long int start_delay = 0; // start of delay time
long int write_time = 5000; // time between writes to base

struct dataStruct {
 float f_Temp;
 float H1_Prss;
 float H1_Humd;
 float H1_Batt;
}myDataStructure;

void setup() {
  Serial.begin(9600);
  Serial.println(F("Bee Hive 1"));

// Initialise NRF24L01
  radio.begin();
  radio.setDataRate(RF24_1MBPS);
  radio.setChannel(124);
  radio.setPALevel(RF24_PA_MIN);
   // Open a writing and reading pipe on each radio, with opposite addresses
//  radio.openWritingPipe(this_node);
//  radio.openReadingPipe(1, base_node);
 
  radio.stopListening();

  network.begin(124,this_node);  //(channel, node address)


   if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  } // end if (!bmp.begin())

  start_delay = millis();

} // end void setup

void loop() {

    network.update();

 /*   if(!network.available()){
        Serial.println("N0 network");
   }
*/    
    // Read BME280
    c_Temp = bme.readTemperature(); // read temp from BME280
    myDataStructure.f_Temp = (c_Temp * 9/5) + 32; // calculate F'  
    myDataStructure.H1_Prss = (bme.readPressure() / 3389.39);
    myDataStructure.H1_Humd = bme.readHumidity();
  
    batt_V = analogRead(batt_In); // read battery voltage
   // batt_Map = (map(batt_V, 0, 1025, 0, 500));
    myDataStructure.H1_Batt = batt_Map / 100;
  
    if(millis() > (start_delay + write_time)){
    RF24NetworkHeader header(base_node);     // (Address where the data is going)
    network.write(header, &myDataStructure, sizeof(myDataStructure)); // send string to base

    batt_Map ++;
    Serial.print("Temperature = ");
    Serial.print(myDataStructure.f_Temp);
    Serial.println("*F");
  
    Serial.print("Pressure = ");
    Serial.print(myDataStructure.H1_Prss);
    Serial.println(" inHg");
  
    Serial.print("Humidity = ");
    Serial.print(myDataStructure.H1_Humd);
    Serial.println("%");

    Serial.print("Battery = ");
    Serial.print(myDataStructure.H1_Batt);
    Serial.println(" V");
  
    Serial.println();
    
    start_delay = millis();
    } // end if(millis() >
    
 } // end void loop

Read Hive Node

void readHive()

{

    if(network.available()){
      network.read(header, &myDataStructure,sizeof(myDataStructure));
    
      showParsedData(); // Send data to serial monitor
     
   } // end if(network.available()) 

/*   if(!network.available()){
        Serial.println("N0 network");
   }
*/   
} // end void readHive

void showParsedData() { // print to serial monitor
  
  // Write the results to the serial port
Serial.print(myDataStructure.f_Temp); Serial.print(",");
Serial.print(myDataStructure.H1_Prss); Serial.print(",");
Serial.print(myDataStructure.H1_Humd); Serial.print(",");
Serial.println(myDataStructure.H1_Batt);

// convert temp reading to string for display printing
   dtostrf(myDataStructure.f_Temp, 5, 2, temp_str);
   strcat(temp_str, " 'F"); // add units to end of string

// convert Humidity reading to string for display printing
   dtostrf(myDataStructure.H1_Humd, 5, 2, humd_str);
   strcat(humd_str, " %RH"); // add units to end of string

// convert Pressure reading to string for display printing
   dtostrf(myDataStructure.H1_Prss, 5, 2, prss_str);
   strcat(prss_str, " InHg"); // add units to end of string

// convert Battery reading to string for display printing
   dtostrf(myDataStructure.H1_Batt, 5, 2, batt_str);
   strcat(batt_str, " V"); // add units to end of string

} // end showparsedData

If all the hives are within wireless range of the base it would not be necessary to use the Network. They could all talk directly to the base. Or, better, have the base call each hive in turn when it is time to get data from it.

...R
Simple nRF24L01+ Tutorial

Robin2, lots of info in the tutorial, I will dig into it more later, have been trying to find as much as I can about these. As far as your comment, my understanding is that each NRF24L can only talk directly to 5 children modules, I thought if I had more than 5 I would need to use the network. Can I use the address structure without the RF24network library?

Thanks

AFAIK, the nRF24 module can talk to 1000's of nodes! You just need to tell it which node to talk to. The 'limitation', if you see it like that, is that each nRF24 module has 6 receive pipes.

You could go with Robin's suggestion of the base talking to each node individually. Each node could, when prompted by the base, respond with whatever readings it wants to pass. This is a more controlled setup, a command-response system, where nobody talks unless they are spoken to by the base. Each of your nodes would transmit to the same address on the base unit, so using only 1 receive pipe regardless of how many nodes you have. You could also pre-load a message so it would automatically be transmitted back to the base unit as part of the Ack.

Another option is for each node to talk to the base on an ad-hoc basis as and when it has something to say. This scenario can result in messages not being received by the base if 2 nodes decide to transmit at the same time. You can get around this to a certain extent by using setRetries() and having slightly different delays for each node. You would still only use one receive pipe on the base unit.

Hope that helps.

Also, if you are transmitting temperature, humidity, pressure and battery level, then I would not expect the readings to change much. If each node was to transmit this data back to the base unit once every 10 minutes or so, then the chances of a packet collision are pretty slim.

As you are measuring battery level, I guess you would want a fairly frugal power consumption on each of the bee hive nodes. If you let the nodes talk to the base unit when they want to, then you can put the node to sleep for 10 minutes, only waking up to transmit the data and then going back to sleep again.

I forget where I read it, but there's a setup out there on the web that runs for a year on batteries using this sort of approach.

Your base unit could be powered all the time just sitting there waiting for the nodes to talk to it.

M.

markd833, The plan is to have each hive node battery powered, then sleep for 10 minutes, using a RTC to an interrupt pin to wake it up. As you say nothing will change drastically within the 10 minutes. I have it running a bit faster during development as I don't want to wait 10 minutes to see if it's working. But by using the command-response system I could send it a command while it's a sleep so I would get no response. I think I would have to keep the base constantly reading the network so as soon as one of the nodes wakes up and sends something it would be capture.

All of the comments are very helpful, much appreciated and give me lots to look into but do not answer the original question. Why do I get better results by time matching when the nodes? I will try later to just have the base consistently scanning at a faster time than the nodes send.

Thanks again
John

markd833:
Each node could, when prompted by the base, respond with whatever readings it wants to pass.

Using the ackPayload feature (as in the 2nd example in my Tutorial) it is very easy to get data from a slave with no risk of a data collision.

...R

Stumpy_L:
But by using the command-response system I could send it a command while it's a sleep so I would get no response.

That is certainly a risk for which you have to create a solution. One solution is for the base unit to poll all the hives on a regular basis. Then it is only necessary to ensure that a hive is awake long enough to receive one (or better, two) of the messages from the base. For example if the base sends a request every second it would only be necessary for a hive to be awake for (say) 3 seconds to be sure of receiving at least one request. Three seconds in 10 minutes is only 0.5% of the time if my maths is correct.

On the other hand if you have the base listening all the time and the hives send messages at intervals there is a risk of two transmissions overlapping and both being garbage - but if you use an RTC and stagger the 10 minute intervals that risk should be very low. If a message is lost just wait a random time before re-trying.

...R

If you go with a solution where the base listens all the time, then the nodes will be active for a very short period of time which will extend their battery life.

If you have slightly different delays between retries for each node, then if you do get a collision, then the next retry will more than likely succeed. The whole retry mechanism happens behind the scenes and is part of the nRF24L01 packet handling process so your node would be unaware that a collision had occurred unless all the retries failed.

There's a good explanation on the lastminuteengineers.com website.

To answer your original question, it sounds like a receive buffer has filled up somewhere. When you read your data out after 5 seconds, are you doing multiple reads to make sure you've extracted all the data from the receive buffer. If not, then your buffer will eventually fill up.

I think your clue here is where you said:

Next I changed the read time in the base to 4.8 seconds, hive still writing at 5. This works fine. It's been running for over an hour and the numbers are in sync.

If you check the receive buffer more often than it's written to, then the buffer shouldn't fill up.

If your base unit has nothing else to do but sit and wait for data from each bee hive node, then it can be polling the receive buffer hundreds of times a second just on the off chance that a message has been received. This is what you see in the demo programs in the loop() section of the code.

markd833:
so your node would be unaware that a collision had occurred unless all the retries failed.

A program should allow for that possibility if the loss of data would be serious.

I think my preference would be to set a small number of retries and if a transmission failure is reported have the program wait a random period (perhaps between 0.5 and 2 seconds) before resending the message. That way it is unlikely that the two colliding Arduinos will try to resend at the same moment.

...R

Just as a quick test I took the time delay out of the base. I already had if(network.available()), in the read hive logic. So it keeps scanning until there is something to read, seems to work fine but there is only one node writing to it now. I will read more about the ackPayload feature you mentioned, best to do a bit of handshaking.

Thanks so much for all the comments and discussion has given me plenty to work with.
John

Robin2, I loaded up the ackPayload sketches, I have the CE and CSN pins on different pins than you so I changed the definitions to mine, I also added the pinmode statement, pinMode(10, OUTPUT); On the transmitter monitor I get " Data sent message (0 - 9) Acknowledge but no data" Would this because I don't have the TMrh20 version of the RF24 library?

I did try putting the CE and CSN pins as you defined them and removed the pinmode statement. I then got "TxFail" after that I put them back to mine and now also got "Txfail". I loaded my hive sketches in and that worked as before. Reloaded the ackPayload sketches with my CE and CSN definitions and was now back to "Acknowledge but no data" Why all that worked that way makes no sense to me but I don't that it has anything to do with your sketches or how the pins are defined, I'll chalk it up to another mystery of modern electronics.

Thanks
John

Just in case you are not aware of it, the nRF24 doesn't reset each time your Arduino is auto reset when you load a sketch. It sounds like the nRF24 registers have retained settings from a previous sketch. Try cycling the power to get the whole setup back to a known state.

I'll try that but I do power it off when I swap the CE and CSN pins. That may help with the Tx Fail problem but not with the "Acknowledge but no data" result

Thanks

Stumpy_L:
On the transmitter monitor I get " Data sent message (0 - 9) Acknowledge but no data" Would this because I don't have the TMrh20 version of the RF24 library?

[....]

I did try putting the CE and CSN pins as you defined them and removed the pinmode statement. I then got "TxFail" after that I put them back to mine and now also got "Txfail".

[....]

Your questions convey to me that you don't really appreciate how difficult it can be to debug wireless problems.

I don't know if the problem is due to the wrong library. All I know is that my code works with the TMRh20 version. If you want to try another library I won't be able to help.

If you changed the pin numbers in the program without changing the connections failure is inevitable.

Set your system up EXACTLY as in my Tutorial and try the first pair of programs and if they don't work post a sample of the output from each. Also be sure to read every detail of my Tutorial carefully in case there is some small point that is relevant.

...R

I do realize how difficult it is to debug wireless or any complex system, that is why I am asking on this forum and greatly appreciate your help.

I did change both the pin definitions in the sketch and the physical connections, I do understand that much.

I will try the first set of sketches as you have them but that wont be until later tonight. I will let you know the results

Thanks again
John