-
Notifications
You must be signed in to change notification settings - Fork 164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NRF24 Network: network.available() returns true in every loop iteration when sending frequent messages from the master node #242
Comments
I am unable to recreate any issue using your code. |
Let me create a simple example to share here. |
I think I found the problem. Nothing wrong in the library but if the network update is not called often enough then this happens. Can you tell me how often, the frequency, should this update method be called? |
Typically, you want to call Keep in mind, the radios will buffer up to 3 payloads, while RF24Network will buffer up to 144 bytes of data by default. The radio will also keep receiving data while the microcontroller is doing other things, as long as its in RX mode. |
But even if this is called at a slower rate the network.available should not return true if there is no payload for this node. If due to any reason example some heavy calculation or some other process delays the network update the network.available should not break. |
Nope, I would suggest a potential issue with your code if that is happening. Often, if you have buffer overwrites or other memory related issues, it tends to invoke strange behavior that cannot be easily explained. If you can post some simplified code that demonstrates this behavior, I can take a closer look. |
yeah sure I will share. |
So here is the node side code #include <Arduino.h>
#include <RF24.h>
#include <RF24Network.h>
const uint8_t STATUS_LED = PC13; ///< Pin for nRF24 CSN
HardwareSerial Serial1(USART1);
const uint8_t NRF_CE_PIN = PB0; ///< Pin for nRF24 CE
const uint8_t NRF_CSN_PIN = PB1; ///< Pin for nRF24 CSN
unsigned long lastDataSendTime = 0; ///< Last time data was sent to the master node
RF24 radio(NRF_CE_PIN, NRF_CSN_PIN);
RF24Network network(radio);
void sendData(const uint8_t* buffer, size_t bufferSize) {
RF24NetworkHeader header(00, 'D'); // 'D' is the message type
Serial.print("Size of buffer: ");
Serial.println(bufferSize);
// Write the buffer to the network
bool success = network.write(header, buffer, bufferSize);
if (success) {
Serial.println("NRF24 Network: Data sent to master node.");
} else {
Serial.println("NRF24 Network: Failed to send data.");
}
}
bool receiveCommand(void) {
network.update();
while (network.available()) {
RF24NetworkHeader header;
uint16_t payloadSize = network.peek(header); // Use peek() to get the size of the payload
uint8_t buffer[payloadSize]; // Adjusted to the size of the expected command payload (6 bytes)
// Read the data into the temporary buffer
network.read(header, buffer, payloadSize);
// Check if the message type matches the command type 'C'
switch (header.type) {
case 'C': // 'C' is the command message type
Serial.println("NRF24 Network: Command received from master node.");
return true;
case 'S':
Serial.println("NRF24 Network: Settings received from master node.");
break;
}
}
return false;
}
void setup() {
Serial.begin(57600);
Serial.setTimeout(1000);
pinMode(STATUS_LED, OUTPUT);
delay(2000);
if (radio.begin()) {
network.begin(90, 01); // 90 is the RF24 channel
radio.setPALevel(RF24_PA_HIGH); // Adjust power level as needed
radio.setDataRate(RF24_1MBPS); // 1Mbps for better range
Serial.println("NRF24 Network: Initialized successfully.");
} else {
Serial.println("NRF24 Network: Failed to initialize radio hardware. Check connections.");
}
}
void loop() {
if (millis() - lastDataSendTime >= 5000) {
lastDataSendTime = millis();
uint8_t buffer[18]; // Increase the buffer size to 17 to accommodate roomTemperature
buffer[0] = 1;
uint64_t some_data = 0x1234567812345678;
memcpy(&buffer[1], &some_data, sizeof(some_data));
buffer[9] = 12;
buffer[10] = 34;
buffer[11] = 2;
buffer[12] = 2;
buffer[13] = 2;
buffer[14] = 2;
buffer[15] = 22;
buffer[16] = 22;
buffer[17] = 2;
sendData(buffer, sizeof(buffer));
}
receiveCommand();
// delay(500); // after adding this delay the network.available keeps returning true even if the master note is completely powered off
} The following is the orange pi side master node python code import time
from logger_config import logger
from nrf24_handler import NRF24Handler # Import NRF24Handler
import json
def main():
# Uncomment and configure NRF24Handler when required
nrf_handler = NRF24Handler(
this_node=0,
channel=90,
ce_pin=8,
csn_pin=10
)
transmit_interval = 0.1
last_publish_time = time.time()
try:
while True:
current_time = time.time()
if current_time - last_publish_time >= transmit_interval:
last_publish_time = current_time
success = nrf_handler.transmit()
# Uncomment when NRF24Handler is enabled
nrf_data = nrf_handler.receive()
time.sleep(0.01)
except KeyboardInterrupt:
logger.info("User cancelled program, stopping processes...")
nrf_handler.cleanup()
logger.info("Shutting down...")
if __name__ == "__main__":
# Run the main application logic
main() nrf24_handler.py import threading
import struct
import json
import logging
from pyrf24 import RF24, RF24Network, RF24NetworkHeader, RF24_DRIVER
from logger_config import logger
class NRF24Handler:
def __init__(self, this_node, channel=90, ce_pin=8, csn_pin=10):
self.radio = RF24(ce_pin, csn_pin)
self.network = RF24Network(self.radio)
self.this_node = this_node
self.channel = channel
self.lock = threading.Lock() # Add a threading lock
# Initialize the radio and network
if not self.radio.begin():
raise OSError("nRF24L01 hardware isn't responding")
self.radio.channel = self.channel
self.network.begin(self.this_node)
self.radio.print_pretty_details()
def receive(self):
"""
Check for incoming messages and process them based on node type.
"""
with self.lock: # Ensure thread-safe access
self.network.update()
while self.network.available():
header, payload = self.network.read(32) # Adjust maxlen as needed
node_network_address = header.from_node
# Parse the first byte to identify the node type
node_type = payload[0] # The first byte indicates the node type
if header.type == ord('D'): # 'D' is the message type
if node_type == 1: # Thermostat node
if len(payload) == 18:
data1, data2, data3, data4, data5, data6, data7, data8, data9 = struct.unpack(
"<QHBBBBBBB", payload[1:] # Skip the first byte (node_type)
)
else:
logger.error(f"Error: Payload size is {len(payload)} bytes. Expected 17 bytes. ")
return {
"node_type": data1,
}
elif node_type == 2: # 3-in-1 Sensor node
data1, data2, data3, data4 = struct.unpack(
"<QHHH", payload[1:] # Skip the first byte (node_type)
)
return {
"node_type": data1,
}
else:
# Unknown node type
return {
"node_type": "unknown",
"raw_payload": payload,
}
return None # No data received
def transmit(self):
with self.lock: # Ensure thread-safe access
try:
data1 = 1
data2 = 2
data3 = 2
data4 = 22
# Prepare the payload
payload = struct.pack("<BBBB", data1, data2, data3, data4)
# Create the header for the target node
header = RF24NetworkHeader(1, ord('C')) # 'C' message type
# Send the payload
ok = self.network.write(header, payload)
if ok:
logger.info(f"Message sent to node {1}.")
else:
logger.error(f"Failed to send message to node {1}.")
return ok
except Exception as e:
logger.error(f"Error in transmit function: {e}")
return False
def cleanup(self):
"""
Power down the radio safely.
"""
self.radio.power = False logger_config.pyimport logging
from logging.handlers import RotatingFileHandler
from collections import deque
# In-memory log handler
class InMemoryLogHandler(logging.Handler):
def __init__(self, max_logs=200):
super().__init__()
self.log_buffer = deque(maxlen=max_logs)
def emit(self, record):
# Format the log entry
log_entry = self.format(record).rstrip("\n") # Apply the formatter here
self.log_buffer.append(log_entry)
def get_logs(self):
return list(self.log_buffer)
# In-memory log stream
in_memory_handler = InMemoryLogHandler(max_logs=200)
# Log file configuration
log_file = "gateway.log"
max_log_size = 10 * 1024 * 1024 # 10 MB
backup_count = 3
# Configure logging
logger = logging.getLogger("gateway_logger")
logger.setLevel(logging.INFO)
# Handlers
console_handler = logging.StreamHandler() # Terminal
console_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
file_handler = RotatingFileHandler(log_file, maxBytes=max_log_size, backupCount=backup_count)
file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
# Apply the same formatter to InMemoryLogHandler
log_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
in_memory_handler.setFormatter(log_formatter)
# Add handlers to logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.addHandler(in_memory_handler) Maybe you can run either on raspberry pi or orange pi zero 3 lts (if you have) but I think in master there shouldn't be any issue |
I still can't recreate using your Arduino example and can't run your python example for now.
Send a 's' over serial to stop sending and 'g' to resume. #include <SPI.h>
#include <RF24.h>
#include <RF24Network.h>
RF24 radio(7, 8); // nRF24L01(+) radio attached using Getting Started board
RF24Network network(radio); // Network uses that radio
const uint16_t this_node = 00; // Address of our node in Octal format
const uint16_t other_node = 01; // Address of the other node in Octal format
const unsigned long interval = 0; // How often (in ms) to send 'hello world' to the other unit
unsigned long last_sent; // When did we last send?
unsigned long packets_sent; // How many have we sent already
struct payload_t { // Structure of our payload
unsigned long ms;
unsigned long counter;
};
void setup(void) {
Serial.begin(115200);
while (!Serial) {
// some boards need this because of native USB capability
}
Serial.println(F("RF24Network/examples/helloworld_tx/"));
if (!radio.begin()) {
Serial.println(F("Radio hardware not responding!"));
while (1) {
// hold in infinite loop
}
}
radio.setChannel(90);
network.begin(/*node address*/ this_node);
}
bool sending = 1;
void loop() {
if(Serial.available()){
char c = Serial.read();
if(c == 'g'){
sending = 1;
}else
if(c == 's'){
sending = 0;
}
}
network.update(); // Check the network regularly
unsigned long now = millis();
// If it's time to send a message, send it!
if (now - last_sent >= interval) {
last_sent = now;
if(sending){
Serial.print(F("Sending... "));
payload_t payload = { millis(), packets_sent++ };
RF24NetworkHeader header(/*to node*/ other_node);
bool ok = network.write(header, &payload, sizeof(payload));
Serial.println(ok ? F("ok.") : F("failed."));
}
}
while (network.available()) { // Is there anything ready for us?
RF24NetworkHeader header; // If so, grab it and print it out
network.read(header, 0, 0);
Serial.println(F("Received packet: counter="));
}
}
and in the receiver example I modified mainly the below: switch (header.type) {
case 'C': // 'C' is the command message type
Serial.println("NRF24 Network: Command received from master node.");
return true;
case 'S':
Serial.println("NRF24 Network: Settings received from master node.");
break;
default: Serial.println("xD"); break;
} |
Maybe we should call in the expert: @2bndy5 any thoughts? |
switch (header.type) {
case 'C': // 'C' is the command message type
Serial.println("NRF24 Network: Command received from master node.");
return true;
case 'S':
Serial.println("Settings received!");
break;
} When you hit this while It's been a while since I went over the internal pointer that manages the network "queue" for non-linux platforms. The user code posted here seems right at a glance. |
Yeah it goes to the case 'C' |
I will do this |
Works fine with an Arduino uno as master |
Can you confirm that powering off the Orange Pi AND disconnecting the radio from the Orange Pi results in the same behavior with your original code? |
working on it |
I suspected a hardware problem from the start, but it is strange to see it in the network layer. Usually hardware errors annoyingly surface when using the core layer (RF24 lib). Have we checked behavior in that layer first? The payloads aren't big enough to need fragmentation, so recreating this with only RF24 layer shouldn't be too hard. If it is not a hardware error, then some kind of memory leak might be preventing @mirhamza708 You could try reusing a globally/statically allocated buffer instead of declaring the buffer every time you need one. Or use a data structure like we do in some RF24 examples (see RF24/examples/multiceiverDemo.ino)... |
@mirhamza708 Any luck or progress on this issue? |
Shit man I was busy couldn't get my hands on it. Let me update you in a while. |
So I completely removed the nrf24 from master and the node is still receiving messages this is the code of the node
|
I was able to recreate this behavior between Arduino Due and Arduino Nano with the following as TX:
Houston, we've had a problem... This is going to take some debugging to fix. |
https://github.com/nRF24/RF24Network/blob/master/RF24Network.cpp#L518 I think this should be Are you able to test this change out @mirhamza708 ? |
Yeah just checked it, resolves the issue, but need to test more. |
Somehow after sending these messages the master node keeps failing. Restarting the app resolves the issue. |
Works fine with my setup. |
This is not after the change this was there even before the change. |
Lol more issues? Can you include something like |
I don't know this always returns true is this correct python implementation? |
Python looks right. You will find more details of in the pyrf24 API docs. |
This looks to me like the receiver's RX FIFO is getting full and it takes the time of 2 failed transmissions to free up it's RX FIFO. |
That is odd. Maybe try just I would like to somehow isolate the problem to either the Orange Pi or the STM32. It works for me, so not quite sure how to address this problem yet. |
This always returned 1 for failureDetected. The self.radio.getDataRate() reads 1MBPS.
|
there was a mistake in this from my side, it works now. The only issue is that the |
How about using something like the following, again though in python:
Wondering if you really are getting failures, or if there is something wrong in the code. Will this print "Failure Detected" in rapid succession, or will it be intermittent or go away entirely? |
Setting the
|
Hmm, I think now this is just because the failureDetected var is not initialized in the library at runtime. Something we should correct. It uses a simple timer to detect timeouts in SPI communication. So if the radio is unresponsive via SPI, the failureDetected will return true. Sometimes SPI does give data, but it is garbage data, so I typically check in internal variable like the dataRate as well, in attempts to correct all SPI/radio errors. |
I would like to correct/contribute to it but I don't understand how the python code is written I see cpp files in the pyrf24 repo all around so I guess I will have to wait for your update. Thanks guys 👍 |
Basically, to fix temporarily you can initialize failureDetected to 0 like you are doing. It should still work afterwards. I will go ahead an open an issue on your behalf to fix it. Thanks alot for identifying these issues! It helps to have really good info and communication when trying to figure out what exactly is going on. |
Issue identified by @mirhamza708 * Fix buffering of user payloads: Per #242 fix buffer overrun * Mods to #243 per @2bndy5
Issue identified by @mirhamza708 * Fix buffering of user payloads: Per #242 fix buffer overrun * Mods to #243 per @2bndy5
I'm experiencing an issue where the network.available() function in the receiveCommand method returns true in each loop iteration, even when the master node is not sending any data. This issue occurs only when a large number of messages are sent quickly from the master node (with intervals reduced to around 100ms-200ms). When a delay of approximately 500ms is introduced between messages, the issue does not occur, and the function behaves as expected.
Steps to Reproduce:
Expected Behavior: network.available() should return true only when new data is available for reading, not in every iteration of the loop.
Actual Behavior: When a lot of messages are sent rapidly from the master node, network.available() returns true in every iteration of the loop, even when no new data has been received.
nrf24Handler class methods
my main.cpp
The text was updated successfully, but these errors were encountered: