Sending data struct from Arduino to Raspberry via NRF24L01

Hello, I have a problem sending data struct from arduino to raspberry via NRF24 module, communication is working I can send text or single numbers but I want to send package with to field id and temperature but my output looks like that:

id: temperatura: 0

And for now I’m only sending id = 1 temperature = 2.

It looks like all data is written to id field, but I don’t have much experience with c/c++ programming and I cant really tell what’s wrong. So my question is what is wrong with code below or how I send pair of data id and temeprature in one package.

Here si my transmitter code(Arduino):

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

#define RREF      430.0
#define RNOMINAL  100.0

typedef struct{
  uint8_t id;
  uint16_t temperature;
}
temp;

temp data;
bool radioNumber = 0;
int i =0;
Adafruit_MAX31865 thermo = Adafruit_MAX31865(10, 11, 12, 13);
RF24 radio(7,8);

byte addresses[][6] = {"1Node","2Node"};
bool role = 0;

void setup() {
  Serial.begin(115200);
  thermo.begin(MAX31865_3WIRE);
  radio.begin();
  radio.setChannel(125);
  radio.setPALevel(RF24_PA_MAX);
  radio.powerUp();
  radio.setDataRate(RF24_250KBPS);
  if(radioNumber){
    radio.openWritingPipe(addresses[1]);
    radio.openReadingPipe(1,addresses[0]);
  }else{
    radio.openWritingPipe(addresses[0]);
    radio.openReadingPipe(1,addresses[1]);
  }
  radio.stopListening();
}

void loop() {
 
     if( i == 14){ i = 0; }                                   
    data.id = 1;
    //data.temperature = thermo.temperature(RNOMINAL, RREF);
    data.temperature = 2;
    Serial.println(F("Now sending"));   
    if (!radio.write( &data, sizeof(data) )){
        Serial.println(F("failed"));
     }
     delay(1000);
}

And my receiver code(Raspberry pi):

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <unistd.h>
#include <RF24/RF24.h>

using namespace std;

typedef struct{
        uint8_t id = 99;   // debug value
        uint16_t temperature = 99;
}
temp;

temp data;
RF24 radio(22,0);

bool radioNumber = 1;
const uint8_t pipes[][6] = {"1Node", "2Node"};

int main(int argc, char** argv)
{
    radio.begin();
    radio.setChannel(125);
    radio.setPALevel(RF24_PA_MAX);
    radio.setDataRate(RF24_250KBPS);
    radio.setRetries(15, 15);
    radio.printDetails();
    if (!radioNumber) {
        radio.openWritingPipe(pipes[0]);
        radio.openReadingPipe(1, pipes[1]);
    } else {
        radio.openWritingPipe(pipes[1]);
        radio.openReadingPipe(1, pipes[0]);
    }

    radio.startListening();

    while (1) {
            if (radio.available()) {
                while (radio.available()) {
                    radio.read(&data, sizeof(data));
                }
                cout  << "  " << "id: "<< data.id<< " temperatura: " << data.temperature << endl;
                delay(925);

            }

on the pi should you call setPayloadSize( sizeof(data)) ?

also the print should be in the loop so you print when something is received, e.g.

   while (1) {
            if (radio.available()) {
                    radio.read(&data, sizeof(data));
                   cout  << "  " << "id: "<< data.id<< " temperatura: " << data.temperature << endl;
                  }
            }

why the delay() ? radio.available() should only return true if data is available

horace:
on the pi should you call setPayloadSize( sizeof(data)) ?

When I changed it, they can't communicate with each other, I changed it on both devices but then I have no trasmission I guess? I dont really know why.

horace:
why the delay() ? radio.available() should only return true if data is available

I take this from example code from lib(https://tmrh20.github.io/RF24/gettingstarted_8cpp-example.html) and they said that delay is to minimize raspberry CPU time so I left it there.

EDIT:
Okey you acaully point me to the right track because this struct has various size in arduino and raspberry so I changed on arduino to int id and int temperature and in raspberry to uint16_t id and uint16_t temperature and everything is ok! But can you tell me why they were variouis size?

TracerP:
But can you tell me why they were variouis size?

probably due to how the compilers for the different processor were packing the uint8_t to byte or word boundaries

typedef struct{
  uint8_t id;
  uint16_t temperature;
}

sending informationin in binary between different processors can have problems with different variable type sizes and also if the endianness of multibyte types is different

for example run this code on an Arduino Uno

typedef struct{
  uint8_t id;
  uint16_t temperature;
}
temp;

void setup() {
  while(!Serial);
  delay(1000);
  // Start the built-in serial port, probably to Serial Monitor
  Serial.begin(115200);
  Serial.print("sizeof struct ");
  Serial.println(sizeof(temp));

}

void loop() {
  // put your main code here, to run repeatedly:

}

gives

sizeof struct 3

running the following on a PC (Compiled using gcc)

#include <stdint.h>

typedef struct{
  uint8_t id;
  uint16_t temperature;
}
temp;

int main(void) {
  printf("sizeof struct %d", sizeof(temp));
}

gives

sizeof struct 4

gcc has added a byte after id to align to a 16bit word boundary

Thank you for answer that I could understand, I didnt thought about that. For sure now I will be remeber that I spent like whole day debugging this issue. Thanks again. And may I ask one thing? Is there any way to deal with the noise? I already moved up to channel 125 that I think corresponds to WiFi channel 14 that is unused by any local WiFi network. For now almost every package are misrepresented not all for example I'm reading 27C but recieved data is 45 or something like that. I don't really know much about wireless transmission or transmission whatsoever.

are you sure you are receving and decoding complete frames, e.g. if you miss some bytes you may read half of one frame then half of the next

in transmitting binary data over communications links which may have errors I tend to create a frame with a known start and end sequences and process a byte at a time, e.g. frame such as
SYN DLE STX byte stuffed data CRC DLE ETX

  1. incomming data stream is searched for start of frame sequence SYN DLE STX
  2. the data then follows (with DLEs byte stuffed)
  3. CRC check
  4. followed by end of frame DLE ETX

the data itsellf will have sequence numbers so it is known if frames are lost or corrupted

I’m pretty sure that now I’m receving complete frames, but your idea is clever I thnik I’m going to try doing something like that.

to check you are receiving complete frames etc add a simple sequence number and a checksum to the structure

typedef struct{
  uint8_t seq;     // sequence number
  uint8_t id;
  uint16_t temperature;
  uint8_t crc;      // CRC or simple checksum
}
temp;

the sequence number is an unsigned byte and cycles 0 thru 255 will show if you are loosing frame
the crc checks for errors in the frame - try a simple checksum initially

Nice going to test it today! I have read yesterady some and come across various data, some people saing that this high power modules (with external antenna) can't be reliable supply from 3.3V from arduino due to hight current draw I think it was around 15 mA, can it be true? I think 15mA is quite small value but I don't have any external 3,3V voltage regulators so I can't test it, so for now I just put a 10uF electrolytic capacitor between Vcc and GND but I cant see any differences. I have a question to calculating a checksum will simple XOR would do the trick? I mean something like that

uint16_t checksum = 0;
checksum ^= seq + id + temperature;

I'm going to check the code this afternoon after I get along with everything else.

usually for a simple checksum one sums the unsigned bytes of a record ignoring any overflow
something along the lines of

#include <stdio.h>
#include <stdint.h>
typedef struct{
  uint8_t seq;     // sequence number
  uint8_t id;
  uint16_t temperature;
  uint8_t crc;      // CRC or simple checksum
}
temp;
temp data;

// checksum of data up to crc
unsigned char checksum(unsigned char *data, unsigned char *crc) {
    unsigned char sum=0;
    while(data != crc) {
      sum+=*data;
      data++;
      }
    return sum;
    }

int main(void) {
    data.seq=1;
    data.id=3;
    data.temperature=5;
    printf("checksum = %d", checksum(&data, &data.crc));
}

gives

checksum = 9

I’m keep getting this error and don’t really know what to do about this. changing data types do not make it better simillar error shows up. Any idea? This error is the same at raspberry and arduino

error: cannot convert 'temp*' to 'unsigned char*' for argument '1' to 'unsigned char checksum(unsigned char*, unsigned char*)'

     data.crc = checksum(&data, &data.crc);

                                         ^

exit status 1
cannot convert 'temp*' to 'unsigned char*' for argument '1' to 'unsigned char checksum(unsigned char*, unsigned char*)'

the code of #9 was tested as a C program

C++ requires the parameters to be cast to the correct type, e.g.

    printf("checksum = %d",checksum( (unsigned char *) &data, &data.crc));

It is going somewhere but now I don’t know what I’m looking at.

This is whole code I’m runining on arduino

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

#define RREF      430.0
#define RNOMINAL  100.0

////// Updated struct as you suggested
typedef struct{
  uint8_t seq;
  uint8_t id;
  uint16_t temperature ;
  uint8_t crc;
}
temp;

temp data;
// Checksum
unsigned char checksum(unsigned char *data, uint8_t *crc) {
    unsigned char sum=0;
    while(data != crc) {
      sum+=*data;
      data++;
      }
    return sum;
    }

bool radioNumber = 0;
Adafruit_MAX31865 thermo[1] = {Adafruit_MAX31865(10, 11, 12, 13)};
RF24 radio(7,8);

byte addresses[][6] = {"1Node","2Node"};
bool role = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(sizeof(data));
  thermo[0].begin(MAX31865_3WIRE);
  radio.begin();
  radio.setChannel(125);
  radio.setPALevel(RF24_PA_MIN);
  radio.powerUp();
  radio.setDataRate(RF24_1MBPS);
  if(radioNumber){
    radio.openWritingPipe(addresses[1]);
    radio.openReadingPipe(1,addresses[0]);
  }else{
    radio.openWritingPipe(addresses[0]);
    radio.openReadingPipe(1,addresses[1]);
  }
  radio.stopListening();
}

void loop() {
//Dummy data
    data.seq = 1;
    data.id = 3;
    data.temperature = thermo[0].temperature(RNOMINAL, RREF);
    data.crc = checksum((unsigned char *)&data, &data.crc);
/////////   
    Serial.println(data.seq);
    Serial.println(data.id);
    Serial.println(data.crc); // Here CRC is printing to serial monitor but on raspberry isnt
    Serial.println(data.temperature);
    if (!radio.write( &data, sizeof(data) )){
        Serial.println(F("failed"));
     }
     delay(1000);
}

and raspberry RX

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <unistd.h>
#include <RF24/RF24.h>

using namespace std;

typedef struct{
        uint8_t seq;
        uint8_t id;   // debug value
        uint16_t temperature;
        uint8_t crc;
}
temp;

RF24 radio(22,0);
temp data;

bool radioNumber = 1;
const uint8_t pipes[][6] = {"1Node", "2Node"};

unsigned char checksum(unsigned char *data, unsigned char *crc){
        unsigned char sum = 0;
        while(data != crc){
                sum+=*data;
                data++;
        }
        return sum;
}


int main(int argc, char** argv)
{
    radio.begin();
    radio.setChannel(125);
    radio.setPALevel(RF24_PA_MIN);
    radio.setDataRate(RF24_1MBPS);
    radio.setRetries(15, 15);
    //cout << sizeof(data) << endl;
    radio.printDetails();
    if (!radioNumber) {
        radio.openWritingPipe(pipes[0]);
        radio.openReadingPipe(1, pipes[1]);
    } else {
        radio.openWritingPipe(pipes[1]);
        radio.openReadingPipe(1, pipes[0]);
    }

    radio.startListening();
    while (1) {
            if (radio.available()) {
                while (radio.available()) {
                    radio.read(&data, sizeof(data));
                    if(checksum((unsigned char *)&data, &data.crc) == data.crc){
                            cout << "CRC Y" << endl;
                    } else{
                        cout << "CRC N" << endl;
                    }
                    cout <<"seq " << data.seq << " id: "<< data.id<< " temperatura: " << data.temperature << " crc " << data.crc << endl; // here again I have problem with displaying the data
                }
                delay(925);

            }


    } // forever loop

    return 0;
}

And my raspberry output is

CRC Y // received checksum is the same as transmitted
seq  id:  temperatura: 28 crc  // here again I cant access seq and id field
CRC N // checksums are not equal
seq  id:  temperatura: 30 crc 0

I feel like I’m again missing something obvious but I just cant get what it is.

EDIT: I ran it for a while like this and it looks like 1/3 to 1/2 packages have wrong checksum.

because seq, id and crc are uint8_t cout stream is treating them as characters and printing the equivalent ASCII character
try casting them to int, e.g.

     cout <<" seq " << (int) data.seq << " id: "<< (int) data.id<< " temperatura: " << data.temperature << " crc " << (int) data.crc << endl; // here again I have problem with displaying the data

horace:
because seq, id and crc are uint8_t cout stream is treating them as characters and printing the equivalent ASCII character
try casting them to int, e.g.

     cout <<" seq " << (int) data.seq << " id: "<< (int) data.id<< " temperatura: " << data.temperature << " crc " << (int) data.crc << endl; // here again I have problem with displaying the data

Ok it is working now I assume this is expected behaviour. But I know what is making so much noise. I’m using MAX31865 with pt100 to read temperature and when sending static data like id = 1 temperature = 3 it working like a charm but when I try to send this data.temperature = thermo[0].temperature(RNOMINAL, RREF); it is going crazy. For now I’m using software spi bus for max and hardware for NRF because I thought it will be better to sepparate this devices but maybe this is wrong apporach? Can you maybe relate to this? And thank you so much for your help without you I still would be lost in this.

EDIT:
On hardware SPI bus situation is the same almost 50% of packages are wrong

when transmitting frames increment seq each time so you can check for lost frames

I would use hardwareSPI bus

if you just use the Arduino to read the MAX31865 without attempting to transmit does it give correct values

Sorry I didnt notice second page, as I updated previous post, now I'm using hardware bus. Alone MAX31865 gives correct values

The first couple of packages are

seq 0 id: 3 temperatura: 31 crc 31
CRC ZLE
seq 1 id: 3 temperatura: 27 crc 30
CRC ZLE
seq 3 id: 3 temperatura: 31 crc 31
CRC ZLE
seq 3 id: 3 temperatura: 31 crc 32
CRC ZLE
seq 4 id: 3 temperatura: 27 crc 33
CRC ZLE
seq 7 id: 3 temperatura: 31 crc 51
CRC ZLE
seq 7 id: 3 temperatura: 31 crc 51
CRC ZLE
seq 7 id: 3 temperatura: 31 crc 36
CRC ZLE
seq 12 id: 3 temperatura: 31 crc 55
CRC OK
seq 9 id: 3 temperatura: 27 crc 39
CRC ZLE
seq 11 id: 3 temperatura: 27 crc 39
CRC ZLE
seq 15 id: 3 temperatura: 31 crc 60
CRC ZLE
seq 12 id: 3 temperatura: 27 crc 41
CRC ZLE
seq 15 id: 3 temperatura: 27 crc 43
CRC ZLE
seq 15 id: 3 temperatura: 27 crc 43
CRC ZLE
seq 15 id: 3 temperatura: 27 crc 44

I have var i that are iincremented at the end of the loop and at the beginning check if( i ==255) i =0;

when you are transmitting does the Arduino print correct MAX31865 values?
i.e. is the data being corrupted on the Arduino or during transmission

does the seq number show you are loosing frames?

I don't think I'm loosing frames, Serial.print(data.seq) shows correct values
I attach screenshoot of arduino serial monitor and raspberry output
Screenshot(google drive)

The numbers of frames are equal but the data inside are just horror

The funny thing is that transmitting static temperature eg data.temperature= 5 but incrementing seq all packages are wrong, the smoothes transmission are with all data static

looking at the raspberry pi output the first two frames are received Ok but after that sequence numbers and checksums are wrong

try removing the delay(925); in the loop - the Arduino should control the overall asynchronous timing
also print the crc values calculated on the Arduino - the more information to compare the better

also on the pi should you call setPayloadSize( sizeof(data)) otherwise it may default to some other value