How does one split a string into more than only 2 values?

Hello everyone.

I have searched everywhere and tried implementing compiled variants to no avail.

Long story short: I am receiving a wireless signal that contains 5 discrete sensor values and I want to split them, route them, and display them accordingly.

Here is the significant snippet from the transmitter portion:

  // Combine all sensor readings
  str_out = str_Ctemp1 + "," + str_Ftemp1 + "," + str_Ctemp2 + "," + str_Ftemp2 + "," + str_humid;

From the serial monitor I get this:

Hum = 0
22.00,71.60,23.00,73.40,16.00*
C = 21.25
F = 1.25
C2 = .25
F2 = 25
Hum = 5
21.25,70.25,23.00,73.40,16.00*
C = 21.75
F = 1.75
C2 = .75
F2 = 75
Hum = 5
21.75,71.15,23.00,73.40,16.00*

Clearly the received transmission on the serial monitor is intact given this line: 21.75,71.15,23.00,73.40,16.00* (How do I get rid of that pesky asterisk BTW?)

Now I have the receiver code snippet here:

    // Split string into five values - HOW??????????
    for (int i = 0; i < str_out.length(); i++ ) {
      if (str_out.substring(i, i + 1) == ",") {
        str_Ctemp1 = str_out.substring(0, i);
        str_Ftemp1 = str_out.substring(i + 1);
        str_Ctemp2 = str_out.substring(1, i);
        str_Ftemp2 = str_out.substring(i + i);
        str_humid = str_out.substring(2, i);
        break;
      }
    }

It is clear to me as to how to SPLIT a string into TWO...

      // Split string into two values
      for (int i = 0; i < str_out.length(); i++) {
		  if (str_out.substring(i, i+1) == ",") {
			str_humid = str_out.substring(0, i);
			str_temp = str_out.substring(i+1);
			break;
		  }
		}

What if I want to decode MORE than just 2?

The (0,i); + (i+1); thing doesn't seem to work for fore than 2 values. :disappointed_relieved:

How would the code need to be written? :confused:

I adapted my code from this example shown in this URL: Using 433MHz RF Modules with Arduino | DroneBot Workshop

P.S. I don't know what I am doing and I am humbled by the Arduino gurus in here because I'm a noob.

Thank you in advance. :slight_smile:

snippetA.JPG

I have yet to see anything concise (IMHO) as to how one goes about splitting a String into more than 2 places so that each substring as it were can be printed to the Serial monitor accordingly.

And here's something for the code-snippets page...

    // Split string into five values - HOW??????????
    for (int i = 0; i < str_out.length(); i++ ) {
      if (str_out.substring(i, i + 1) == ",") {
        str_Ctemp1 = str_out.substring(0, i);
        str_Ftemp1 = str_out.substring(i + 1);// I got lost from here on down up to but limited by break;
        str_Ctemp2 = str_out.substring(1, i);   // Definitely wrong...
        str_Ftemp2 = str_out.substring(i + i);  // Definitely wrong...
        str_humid = str_out.substring(2, i);     // Definitely wrong...
        break;
      }
    }

    // Print values to Serial Monitor
    Serial.print("C = ");
    Serial.println(str_Ctemp1);
    Serial.print("F = ");
    Serial.println(str_Ftemp1);

    Serial.print("C2 = ");
    Serial.println(str_Ctemp2);
    Serial.print("F2 = ");
    Serial.println(str_Ftemp2);
    Serial.print("Hum = ");
    Serial.println(str_humid);

If the entire code(s) are needed for clarity on your part, then I can provide that as well.

Any pointing in the right direction is appreciated.

something like this is short enough for you? :wink:

void setup() {
  char str[] = "strtok needs to be called several times to split a string";
  char delim[] = " ";

  char *ptr = strtok(str, delim);

  Serial.begin(115200);

  while(ptr != NULL)
  {
    Serial.println(ptr);
    ptr = strtok(NULL, delim);
  }

}

void loop() {
  // ...
}

output:

strtok
needs
to
be
called
several
times
to
split
a
string

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

...R

Robin2:
It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

...R

like in my example! :wink:

/*
  433 MHz RF Module Receiver Demonstration 2
  RF-Rcv-Demo-2.ino
  Demonstrates 433 MHz RF Receiver Module with MAX6675 Thermocouple
  Reciever goes to pin 11
*/

// Include RadioHead Amplitude Shift Keying Library
#include <RH_ASK.h>
// Include dependant SPI Library
#include <SPI.h>

#include <Wire.h>
#include <LiquidCrystal_I2C.h>



// Bargraph level
#define maxValue 37.78 // (100°F ≈ ~37.78°C)

byte lcdNumCols = 16; // -- number of columns in the LCD
byte lcdLine = 2; // -- number of line in the LCD


// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, lcdNumCols, lcdLine);// use I2C Scanner to find the address: 0x27 or something like that


#include <LcdBarGraphRobojax.h>
LcdBarGraphRobojax rjx(&lcd, 16, 0, 1);  // -- creating 16 character long bargraph starting at char 0 of line 1


// Define output strings
String str_Ctemp1; // Thermocouple Celsius
String str_Ftemp1; // Thermocouple Farenheit
String str_Ctemp2; // Stores DHT temperature value in Celsius
String str_Ftemp2; // Stores DHT temperature value in Farenheit
String str_humid;  // Stores DHT humidity value in percent
String str_out;    // Full output string object


// Create Amplitude Shift Keying Object
RH_ASK rf_driver;


const int a = 32; // Set fom 0 to 255 for a given brightness via PWM

const int LCDPin = 3; // the pin that the LCD LED is attached to PWM pin 3

void setup() {

  char str[] = "strtok needs to be called several times to split a string";
  char delim[] = " , ";

  char *ptr = strtok(str, delim);

  Serial.begin(115200);

  while(ptr != NULL)
  {
    Serial.println(ptr);
    ptr = strtok(NULL, delim);
  }




  pinMode (2 , OUTPUT);    // declare LED pin 2
  pinMode(LCDPin, OUTPUT); // declare pin 3 to be a PWM output
  pinMode (4 , OUTPUT);    // declare LED pin 4



  // Initialize ASK Object
  rf_driver.init();
  // Setup Serial Monitor
  //Serial.begin(9600);



  // initialize the LCD
  lcd.begin();

  // Turn on the blacklight and print a message.
  lcd.backlight();

  delay(500); // wait for MAX chip to stabilize
}

void loop() {


  analogWrite(LCDPin, a); // set the brightness of PWM pin 3:


  int Sensor = str_Ctemp1.toInt();
  if (maxValue < Sensor) {
    Sensor = 0;
  }

  //int distance =sensor.startContinuous(100);
  // rjx.clearLine(0);// clear line 0 to display fresh value

  // -- draw bar graph from the analog value read
  rjx.drawValue(Sensor , maxValue);
  // -- do some delay: frequent draw may cause broken visualization







  // Set buffer to size of expected message
  uint8_t buf[30]; // Was 11 for 2 readings
  uint8_t buflen = sizeof(buf);
  // Check if received packet is correct size
  if (rf_driver.recv(buf, &buflen))
  {

    // Message received with valid checksum
    // Get values from string

    // Convert received data into string
    str_out = String((char*)buf);



    // Split string into five values - HOW??????????
    for (int i = 0; i < str_out.length(); i++ ) {
      if (str_out.substring(i, i + 1) == ",") {
        str_Ctemp1 = str_out.substring(0, i);
        str_Ftemp1 = str_out.substring(i + 1);
        str_Ctemp2 = str_out.substring(1, i); // Got lost here        str_Ftemp2 = str_out.substring(i + i);// and here
        str_humid = str_out.substring(2, i);// and here
        break;
      }
    }

    // Print values to Serial Monitor
    Serial.print("C = ");
    Serial.println(str_Ctemp1);
    Serial.print("F = ");
    Serial.println(str_Ftemp1);

    Serial.print("C2 = ");
    Serial.println(str_Ctemp2);
    Serial.print("F2 = ");
    Serial.println(str_Ftemp2);
    Serial.print("Hum = ");
    Serial.println(str_humid);


  Serial.println(str_out);


    // Print values to LCD
    lcd.setCursor(0, 0);
    //    lcd.print("Temp: ");
    lcd.print(str_Ftemp2);
    lcd.print((char)223);
    lcd.print("F");
    lcd.setCursor(9, 0);
    //    lcd.print("Temp: ");
    lcd.print(str_Ctemp2);
    lcd.print((char)223);
    lcd.print("C");






    // if temperature in °F goes above X, turn the LED ON
    if (str_Ftemp1.toInt() > 80.00) {
      digitalWrite(2, HIGH);// set pin 2 HIGH
    } else {
      digitalWrite(2, LOW);// set pin 2 LOW
    }


    // if temperature in °C goes below X, turn the LED ON
    if (str_Ctemp1.toInt() < 3.00) {
      digitalWrite(4, HIGH);// set pin 4 HIGH
    } else {
      digitalWrite(4, LOW);// set pin 4 LOW
    }
  }
}

This code now compiles great... and it still has issues.

...
..
.

I'll take a shot at it. Will the values always have five characters? Will there always be five values?

This is where I'm lost.

     // Split string into five values - HOW??????????
    for (int i = 0; i < str_out.length(); i++ ) {
      if (str_out.substring(i, i + 1) == ",") {
        str_Ctemp1 = str_out.substring(0, i);
        str_Ftemp1 = str_out.substring(i + 1);
        str_Ctemp2 = str_out.substring(1, i);
        str_Ftemp2 = str_out.substring(i + 2);
        str_humid = str_out.substring(i, 3);
        break;
      }
    }

And this doesn't really guide me... It adds even more confusion to the issue.

void setup() {

  char str[30] = "str_out";
  char delim[4] = " , ";

  char *ptr = strtok(str, delim);

  Serial.begin(9600);

  while(ptr != NULL)
  {
    Serial.println(ptr);
    ptr = strtok(NULL, delim);
  }

Back to my original question now...

What exactly needs to be written and where do I write it? :roll_eyes:

Long story short: How do I break up the received "string" so that I can properly display each substring?

Since my coding skills are sub-par, I have come here seeking some guidance.

At this point in time, THIS is what I get from the Serial Monitor:

21.50,70.70,20.00,68.00,95.00*
C = 21.75
F = 1.75
C2 = .75
F2 = 75
Hum = 5
21.75,71.15,20.00,68.00,95.00*
C = 21.50
F = 1.50
C2 = .50
F2 = 50
Hum = 0
21.50,70.70,20.00,68.00,95.00*

I want to take 21.50,70.70,20.00,68.00,95.00* and display it vertically minus the *.
(I know the answer I'm looking for is simple to the God members and I humbly bow to you.)

Again, my coding skills suck and I admit that.

The mantra around here is not to use the String class. I'd follow that, at least for low-memory AVR processors. ARM and ESP processors my be a different matter.

Anyway, don't do this:

   // Convert received data into string
    str_out = String((char*)buf);

Just work with the cstring (aka array of char) as it is. Use strtok to split it apart as demonstrated by @sherzaad.

Try this example:

void setup() {
  char data[] = "21.50,70.70,20.00,68.00,95.00*";   // The string to break out
  char delimiter[] = ",*";
  char *ptr;

  Serial.begin(9600);

  ptr = strtok(data, delimiter);          // Set pointer to start of string...

  /* walk through other tokens */
  while ( ptr != NULL ) {                 // As long as we don't see the end-of-string marker, NULL...
    Serial.println(ptr);                  // Show the substring we just created
    ptr = strtok(NULL, delimiter);        // Set the pointer past the delimited and look again...
  }
  Serial.println("Done.");
}

void loop() {

}

The strtok() function sets a pointer to the start of your input string. The while loop says that, as long as we don't see the NULL that marks the end of the input string, keep looking for a character that matches one of the characters in the delimiter[] array. If it finds a match, it overwrites that character with a NULL, which means that ptr now can be treated as a sub-string. We use the Serial object to display this substring. The next call to strtok() resumes the search, but has set the pointer to look past the last delimiter character it found. We continue to march through the string until we see the original NULL character of the input string. Since you don't want to "see" the asterisk, I included it in the delimiter[] array.

Note that strtok() is destructive. That is, it chops up the input string into substrings, thus destroying the original string. If you need the original string, make a copy of it before the call to strtok(). If you need to preserve the substrings, use strcpy() to make copies of those in the while loop.

@Arduino_n00b

Topic MERGED.

It has been clear that you cannot follow instructions or guides despite being told on at least three occasions either by myself or others.

Please take the next 7 days to Learn How To Use The Forum.
It will help you get the best out of the forum.

Bob.

Parsing a String is fairly simple if the String contains data delimiters and the end of packet marker. As to print in the data in vertical packets, adding a carriage return/line feed is a typical way.

THANKS for good discussion