Miscommunication between two nrf24l01 modules

I have been testing communication between two nrf24l01 modules for the past days and I have recentely started developing my own code for them, I was inspired by a video from HowToMechatronics.

My setup is the same as DroneBot Workshop's video and I use airspayce's RadioHead library.

My goal with the following code is to create an easy and fast way to switch between transmitter and receiver code.
I also plan on expanding this project so that I can integrate it in the RC transmitter.


The problem is that the server receives corrupted messages (last screenshot) when running the following code, but it works perfectly fine when using the example sketches.
This is the code

controller_reliable:

#define STATE 2 // 1 = server | 2 = client

#include "transmission.h"
#define SERVER_ADDRESS 2

RH_NRF24 driver;
RHReliableDatagram manager(driver, SERVER_ADDRESS);

#if STATE == 1
    // Message to send back to the server
    uint8_t data[] = " ";  
#elif STATE == 2
    // Message sent to the server
    uint8_t data[] = "Hello";
#endif

void setup() {
    Serial.begin(9600);
    if (!manager.init())
        Serial.println("init failed");
}
void loop() {
#if STATE == 1
    manageServer(data, manager);
#elif STATE == 2
    sendToServer(data, manager, SERVER_ADDRESS);   
#endif
    delay(500);
}

transmission.h:

#pragma once

#include <RHReliableDatagram.h>
#include <RH_NRF24.h>
#include <SPI.h>

void sendToServer(byte data[], RHReliableDatagram manager, int SERVER_ADDRESS);

void manageServer(byte data[], RHReliableDatagram manager);

transmission.cpp:

#include "transmission.h"

void sendToServer(byte data[], RHReliableDatagram manager, int SERVER_ADDRESS) 
{
    uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];

    Serial.println("sendToServer (client) function called");
    
    // Send data
    if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS))
    {
        // Now wait for a reply from the Server
        uint8_t len = sizeof(buf);
        uint8_t from;   
        
        // Receve the ack from the Server, and possibly a message | THIS DOES NOT WORK
        if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) 
        {
            Serial.println("The Server sent a message:");
            Serial.println((char*)buf);
        }
        else { /* Timeout, probably the Server was turned off ?*/ }
    }
}
void manageServer(byte data[], RHReliableDatagram manager)
{
    uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
    
    if (manager.available())
  {
        // Wait for a message addressed to us from the client
        uint8_t len = sizeof(buf);
        uint8_t from;
        Serial.println("manageServer (server) function called");
        if (manager.recvfromAck(buf, &len, &from))
        {
            Serial.println("The client sent a message:");
            Serial.println((char*)buf);
            // Send a message to the client
            if(manager.sendtoWait(data, sizeof(data), from)) 
                Serial.println("SendToWait succeded");
            else
                Serial.println("SendToWaitFailed");
        }
    }
}

The problem with this code is that the server picks up a bunch of garbage data

At first I thought this might have been because of my 2.4Ghz wifi or because I had made some mistakes with the wiring, but turning turning off the wifi gives the same result, and the example library sketches work correctly
(nrf24_reliable_datagram_client | nrf24_reliable_datagram_server).

What I think this means is that there is an issue with my code, but I really can't figure out what the problem is, I've tried reading documentation and watching videos, but I don't think I have the skill required for solving this problem.

I use two arduino clones to control the modules: one from Sunfounder and one from AZdelivery, but I don't think this might be the problem.

I just had a quick look here at the examples which you say work:

https://www.airspayce.com/mikem/arduino/RadioHead/nrf24_reliable_datagram_client_8pde-example.html

It has this instruction:

// Dont put this on the stack:
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];

You have this in your code:

void sendToServer(byte data[], RHReliableDatagram manager, int SERVER_ADDRESS)
{
    uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];

    Serial.println("sendToServer (client) function called");
. . .
}

which appears to violate the instruction.

Try making buf[] a global or static variable.

Have a look at this Simple nRF24L01+ Tutorial.

Note that the Tutorial examples use the TMRh20 version of the RF24 library

Wireless problems can be very difficult to debug so get the wireless part working on its own before you start adding any other features.

The examples are as simple as I could make them and they have worked for other Forum members. If you get stuck it will be easier to help with code that I am familiar with. Start by getting the first example to work

There is also a connection test program to check that the Arduino can talk to the nRF24 it is connected to. If the first example does not work be sure to try the connection test for both of your Arduinos. Nothing will work if the connection test fails.

A common problem with nRF24 modules is insufficient 3.3v current from the Arduino 3.3v pin. This seems to be a particular problem with the nano. The high-power nRF24s (with the external antenna) will definitely need an external power supply. At least for testing try powering the nRF24 with a pair of AA alkaline cells (3v) with the battery GND connected to the Arduino GND.

If you are using the high-power nRF24s (with the external antenna) it may help to have a distance of about 3 metres between the two nRF24s so that the signal does not overwhelm the receiver. However someone on the Forum has had them working without that separation - I don't have any personal experience with them. If you are new to nRF24s it may be better to start with a pair of low power modules with the pcb antenna.

...R

6v6gt:
Try making buf[] a global or static variable.

I didn't think of that, does making it static/global take it off the stack?

I thought the only way to take something away from the stack was to pout it in the heap.

Robin2:
A common problem with nRF24 modules is insufficient 3.3v current from the Arduino 3.3v pin. This seems to be a particular problem with the nano. The high-power nRF24s (with the external antenna) will definitely need an external power supply. At least for testing try powering the nRF24 with a pair of AA alkaline cells (3v) with the battery GND connected to the Arduino GND.

If you are using the high-power nRF24s (with the external antenna) it may help to have a distance of about 3 metres between the two nRF24s so that the signal does not overwhelm the receiver. However someone on the Forum has had them working without that separation - I don't have any personal experience with them. If you are new to nRF24s it may be better to start with a pair of low power modules with the pcb antenna.

...R

For this project I used two modules with built in antennas, for the power supply I use two arduino uno clones with the 5v pin, it is not a problem because the nrf24l01s are mounted onto an adapter module with a built-in voltage regulator
(the one shown in the DroneBotWorkshop video).
Right now I can't work on the project, in afwe days I should be able to try your alternative, I'll let you know how it does :slight_smile:

+1 for Robin2's tutorial.

michelecioccarelli:
I didn't think of that, does making it static/global take it off the stack?

I thought the only way to take something away from the stack was to pout it in the heap.

If you just add the static keyword so, it will no longer by on the stack:

  static uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];

This may help if you want to know about the memory layout of a C++ program. Memory Layout of C Programs - GeeksforGeeks
Static data goes into one of the data segments depending on whether it is initialised or not.

As it was, buf[] would have been put on the stack as an automatic variable and destroyed when the containing routine ended.

I have spent considerable time lately working with the RF24Network library (by TMRh20). My project is in larger scale but the communication back and forth between "nodes" is extremely quick and fairly stable. May be worth looking in to.

Try making buf[] a global or static variable.

I've just tried that, now the server receives a wrong message once and then nothing (driver.Available fails)


I have only changed this bit of code:

#if STATE == 1
    // Message to send back to the server
    static uint8_t data[] = "mmh";  
#elif STATE == 2
    // Message sent to the server
    static uint8_t data[] = "Hello";
#endif

and added some more debug messages, I'll try to implement robin's alternative later

txprotolab:
I have spent considerable time lately working with the RF24Network library (by TMRh20).

If the OP only needs communication between two nRF24s there is no need for the network library.

And if simple communication between two nRF24s is not working it certainly won't work with the network library.

...R

Do you think that just implementing your example will be fine even if the project gets more complex, but still only consisting of 2 modules?

Do you think that just implementing your example will be fine even if the project gets more complex, but still only consisting of 2 modules?

If we knew what that means we could respond intelligently.

In any case, using Robin's examples to get your radios to work is a giant step in the right direction. A, you know that they work and B, you know how they work. Expand from a solid base.

Ok, I'll implement that as soon as I can

So I've just had the time to take a look at @Robin2 's tutorial and I got stuck for about one hour because the code from his reply #29 gave a negative result, which meant that there was an hardware problem.

I've just managed to fix the issue by completely changing all of the cables in the project, I don't think that I misplaced any of the old cables, as that was the first thing I always checked: maybe they were just faulty.

This is just an advice to anybody who reads this topic in the future, try this out.

Tomorrow I should have the time to properly follow his tutorial

Be sure to take account of the recent comment I added at the top of the Tutorial about the appropriate version of the RF24 library.

...R

I've just deployed your code and it works perfectly, I will now get to working on porting everything in one file, I will post any problems and the final result, in case anybody needs it

I've been experimenting for a while with @Robin2 's examples, and I've run into what should be the last problem with radio communication; I'm trying to adapt your codebase to my needs, and this is what I've made so far:
I've made 2 main changes: type of the arrays which carry data has been changed from char to uint8_t,
but I've tested this with the code you see below, it is just your examples slightly modified


This is the modified code:
RX:

// SimpleRx - the slave or the receiver

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

#define CE_PIN   9
#define CSN_PIN 10

const byte thisSlaveAddress[5] = {'R','x','A','A','A'};

RF24 radio(CE_PIN, CSN_PIN);

uint8_t dataReceived[10]; // this must match dataToSend in the TX
bool newData = false;

//===========

void setup() {

    Serial.begin(9600);

    Serial.println("SimpleRx Starting");
    radio.begin();
    radio.setDataRate( RF24_250KBPS );
    radio.openReadingPipe(1, thisSlaveAddress);
    radio.startListening();
}

//=============

void loop() {
    getData();
    showData();
}

//==============

void getData() {
    if ( radio.available() ) {
        Serial.println(sizeof(dataReceived));
        radio.read( &dataReceived, sizeof(dataReceived));
        newData = true;
    }
}

void showData() {
    if (newData == true) {
        Serial.print("Data received ");
        for(int i = 0; i < 10; i++) {
            Serial.print(dataReceived[i]);
            Serial.print(" ");
        }
        Serial.println(" ");
        newData = false;
    }
}

TX

// SimpleTx - the master or the transmitter

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


#define CE_PIN   9
#define CSN_PIN 10

const byte slaveAddress[5] = {'R','x','A','A','A'};


RF24 radio(CE_PIN, CSN_PIN); // Create a Radio

uint8_t dataToSend[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
char txNum = '0';


unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 1000; // send once per second


void setup() {

    Serial.begin(9600);

    Serial.println("SimpleTx Starting");

    radio.begin();
    radio.setDataRate( RF24_250KBPS );
    radio.setRetries(3,5); // delay, count
    radio.openWritingPipe(slaveAddress);
}

//====================

void loop() {
    currentMillis = millis();
    if (currentMillis - prevMillis >= txIntervalMillis) {
        send();
        prevMillis = millis();
    }
}

//====================

void send() {

    bool rslt;
    rslt = radio.write( &dataToSend, sizeof(dataToSend) );
        // Always use sizeof() as it gives the size as the number of bytes.
        // For example if dataToSend was an int sizeof() would correctly return 2

    Serial.print("Data Sent ");
    if (rslt) {
        Serial.println("  Acknowledge received");
    }
    else {
        Serial.println("  Tx failed");
    }
}

//================

void updateMessage() {
        // so you can see that new data is being sent
    txNum += 1;
    if (txNum > '9') {
        txNum = '0';
    }
    dataToSend[8] = txNum;
}

RX output:

SimpleRx Starting
10
Data received 1 2 3 4 5 6 7 8 9 10

The other change iI've made is in the .write and .read function I use ARRAY_SIZE instead of sizeof, because it would give out warnings, ARRAY_SIZE #defined as 10, and should function the same way.

warning message:

sketch/transmission.cpp: In function 'void transmit_data(uint8_t*)':
sketch/transmission.cpp:6:53: warning: 'sizeof' on array function parameter 'stuffToSend' will return size of 'uint8_t* {aka unsigned char*}' [-Wsizeof-array-argument]
     if (radio.write(&stuffToSend, sizeof(stuffToSend))) // ARRAY_SIZE
                                                     ^
sketch/transmission.cpp:4:40: note: declared here
 void transmit_data(uint8_t stuffToSend[])

sandbox.ino

#include "transmission.h"

#define TX 1
#define RX 2

#define STATE RX

#define CE_PIN   9
#define CSN_PIN 10
#define ARRAY_SIZE 10

RF24 radio(CE_PIN, CSN_PIN);

const byte thisSlaveAddress[5] = {'R','x','A','A','A'};

#if STATE == TX
    uint8_t stuffToSend[ARRAY_SIZE] = {};
#elif STATE == RX
    uint8_t dataReceived[ARRAY_SIZE] = {};
#endif
    
void setup() 
{
    Serial.begin(9600);
    radio.begin();
    radio.setDataRate( RF24_250KBPS );

    #if STATE == TX
        radio.setRetries(3,5);
        radio.openWritingPipe(thisSlaveAddress); 

    #elif STATE == RX
        radio.openReadingPipe(1, thisSlaveAddress);
        radio.startListening();
    #endif
}

void loop() 
{
    #if STATE == TX
        Serial.println("TX");
        transmit_data(stuffToSend);
        
    #elif STATE == RX
        receive_data(dataReceived);    
    #endif
    delay(1000);
}

transmission.h

#pragma once
#define ARRAY_SIZE 10

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

extern RF24 radio;
extern const byte thisSlaveAddress[5];

void transmit_data(uint8_t stuffToSend[]);

void receive_data(uint8_t dataReceived[]);

void print_array(const uint8_t array[]);

transmission.cpp

#include "transmission.h"

void print_array(const uint8_t array[])
{
    for (uint8_t i = 0; i < ARRAY_SIZE; i++) {
        Serial.print(array[i]);
        Serial.print(" ");
    }
    Serial.println(" ");
}


void transmit_data(uint8_t stuffToSend[])
{
    if (radio.write(&stuffToSend, ARRAY_SIZE))
    {   
    print_array(stuffToSend);
        Serial.println("Success");
    } else {
        Serial.println("Fail");
    }
}

void receive_data(uint8_t dataReceived[])
{   
    if ( radio.available()) 
    {
        Serial.println("Success");
        radio.read(&dataReceived, ARRAY_SIZE);
        print_array(dataReceived);
        
    } else {
        Serial.println("Fail");
    }
}

this is the output from the RX

Fail
Fail
Success
0 0 0 0 0 0 0 0 9 1

TX output

TX
0 0 0 0 0 0 0 0 0 0  
Success

If I try initializing the array:

    uint8_t stuffToSend[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

RX output

Fail
Fail
Success
1 2 3 4 65 0 0 0 0 79

TX output

TX
1 2 3 4 5 6 7 8 9 10  
Success

The code is split in two using preprocessor directives: if the state is TX, everything about the transmitter code is visible, while the receiver code is invisible.
Everything in the setup() is copied and pasted from @Robin2 's examples.
In the loop I call either transmit_data() or receive_data() depending on STATE.
transmit_data doesn't seem to be the problem.

I think that the problem may be with either the arrays being passed by reference (even though I don't think it makes a difference) instead of being global, or their typing.

I've been thinking about this for a few days but I really can't get around this, I hope someone can help, thanks for your time

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.