nRF24 / RF24

OSI Layer 2 driver for nRF24L01 on Arduino & Raspberry Pi/Linux Devices
https://nrf24.github.io/RF24
GNU General Public License v2.0
2.24k stars 1.02k forks source link

Arduino MKRZero receives wrong values from Pi4 (while Pi4 receives the correct values when they are trasmitted from the MKRZero) #763

Closed dkts2000 closed 3 years ago

dkts2000 commented 3 years ago

I started using the gettingstarted example with the Pi4 as the receiver and the MKRZero as the trasmitter. After sending and receiving successfully ints and floats alone i successfully sent and received the following struct:

struct mypayload
{
        float FloatData1;
        float FloatData2; 
        uint32_t Duration1;
        uint32_t Duration2;
        uint16_t Id;  
        uint16_t Data1;
        uint16_t Data2;
        uint16_t Data3;
        uint16_t Data4;
        uint16_t Data5;
        uint32_t Data6;       
};

I tried it with various random values and i always received the correct values (with the ack enabled).

Then i tried the opposite. Pi4 trasmits the same struct to Arduino MKRZero. I always receive the message but some values are wrong. Examples: if everything is 0 and the FloatData1 is 1.50 i receive all zeros and FloatData1=1.75; Always. All zeros and the id is between 0 to 259 i receive it right. if it is 260, i receive 262. And from 260 and over i receive a bigger value. Also the uint32_t variables. If everything else is zero and i trasmit a value eg. 75000 i receive ~76200 (more or less. this is not constant. but its a multiplier of ~1.016 if i remember right )

Does anyone have an idea what might be wrong? I should mention that i use the simple NRF24L01+ (not the one with the antenna) and a Socket Adapter Module Board For 8 Pin NRF24L01+ Wireless Transceiver.

TMRh20 commented 3 years ago

Are you using the SPIDEV driver on the RPi4? Are you able to test with A different device other than mkrzero? I would be tempted to think it’s something with your code, did you just modify gettingstarted to send the struct? Can u post your code?

On Apr 19, 2021, at 8:19 AM, dkts2000 @.***> wrote:

 I started using the gettingstarted example with the Pi4 as the receiver and the MKRZero as the trasmitter. After sending and receiving successfully ints and floats alone i successfully sent and received the following struct:

struct mypayload { float FloatData1; float FloatData2; uint32_t Duration1; uint32_t Duration2; uint16_t Id; uint16_t Data1; uint16_t Data2; uint16_t Data3; uint16_t Data4; uint16_t Data5; uint32_t Data6; };

I tried it with various random values and i always received the correct values (with the ack enabled).

Then i tried the opposite. Pi4 trasmits the same struct to Arduino MKRZero. I always receive the message but some values are wrong. Examples: if everything is 0 and the FloatData1 is 1.50 i receive all zeros and FloatData1=1.75; Always. All zeros and the id is between 0 to 259 i receive it right. if it is 260, i receive 262. And from 260 and over i receive a bigger value. Also the uint32_t variables. If everything else is zero and i trasmit a value eg. 75000 i receive ~76200 (more or less. this is not constant. but its a multiplier of ~1.016 if i remember right )

Does anyone have an idea what might be wrong? I should mention that i use the simple NRF24L01+ (not the one with the antenna) and a Socket Adapter Module Board For 8 Pin NRF24L01+ Wireless Transceiver.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe.

2bndy5 commented 3 years ago

This might be related to how much memory is allocated for different datatypes on the MKRZero. The MKRZero uses the ATSAMD21 and my experience told me to use unsigned long for uint8_t on my ATSAMD21 based boards; this is why the multiceiverDemo example uses unsigned long to transmit 2 numbers that could easily be uint8_t.

Try printing to serial the sizeof() some variable and you should see how many bytes are allocated to it. I noticed that a uint8_t occupied 4 bytes on my ATSAMD21 boards, but it only occupied 1 byte on the RPi.

dkts2000 commented 3 years ago

Yes SPIDEV. This is the output of Details on Pi4 (on the MKRZero i can't make it serial print):

================ SPI Configuration ================
CSN Pin     = /dev/spidev0.0
CE Pin      = Custom GPIO25
SPI Speedz  = 10 Mhz
================ NRF Configuration ================
STATUS      = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1    = 0x65646f4e32 0x65646f4e31
RX_ADDR_P2-5    = 0xc3 0xc4 0xc5 0xc6
TX_ADDR     = 0x65646f4e32
RX_PW_P0-6  = 0x20 0x20 0x20 0x20 0x20 0x20
EN_AA       = 0x3f
EN_RXADDR   = 0x03
RF_CH       = 0x64
RF_SETUP    = 0x23
CONFIG      = 0x0e
DYNPD/FEATURE   = 0x00 0x00
Data Rate   = 250 KBPS
Model       = nRF24L01+
CRC Length  = 16 bits
PA Power    = PA_LOW
ARC     = 0
================ SPI Configuration ================
CSN Pin         = /dev/spidev0.0
CE Pin          = Custom GPIO25
SPI Frequency       = 10 Mhz
================ NRF Configuration ================
Channel         = 100 (~ 2500 MHz)
RF Data Rate        = 250 KBPS
RF Power Amplifier  = PA_LOW
RF Low Noise Amplifier  = Enabled
CRC Length      = 16 bits
Address Length      = 5 bytes
Static Payload Length   = 32 bytes
Auto Retry Delay    = 1500 microseconds
Auto Retry Attempts = 15 maximum
Packets lost on
    current channel = 0
Retry attempts made for
    last transmission   = 0
Multicast       = Disabled
Custom ACK Payload  = Disabled
Dynamic Payloads    = Disabled
Auto Acknowledgment = Enabled
Primary Mode        = TX
TX address      = 0x65646f4e32
pipe 0 ( open ) bound   = 0x65646f4e32
pipe 1 ( open ) bound   = 0x65646f4e31
pipe 2 (closed) bound   = 0xc3
pipe 3 (closed) bound   = 0xc4
pipe 4 (closed) bound   = 0xc5
pipe 5 (closed) bound   = 0xc6

the Pi4 code:

#include <ctime>       // time()
#include <iostream>    // cin, cout, endl
#include <time.h>      // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()

#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()

// custom defined timer for evaluating transmission time in microseconds
struct timespec startTimer, endTimer;
uint32_t getMicros(); // prototype to get ellapsed time in microseconds

void master();

struct mypayload
{ 
  float FloatData1;
  float FloatData2; 
  uint32_t Duration1; 
  uint32_t Duration2; 
  uint16_t Id; 
  uint16_t Data1; 
  uint16_t Data2; 
  uint16_t Data3; 
  uint16_t Data4; 
  uint16_t Data5; 
  uint32_t Data6; 
};

using namespace std;

// Generic:
RF24 radio(25, 0);

int main(int argc, char** argv) {

    // perform hardware check
    if (!radio.begin()) {
        cout << "radio hardware is not responding!!" << endl;
        return 0; // quit now
    }

    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit

    // print example's name
    cout << argv[0] << endl;

    // Let these addresses be used for the pair
    uint8_t address[2][6] = {"1Node", "2Node"};

    radio.setChannel(100);

    radio.setPayloadSize(sizeof(mypayload)); // float datatype occupies 4 bytes

    radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.

    radio.setDataRate(RF24_250KBPS);

    radio.setRetries(5,15);

    radio.setAutoAck(true);

    // set the TX address of the RX node into the TX pipe
    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0

    // set the RX address of the TX node into a RX pipe
    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1

    // For debugging info
    radio.printDetails();       // (smaller) function that prints raw register values
    radio.printPrettyDetails(); // (larger) function that prints human readable data

    cout << "mypayload = " << sizeof(mypayload) << endl;

    master();

    return 0;
}

void master() {
    radio.stopListening();  // put radio in TX mode
    bool ok = false;
    while (ok != true) {;//(failure < 10) {
        clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);            // start the timer
        mypayload packet;

        packet.FloatData1=1.5;
        packet.FloatData2=50.55;    
        packet.Duration1 = 75000;
        packet.Duration2 = 1234567;  
        packet.Id = 260;    
        packet.Data1=0;
        packet.Data2=10;
        packet.Data3=100;
        packet.Data4=200;
        packet.Data5=255;
        packet.Data6=65000;    

        cout << "FloatData1=" << packet.FloatData1 << endl;
        cout << "FloatData2=" << packet.FloatData2 << endl;
        cout << "Duration1=" << packet.Duration1 << endl;
        cout << "Duration2=" << packet.Duration2 << endl;
        cout << "Id=" << packet.Id << endl;
        cout << "Data1=" << packet.Data1 << endl;
        cout << "Data2=" << packet.Data2 << endl;
        cout << "Data3=" << packet.Data3 << endl;
        cout << "Data4=" << packet.Data4 << endl;    
        cout << "Data5=" << packet.Data5 << endl;
        cout << "Data6=" << packet.Data6 << endl;

        bool report = radio.write(&packet, sizeof(packet));

        uint32_t timerEllapsed = getMicros();                       // end the timer

        if (report) {
            cout << "Transmission successful! Time to transmit = ";
            cout << timerEllapsed << endl;                                  // print the timer result
        } else {
            cout << "Transmission failed or timed out" << endl;
        }

        delay(1000); 
    }
}

// Calculate the ellapsed time in microseconds
uint32_t getMicros() {
    // this function assumes that the timer was started using
    // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`

    clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
    uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
    uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;

    return ((seconds) * 1000 + useconds) + 0.5;
}

and the MKRZero code:

#include <SPI.h>
#include "printf.h"
#include "RF24.h"

struct mypayload
{
  float FloatData1;
  float FloatData2;
  uint32_t Duration1;
  uint32_t Duration2;
  uint16_t Id;
  uint16_t Data1;
  uint16_t Data2;
  uint16_t Data3;
  uint16_t Data4;
  uint16_t Data5;
  uint32_t Data6;
};

RF24 radio(6, 7); // using pin 7 for the CE pin, and pin 8 for the CSN pin

uint8_t address[][6] = {"1Node", "2Node"};

bool radioNumber = 0; // 0 uses address[0] to transmit, 1 uses address[1] to transmit

void setup() {

  Serial1.begin(115200);
  while (!Serial1) {
    // some boards need to wait to ensure access to serial over USB
  }

  if (!radio.begin()) {
    Serial1.println(F("radio hardware is not responding!!"));
    while (1) {} // hold in infinite loop
  }

  radio.setChannel(100);

  radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.

  radio.setRetries(5, 15);

  radio.setDataRate(RF24_250KBPS);

  radio.setPayloadSize(sizeof(mypayload)); // float datatype occupies 4 bytes

  radio.setAutoAck(true);

  //radio.disableDynamicPayloads();

  // set the TX address of the RX node into the TX pipe
  radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0

  // set the RX address of the TX node into a RX pipe
  radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1

  // CAN'T MAKE IT SERIAL PRINT ON MKRZERO
  // For debugging info
  //printf_begin();             // needed only once for printing details
  //radio.printDetails();       // (smaller) function that prints raw register values
  //radio.printPrettyDetails(); // (larger) function that prints human readable data

  Serial1.print("packet size=");
  Serial1.println(sizeof(mypayload));

  radio.startListening();
} // setup

void loop() {

  mypayload packet;

  uint8_t pipe;
  if (radio.available(&pipe)) {             // is there a payload? get the pipe number that recieved it
    uint8_t bytes = radio.getPayloadSize(); // get the size of the payload
    radio.read(&packet, sizeof(packet));            // fetch payload from FIFO
    Serial1.print(F("Received "));
    Serial1.print(bytes);                    // print the size of the payload
    Serial1.print(F(" bytes on pipe "));
    Serial1.print(pipe);                     // print the pipe number
    Serial1.print(F(": size="));
    Serial1.println(sizeof(packet));                // print the payload's value

    Serial1.print("FloatData1=");
    Serial1.println(packet.FloatData1, 2);
    Serial1.print("FloatData2=");
    Serial1.println(packet.FloatData2, 2);    
    Serial1.print("Duration1=");
    Serial1.println(packet.Duration1);
    Serial1.print("Duration2=");
    Serial1.println(packet.Duration2);
    Serial1.print("Id=");
    Serial1.println(packet.Id);    
    Serial1.print("Data1=");
    Serial1.println(packet.Data1);
    Serial1.print("Data2=");
    Serial1.println(packet.Data2);
    Serial1.print("Data3=");
    Serial1.println(packet.Data3);
    Serial1.print("Data4=");
    Serial1.println(packet.Data4);
    Serial1.print("Data5=");
    Serial1.println(packet.Data5);                
    Serial1.print("Data6=");
    Serial1.println(packet.Data6);            

  }
}

What i receive is:

23:01:12.902 -> Received 32 bytes on pipe 1: size=32
23:01:12.902 -> FloatData1=1.75
23:01:12.902 -> FloatData2=ovf
23:01:12.902 -> Duration1=79612
23:01:12.902 -> Duration2=1834951
23:01:12.902 -> Id=262
23:01:12.902 -> Data1=0
23:01:12.902 -> Data2=15
23:01:12.902 -> Data3=118
23:01:12.902 -> Data4=236
23:01:12.902 -> Data5=255
23:01:12.902 -> Data6=65532
23:01:27.538 -> Received 32 bytes on pipe 1: size=32
23:01:27.538 -> FloatData1=1.75
23:01:27.538 -> FloatData2=ovf
23:01:27.538 -> Duration1=79612
23:01:27.538 -> Duration2=1834951
23:01:27.538 -> Id=262
23:01:27.538 -> Data1=0
23:01:27.538 -> Data2=15
23:01:27.538 -> Data3=118
23:01:27.538 -> Data4=236
23:01:27.538 -> Data5=255
23:01:27.538 -> Data6=65532
23:01:28.533 -> Received 32 bytes on pipe 1: size=32
23:01:28.533 -> FloatData1=1.75
23:01:28.533 -> FloatData2=ovf
23:01:28.533 -> Duration1=79612
23:01:28.533 -> Duration2=1834951
23:01:28.533 -> Id=262
23:01:28.533 -> Data1=0
23:01:28.533 -> Data2=15
23:01:28.533 -> Data3=118
23:01:28.533 -> Data4=236
23:01:28.533 -> Data5=255
23:01:28.533 -> Data6=65532

(For some reason i can't get the code formatting right. I hope you understand it.)

dkts2000 commented 3 years ago

sizeof on MKRZero:

23:28:00.283 -> uint8_t=1
23:28:00.283 -> uint16_t=2
23:28:00.283 -> uint32_t=4
23:28:00.283 -> uint64_t=8
23:28:00.283 -> float=4
23:28:00.283 -> char=1
23:28:00.283 -> unsigned long=4
23:28:00.283 -> mypayload=32

and on Pi4:

uint8_t = 1
uint16_t = 2
uint32_t = 4
uint64_t = 8
float = 4
char = 1
unsigned long = 4
mypayload = 32
2bndy5 commented 3 years ago
 // CAN'T MAKE IT SERIAL PRINT ON MKRZERO
 // For debugging info
 //printf_begin();             // needed only once for printing details
 //radio.printDetails();       // (smaller) function that prints raw register values

This is because the ArduinoCore-samd doesn't support the printf() calls that we use internally for debugging. The adafruit fork of ArduinoCore-samd does however.

For some reason i can't get the code formatting right. I hope you understand it.

Please use "fenced" code-blocks. For example:

```cpp // your code here ```

The "cpp" is optional and only does syntax highlighting. The "insert code" button only works for inline `code-blocks`.

I should mention that i use the simple NRF24L01+ (not the one with the antenna) and a Socket Adapter Module Board For 8 Pin NRF24L01+ Wireless Transceiver.

About this adapter board you're using: If the jumper wires are long (by "long" I mean anything more than 1 inch) or the connections are somehow loose, then lower the SPI speed by specifying it in the c'tor.

RF24 radio(6, 7, 4000000); // uses 4MHz for SPI speed (default is 10MHz)

This advice is a bit of a hack though. It would be best to use short jumpers and connections that are as tight as possible.

2bndy5 commented 3 years ago

BTW, your float is outputting like "ovf" because this snippet is the code called to output a float. But that only confirms your received data is somehow getting corrupted.

TMRh20 commented 3 years ago

Yeah, the code works fine between RPi4 and Arduino Due, so this is an odd one. Since the radios check the CRC, the data should be good there, its either getting corrupted at the RPi4 before sending or after receiving. I would be very interested to know if this code can be tested with RPi4 to another device other than MKRZero. This would point out where the data is getting corrupted.

dkts2000 commented 3 years ago

Well, the same code on a Leonardo works fine. Receives the values correctly. So what's wrong with MKRZero? Could it be something regarding bit order?

2bndy5 commented 3 years ago

Could it be something regarding bit order?

I think we would've run into endianess problems on the ATSAMD21 a long time ago. I have several ATSAMD21 based boards, and the only problem I ever had with them is with using long jumper wires. However, with my code I was able to look at the whole payload in hexadecimal and binary representation.

I agree with TMRh20, this has to be a corruption at either of the radio modules' SPI connections (SCK, MOSI, MISO). It is strange that the erroneous data is only observed on the MKRZero board (which is why I'm guessing its a problem with wires connecting to the MKRZero board).

Well, the same code on a Leonardo works fine. Receives the values correctly

Are you using the same wires and the same adapter module?

dkts2000 commented 3 years ago

No. Different wires. Different 8 pin Socket Adapter Module but the same NRF24L01+ module (and also a different NRF24L01+ which worked fine as well). I'll try using the same wires and modules from the MKRZero and also a different pair on it. The wires are the 10cm ones. I don't have smaller.

Also (if this helps) both on Leonardo and the MKRZero i connect the power to the 5v pin (not directly from the NRF24L01+ but from the socket adapter which i read has a 3.3V regulator). If i connect it to arduino's 3.3V pin they both fail receiving (and transmitting) any data (Leonardo and MKRZero). On the RPi4 side the power goes to a 3.3V pin with no problems (also from a 8 pin socket adapter).

The Leonardo is powered from a 9V external power supply and the MKRzero from a breadboard power supply module (which outputs 5V) to Vin which (breadboard power supply) is powered from the same 9V external power supply. Only one arduino runs at a time.

dkts2000 commented 3 years ago

You were right. It was the wires after all! I replaced all the wires and now it works, although the connections are very sensitive. With a slight move it starts receiving weird values again. If i leave it in a state that receives right values, it's steady.

Thank you both for your interest and time.

2bndy5 commented 3 years ago

@dkts2000 you could also use a lower SPI speed for more reliability

​RF24 ​radio​(​6​, ​7​, ​4000000​); ​//​ uses 4MHz for SPI speed (default is 10MHz)