Converting data to char array in order to send it with nrf25l01 module

Dear forum users,

As mentioned in the subject i am using nrf24l01 modules to send data to a raspbery pi using Arduino Nano.
The data needs to be transmitted as a single char array with 4 values separated by";". The first three values are acceleration measured with MPU6050 and the third one is the time between them in milliseconds (ideal value ~100). I've spent a lot of time trying to figure it out. That's the best I've managed to do:

bool send(int ax, int ay, int az, unsigned long czas){
  char sep[1] = {';'};
  bool ack = 0;
  float gax, gay, gaz;
  
  char mess[21];

  char outax[5+1];
  char outay[5+1];
  char outaz[5+1];
  char czasout [4];

  gax = (aax/16.384/1000);
  gay = (aay/16.384/1000);
  gaz = (aaz/16.384/1000);

  dtostrf(gax, 5, 2, outax);
  dtostrf(gay, 5, 2, outay);
  dtostrf(gaz, 5, 2, outaz);
  ultoa(czas,czasout, 10);
  
  strcpy(wiad, outax);
  strcat(wiad, sep);
  strcat(wiad, outay);
  strcat(wiad, sep);
  strcat(wiad, outaz);
  strcat(wiad, sep);
  strcat(wiad, czasout);
  
  
  ack = radio.write(mess, sizeof(wiad)); 


  return ack;
}

Still, what is received on the other side is:

[32, 48, 46, 48, 48, 59, 100, 45, 48, 46, 48, 50, 59, 100, 32, 49, 46, 48, 49, 59, 100]

, which translates to

0.00;d-0.02;d 1.01;d

and similar with different letters.
The values look ok, apart from the fact that the last one is missing. I tried printing the message before sending and it looks like this:

-0.00;d-0.02;d 1.00;d100

Sometimes a weird square character appears, between values.
NRF library: GitHub - nRF24/RF24: OSI Layer 2 driver for nRF24L01 on Arduino & Raspberry Pi/Linux Devices
Could anyone please help me with the char array conversion?

Greetings from Poland,
Artur P

PS.
Pardon my english, I'm not a native speaker.

May I suggest that you put your data into a struct and send the struct. It is, I think, much simpler, and avoids the strings and arrays.

These are modified examples from Robin2's simple rf24 tutorial code.

Here is example code to send and receive data as a struct.

Sender:

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


const byte CE_PIN = 9;
const byte CSN_PIN = 10;

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


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

struct LdrValues
{
   int ldr_1;
   int ldr_2;
   unsigned long timingInterval;
} ldrValues;

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

const byte LDR1 = A0;
const byte LDR2 = A2;

unsigned long tempMillis;

void setup()
{

   Serial.begin(9600);
   Serial.println("SimpleTx Starting");

   pinMode(LDR1, INPUT_PULLUP);
   pinMode(LDR2, INPUT_PULLUP);
   pinMode(A1, OUTPUT);
   digitalWrite(A1, LOW);
   radio.begin();
   radio.setChannel(76);  //76 library default
   //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
   radio.setPALevel(RF24_PA_HIGH);
   radio.setDataRate( RF24_250KBPS );
   radio.setRetries(3, 5); // delay, count
   radio.openWritingPipe(slaveAddress);
}

void loop()
{
   currentMillis = millis();
   if (currentMillis - prevMillis >= txIntervalMillis)
   {
      send();
      Serial.print("LDR 1 = ");
      Serial.print(ldrValues.ldr_1);
      Serial.print("    LDR 2 = ");
      Serial.print(ldrValues.ldr_2);
      tempMillis = currentMillis - prevMillis;
      Serial.print("     Sample interval = ");
      Serial.println(tempMillis);
      prevMillis = millis();
   }
}

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

void send()
{
   ldrValues.ldr_1 = analogRead(LDR1);
   ldrValues.ldr_2 = analogRead(LDR2);
   ldrValues.timingInterval = tempMillis;
   radio.write( &ldrValues, sizeof(ldrValues) );
}

Receiver:

// SimpleRx - the slave or the receiver

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

const byte CE_PIN = 9;
const byte CSN_PIN = 10;

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

RF24 radio(CE_PIN, CSN_PIN);

struct LdrValues
{
   int ldr_1;
   int ldr_2;
   unsigned long timingInterval;
} ldrValues;

bool newData = false;

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

void setup()
{

   Serial.begin(9600);

   Serial.println("SimpleRx Starting");

   radio.begin();
   radio.setChannel(76);  //76 library default
   //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
   radio.setPALevel(RF24_PA_HIGH);
   radio.setDataRate( RF24_250KBPS );
   radio.openReadingPipe(1, thisSlaveAddress);
   radio.startListening();
}

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

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

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

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

void showData()
{
   if (newData == true)
   {
      Serial.print("Data received >> ");
      Serial.print("LDR 1 = ");
      Serial.print(ldrValues.ldr_1);
      Serial.print("    LDR 2 = ");
      Serial.print(ldrValues.ldr_2);
      Serial.print("    timing interval = ");
      Serial.println(ldrValues.timingInterval);
      newData = false;
   }
}

So you are receiving the same info you are sending. That means the problem is with creating what you are sending. You can reuse the same buffer to convert those numbers.

bool send(int ax, int ay, int az, unsigned long czas){
  char sep = ";";
  bool ack = 0;
  float value;

  const int numSize = 10;
  const int bufferSize = 50;

  char numBuffer[numSize];
  char outputBuffer[bufferSize] = {0};

  value = ax/16.384/1000;
  dtostrf(value, numSize, 2, numBuffer);
  strcat(outputBuffer, numBuffer);
  strcat(wiad, sep);

  value = ay/16.384/1000;
  dtostrf(value, numSize, 2, numBuffer);
  strcat(outputBuffer, numBuffer);
  strcat(wiad, sep);
  
  value = az/16.384/1000;
  dtostrf(value, numSize, 2, numBuffer);
  strcat(outputBuffer, numBuffer);
  strcat(wiad, sep);

  ultoa(czas, numBuffer, 10);
  strcat(outputBuffer, numBuffer);
 
  ack = radio.write(outputBuffer, strlen(outputBuffer));

  return ack;
}

This is not a valid C-string and won't work with the function strcat().

 char sep[1] = {';'};

A valid C-string has a terminating zero byte:

 char sep[2]=";";

It is not a good idea to scale sensor values and send them as floats. Better to do the scaling at the receiving end.

 gax = (aax/16.384/1000);

Ok. Firstly, huge thank you to everyone who answered.

groundFungus:
May I suggest that you put your data into a struct and send the struct. It is, I think, much simpler, and avoids the strings and arrays.

Thanks for letting me know, but a string is easier for me to process on thhe Python side of things, but i will definiately consider your suggestion in the next version.

jremington:
This is not a valid C-string and won't work with the function strcat().

 char sep[1] = {';'};

A valid C-string has a terminating zero byte:

 char sep[2]=";";

It is not a good idea to scale sensor values and send them as floats. Better to do the scaling at the receiving end.

 gax = (aax/16.384/1000);

Thank you! Should've thought of that;)

Considering all your replies this is what i ended up with:

bool wyslij(int aax, int aay, int aaz, unsigned long czas){
  char sep[2] = ";";
  bool ack = 0;
  
  char wiad[21];
  char outax[5+1];
  char outay[5+1];
  char outaz[5+1];
  char czasout [3+1];
  itoa(aax, outax, 10);
  itoa(aay, outay, 10);
  itoa(aaz, outaz, 10);
  ultoa(czas,czasout, 10);
  
  strcpy(wiad, outax);
  strcat(wiad, sep);
  strcat(wiad, outay);
  strcat(wiad, sep);
  strcat(wiad, outaz);
  strcat(wiad, sep);
  strcat(wiad, czasout);
  
  
  ack = radio.write(&wiad, sizeof(wiad)); 
  Serial.println(ack);
  Serial.println(wiad);
  return ack;
}

Seems to work for now.
Once again thanks for help,
Artur P

but a string is easier for me to process on thhe Python side of things

With the struct there is no need for any conversion (int to string or string to int, for instance) on either end. You can send and receive the data type that you want.

Is the receiving nrf24l01 connected directly to the Raspberi?

is it like this

Arduino (sender) - NRF ========RF======== NRF - Raspbery

or like this

Arduino (sender) - NRF ========RF======== NRF - Arduino (receiver) -- serial --- Raspbery

FIrst, wiad is too short, you did not leave space for the terminating null.

Second, you do not need to put each value into a separate char array then concatenate them onto wiad, just use the wiad array for everything:

bool wyslij(int aax, int aay, int aaz, unsigned long czas) {
  const char sep[2] = ";";
  bool ack = 0;

  char wiad[22];
  
  itoa(aax, wiad, 10);
  strcat(wiad, sep);
  itoa(aay, wiad + strlen(wiad), 10); //wiad + strlen(wiad) = position of terminating null
                                      //this effectively appends the itoa() onto the end of wiad
  strcat(wiad, sep);
  itoa(aaz, wiad + strlen(wiad), 10);
  strcat(wiad, sep);
  ultoa(czas, wiad + strlen(wiad), 10);

  ack = radio.write(&wiad, sizeof(wiad));
  Serial.println(ack);
  Serial.println(wiad);
  return ack;
}

Sending as a struct would be simpler, but you do have to be careful that an int and unsigned long are the same size at the sender and receiver, and that the endianness is the same (whether the least significant byte or most significant byte is stored first).

you do have to be careful that an int and unsigned long are the same size at the sender and receiver

Yes, pretty much all the code that I write is for Uno, Nano, Mega so do forget about that. Would using data types like uint8_t, int32_t take care of that?

hzrnbgy:
Is the receiving nrf24l01 connected directly to the Raspberi?

is it like this

Arduino (sender) - NRF ========RF======== NRF - Raspbery

or like this

Arduino (sender) - NRF ========RF======== NRF - Arduino (receiver) -- serial --- Raspbery

Answer: option 1 Arduino (sender) - NRF ========RF======== NRF - Raspbery

david_2018:
FIrst, wiad is too short, you did not leave space for the terminating null.

Second, you do not need to put each value into a separate char array then concatenate them onto wiad, just use the wiad array for everything:

bool wyslij(int aax, int aay, int aaz, unsigned long czas) {

const char sep[2] = ";";
  bool ack = 0;

char wiad[22];
 
  itoa(aax, wiad, 10);
  strcat(wiad, sep);
  itoa(aay, wiad + strlen(wiad), 10); //wiad + strlen(wiad) = position of terminating null
                                      //this effectively appends the itoa() onto the end of wiad
  strcat(wiad, sep);
  itoa(aaz, wiad + strlen(wiad), 10);
  strcat(wiad, sep);
  ultoa(czas, wiad + strlen(wiad), 10);

ack = radio.write(&wiad, sizeof(wiad));
  Serial.println(ack);
  Serial.println(wiad);
  return ack;
}





Sending as a struct would be simpler, but you do have to be careful that an int and unsigned long are the same size at the sender and receiver, and that the endianness is the same (whether the least significant byte or most significant byte is stored first).

Thanks for help;)
After using this code wiad looks good in serial monitor (-126;-244;16670;101), but not so good on the raspberry pi (-278;116;16386;100@@)

Is the structure format of the Nano, byte for byte, exactly the same as that for the Pi ?

Rutra:
Thanks for help;)
After using this code wiad looks good in serial monitor (-126;-244;16670;101), but not so good on the raspberry pi (-278;116;16386;100@@)

You are sending the entire buffer length, not the length of the string it contains, so you will get a few extra random characters when the numbers are not the maximum length you have allowed. If you want fixed length fields for the numbers, use sprintf().

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