Go Down

Topic: Split of char array is a diva (Read 212 times) previous topic - next topic

MucFB

Hi together and greets from munich!

A short introduction to my project: I have two wemos d1 mini. One measures temperature, pressue and humidity with a bme280. The data is sent with a nrf24l01 to the other wemos. I sent all three values with one char array. The scond wemos splits the array and will (in some near future...) display the values on an E-ink display...

By now the measurement and the transmisson works. Also the split of the char array works kind of. But the second value acts....strange. And I just can't figure out why.

This is the Sketch of the wemos which measures and sends the data. The output on the Serial Monitor is like:
21:29:26.490 -> BME280 test
21:29:26.931 -> 951.15
21:29:26.931 -> 53.82
21:29:26.931 -> 26.63
21:29:26.931 -> 26.63/951.15/53.82#

So works fine...

Code: [Select]
#include <Wire.h>
#include <SPI.h>
#include "RF24.h"
#include "nRF24L01.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// Initialisierung fuer Funk
#define CE D3
#define CS D4
RF24 radio (CE, CS);
const byte address[6] = "00001";

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C

void setup() {
    Serial.begin(9600);
    delay(10);
    Serial.println('\n');   
   
    Serial.println("BME280 test");

    bool status;
   
    // Status des Sensors pruefen
    status = bme.begin(0x76); 
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }
   
    //Radio Initialisierung
    radio.begin();
    radio.openWritingPipe(address);
    radio.setPALevel(RF24_PA_MIN);
    radio.stopListening();
}

void loop() {
    char str_press[6] = ""; 
    dtostrf((bme.readPressure() / 100.0F), 4, 2, str_press);
    Serial.println(str_press);
   
    char str_hum[6] = "";
    dtostrf(bme.readHumidity(), 4, 2, str_hum);
    Serial.println(str_hum);
   
    char str_temp[6] = "";
    dtostrf(bme.readTemperature(), 4, 2, str_temp);
    Serial.println(str_temp);
   
    char messwerte[30] = "";
    sprintf(messwerte, "%s/%s/%s#", str_temp, str_press, str_hum);
    Serial.println(messwerte);

    radio.write(&messwerte, sizeof(messwerte));
   
    ESP.deepSleep(5e6);
}




Here is the Sketch of the wemos which receives the data and splits the values up. The Display hasn't been implemented yet.

Code: [Select]
#include <SPI.h>
#include "RF24.h"
#include "nRF24L01.h"

// Initialisierung fuer Funk
#define CE D3
#define CS D4
RF24 radio (CE, CS);
const byte address[6] = "00001";

void setup() {
  Serial.begin(9600);
  delay(10);

  //Radio Initialisierung
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();

}

void loop() {
  if (radio.available()) {
    char text[32] = "";
    char str_temp[6] = "";
    char str_press[6] = "";
    char str_hum[6] = "";
    radio.read(&text, sizeof(text));

    int counter = 0;
    int counter_temp = 0;
    int counter_press = 0;
    int counter_hum = 0;
   
    Serial.println(text);
   
    for (int i=0; i<=sizeof(text); i++) {
      if (text[i]=='/') {
        counter += 1;
      }
      else if (text[i]=='#') {
        counter = 3;
      }
      else {
        switch (counter) {
          case 0:
            str_temp[counter_temp] = text[i];
            counter_temp += 1;
            break;
          case 1:
            str_press[counter_press] = text[i];
            counter_press += 1;
            break;
          case 2:
            str_hum[counter_hum] = text[i];
            counter_hum += 1;
            break;
          case 3:
            i = sizeof(text);
            break;
        }
      }
       
    }

    Serial.println(str_temp);
    Serial.println(str_press);
    Serial.println(str_hum);

    Serial.println();
  }
}


The output on the serial monitor of the second wemos is like:

21:31:11.833 -> 26.76/951.11/53.97#
21:31:11.833 -> 26.76
21:31:11.833 -> 951.1126.76
21:31:11.866 -> 53.97


So it receives the array correct and, kind of, splits it correct. But the second value allways is wrong.
And I just don't know why...

So, has anyone an idea which can help me...?

Thank you very much in advance!!

jremington

#1
Jun 24, 2019, 09:42 pm Last Edit: Jun 24, 2019, 09:44 pm by jremington
I would use the C-string function strtok() to do the splitting. '/' and '#' are the tokens in your case.

Lots of tutorials on line, for example
https://www.geeksforgeeks.org/strtok-strtok_r-functions-c-examples/
https://fresh2refresh.com/c-programming/c-strings/c-strtok-function/

You need to make sure the character string is properly terminated with a zero byte. This probably does not do that. You need to know how many bytes were actually transferred.
Code: [Select]

    radio.read(&text, sizeof(text));

RayLivingston

#2
Jun 24, 2019, 09:48 pm Last Edit: Jun 24, 2019, 09:52 pm by RayLivingston
c "strings" MUST be terminated with a null terminator ('\0').  You are not terminating your strings, not are your three buffers necessarily long enough to hold both the strings, and the terminators.  And, you have NO protection if an invalid, too-long string is sent - your buffers WILL over-run, clobbering memory, and crashing the Arduino.

In fact, to do what you're doing, you do NOT need to copy the strings into the three str_xxxx buffers.  Simply save a pointer to the start of each string (the first character of the received string for the first, and the character after each '/' for the other two), and replace the three delimiters (the two '\' and one '#') with null characters. 

Regards,
Ray L.

Whandall

Why don't you simply send the float values (from a float array) directly?
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

Romonaga

Why send a string?  Create a struct. Populate struct. Send struct. 

No need to deal with parsing strings.
The universe exists only because we are aware of it.
We want a few mad people now. See where the sane ones have landed us!

MucFB

First: Thank you for your fast answers!

The missing terminator was the missing thing. I found a smart way online by just initialising every char array as ... = {0]. And now it works without a problem!

To your other comments: I use char arrays, because that's the easiest way to print the values on the E-Ink Display. So as I have to use them later I thought I can use them from the start.
I'm no pro in programming (yet!) so I'm not very used in working with pointers.
As the sensor is very reliable in the values it gives out I'm not too afraid of stack overflows. But it's good to know and I will implement protection probably later.
The suggestion with the struct sending sounds very good to me. I will probably try this.

So thanks again for your help!

Whandall

I use char arrays, because that's the easiest way to print the values on the E-Ink Display.
If you convert the values on the receiving side, you can omit the parsing step,
which gave you some headache IIRC.

Omitted code is easy to write, maintain, document, understand and it is error free.
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

jremington

Quote
Omitted code is easy to write, maintain, document, understand and it is error free.
Excellent and insightful observation!

Romonaga

If you convert the values on the receiving side, you can omit the parsing step,
which gave you some headache IIRC.

Omitted code is easy to write, maintain, document, understand and it is error free.
Not to mention it is faster to send a struct, lower packet size in many cases.  Also faster to process on the receiver side.

The universe exists only because we are aware of it.
We want a few mad people now. See where the sane ones have landed us!

Go Up