split string by delimiters

Hello,

i have obviously easy to solve problem but cant find out the solution. My sketch looks like this:

String serialResponse = "";
char sz[] = "Here; is some; sample;100;data;1.414;1020";

void setup()
{
 Serial.begin(9600);
 Serial.setTimeout(5);
 delay(5000);
 char *p = sz;
 char *str;
 while ((str = strtok_r(p, ";", &p)) != NULL) // delimiter is the semicolon
   Serial.println(str);

}

void loop()
{
if ( Serial.available()) {
 serialResponse = Serial.readStringUntil('\r\n');
 Serial.println(serialResponse);
}
}

I simplified a sketch so you can focus on my problem. As you can see in setup function i cut sz[] to pieces after each semicolon which is declared as delimiter.
I also listen to Serial port and if I have any incoming data I want to send it back as a confirmation.
the question is - how to mix both of those functions? I want to send via serial data, read it with readStringUntil, cut it by delimiter and send parts back to serial. In other words i want to put string from serialResponse use it as if it is sz[].
The problem i've reached while trying to do it by myself is that readStringUntil returns string, but strtok need chars and i have no idea how to mix it. Can you help me please?

readStringUntil returns string

Does it return a String or a string ? It looks like the former.

Have a look at Serial input basics to see how to read the serial input into a string (array of chars).

You're mixing up "Strings" with "String Objects" (and you aren't the first person). These links should clear it up.

The String Object has a method to return a String, upon which your code can operate:

#include "Arduino.h"

String serialResponse = "";
char sz[] = "Here; is some; sample;100;data;1.414;1020";

void setup()
{
 Serial.begin(9600);
 Serial.setTimeout(5);
}

void loop()
{
  if ( Serial.available()) {
    serialResponse = Serial.readStringUntil('\r\n');

    // Convert from String Object to String.
    char buf[sizeof(sz)];
    serialResponse.toCharArray(buf, sizeof(buf));
    char *p = buf;
    char *str;
    while ((str = strtok_r(p, ";", &p)) != NULL) // delimiter is the semicolon
      Serial.println(str);
  }
}

If you type "Here; is some; sample;100;data;1.414;1020" into the serial monitor, the results will be:

Here
is some
sample
100
data
1.414
1020

Note that this is pretty inefficient, however. You'd be better off making a function doesn't use a String Object, but just parses the serial input and builds a String directly. At that point, you might as well parse the fields as you receive them and then operate upon them once the "\r\n" is received.

You'd be better off making a function doesn't use a String Object, but just parses the serial input and builds a String directly

You'd be better off making a function doesn't use a String Object, but just parses the serial input and builds a string directly

Thank you for your answer. I'll try it in a moment.
You suggest to operate on individual chars coming from Serial until delimiter is reached if I understand correctly. Maybe I will however i guess code should be more complicated and I am not sure if efficiency of your code isnt enough for me. Anyways thank you for help... i have to become more familiar with all those strings, chars and so on.. that kind of knowledge is no necessery with php or javascript :slight_smile:

I am not sure if efficiency of your code isnt enough for me.

Each character, and hence the whole string/String, will take just as long to arrive however it is dealt with. In terms of the Arduino the characters arrive quite slowly which leaves plenty of time to process them.

9600 baud is very slow

Lots of ways to parse a captured character strings. The below uses the String functions. In this example the data string uses a comma "," to separate the data parts and a "*" to mark the end of the total data packet.

//zoomkat 11-12-13 String capture and parsing  
//from serial port input (via serial monitor)
//and print result out serial port
//copy test strings and use ctrl/v to paste in
//serial monitor if desired
// * is used as the data string delimiter
// , is used to delimit individual data 

String readString; //main captured String 
String angle; //data String
String fuel;
String speed1;
String altidude;

int ind1; // , locations
int ind2;
int ind3;
int ind4;
 
void setup() {
  Serial.begin(9600);
  Serial.println("serial delimit test 11-12-13"); // so I can keep track of what is loaded
}

void loop() {

  //expect a string like 90,low,15.6,125*
  //or 130,hi,7.2,389*

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == '*') {
      //do stuff
      
      Serial.println();
      Serial.print("captured String is : "); 
      Serial.println(readString); //prints string to serial port out
      
      ind1 = readString.indexOf(',');  //finds location of first ,
      angle = readString.substring(0, ind1);   //captures first data String
      ind2 = readString.indexOf(',', ind1+1 );   //finds location of second ,
      fuel = readString.substring(ind1+1, ind2+1);   //captures second data String
      ind3 = readString.indexOf(',', ind2+1 );
      speed1 = readString.substring(ind2+1, ind3+1);
      ind4 = readString.indexOf(',', ind3+1 );
      altidude = readString.substring(ind3+1); //captures remain part of data after last ,

      Serial.print("angle = ");
      Serial.println(angle); 
      Serial.print("fuel = ");
      Serial.println(fuel);
      Serial.print("speed = ");
      Serial.println(speed1);
      Serial.print("altidude = ");
      Serial.println(altidude);
      Serial.println();
      Serial.println();
      
      readString=""; //clears variable for new input
      angle="";
      fuel="";
      speed1="";
      altidude="";
    }  
    else {     
      readString += c; //makes the string readString
    }
  }
}

UKHeliBob:
Does it return a String or a string ? It looks like the former.

Have a look at Serial input basics to see how to read the serial input into a string (array of chars).

+1
That will allow you to use strtok(). The String class is not a good idea on the Arduino, due to a significant risk of heap fragmentation as memory usage increases beyond the size of a trivial sketch, which will cause random crashes and freezing.

UKHeliBob:
You'd be better off making a function doesn't use a String Object, but just parses the serial input and builds a string directly

Heh...fine..."string" instead of "String."

If I saw someone write code where variable and Variable were different things though, I'd slap them.

But here Arduino is expecting string and String to be super clear to all of the people getting confused.

BigBobby:
But here Arduino is expecting string and String to be super clear to all of the people getting confused.

It's not the fault of the Arduino folks. It is a C/C++ thing.

...R

But here Arduino is expecting string and String to be super clear to all of the people getting confused.

Avoid any confusion by always using strings (lowercase s)

By which standard? The class was "std::string" in the 1998 standard upon which avr-gcc is based. Even as of the 2014 standard they make no mention of string vs String -> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf

Although I rarely use C++ in embedded programming, I have several other compilers that support it. Here's code for IAR Workbench for ARM set to Strict:

#include <iostream>
using namespace std;

int main()
{
    string test;
    String test2;

    return 0;
}

The only error I get is "Error[Pe020]: identifier "String" is undefined".

The first place I've ever seen the convention of "string" vs "String" is the Arduino documentation, and frankly it's stupid. I've only been on these forums a few months and already seen many cases where people were confused. I can't blame them for not picking up on a capitalization convention. I mean really...with that convention, how am I supposed to write a title or start a sentence about "strings?"

with that convention, how am I supposed to write a title or start a sentence about "strings?"

Example :
strings make better use of memory than Strings in the limited memory environment of most Arduinos.

UKHeliBob:
Example :
strings make better use of memory than Strings in the limited memory environment of most Arduinos.

Ok, and we both agree that a convention that can't be discussed without breaking grammar rules is stupid?

Bah...I've sat on standards committees before...if you suggested a convention like this professionally you'd be laughed out of the room for good reason. This forum itself contains many examples as to why!

and another way

/*
  GPS serial read and sentence detection
  Derrived from work by M. Ray Burnette on the PSoC code published on Hackster.io/RayBurne
  Compiler board: Nano
  Arduino 1.7.3 & 1..7.8 // 6/09/2015 & 1/17/2016
    Sketch uses 504 bytes (1%) of program storage space. Maximum is 30,720 bytes.
    Global variables use 16 bytes (0%) of dynamic memory, leaving 2,032 bytes for local variables. Maximum is 2,048 bytes
*/

#include <stdlib.h> // for function atoi()

// Global variables
uint8_t hour, minute, seconds;          //, year, month, day;
uint8_t day, month, year;

char    buffer[9];
char    nmea[120];

// double  atof(const char *str);       // elimited Limor's float implementation

boolean DayLightSavings;                // Pin 3.4
uint8_t GMToffset = 4;
uint8_t Flag = 1;
uint32_t  c = '\0';
int8_t  k   = 0 ;

boolean DSTime_Read(void) {
  return true;
}

void GLCD_WRITE( char *x ) {
}

void glcd_write(void) {
}

void glcd_clear(void) {
}

void glcd_tiny_draw_string(int x, int y, char* c) {
}

char UART_UartGetChar() {
  char i = '\0';
  return i;
}

void LED_Write( boolean PinState) {
}

boolean LED_Read(void) {
  return ~true;
}

void setup(void)
{
    DayLightSavings = DSTime_Read();        // Pin# Normally true (HIGH)

    if(! DayLightSavings) GMToffset += 1;
}

void loop(void)
    {
        if (Flag == 1)
        {
            GLCD_WRITE("Waiting on GPS") ;
            // Flag = 0;
        
        c = UART_UartGetChar();     // Get received character or null
        if (c)  
        {
            if(c == '

)            // $ start of NMEA sentences
            {
                for(k=0; k<5; k++)  // need 5 characters for sentence type
                {
                    LED_Write( ~LED_Read() );  // flicker LED for activity
                    do {
                        c = UART_UartGetChar();
                    }
                    while (! (c));
                    nmea[k] = c;    // G + P + R + M + C
                    // sprintf(buffer + k, "%c", c) ;  // only for debug
                }

LED_Write( LOW );                  // LED off
                if (strstr(nmea, "GPRMC"))
                {
                    do {
                        do {
                            c = UART_UartGetChar();
                            LED_Write( ~LED_Read() ); // flicker LED
                        } while (!(c));
                        nmea[k] = c;
                        ++k;
                    } while ( !( c == '*' ) && k < 120) ;  // marker
                    LED_Write( LOW );                      // LED off
                    // Inspiration: Limor Fried's Arduino GPS lib
                    char *p = nmea;
                    p = strchr(p, ',') + 1;  // position after 1st comma
                    // float    timef  = atof(p);
                    // uint32_t  time  = timef;
                    uint32_t  time  = atoi(p);
                              hour  = time / 10000;
                            minute  = (time % 10000) / 100;
                          seconds  = (time % 100);
                    // output to GLCD
                    sprintf(buffer, "              %s","");  // clearbuffer
                   
                    // this corrects time to the West but not the date!!!
                    if( hour > GMToffset) {
                        hour = hour - GMToffset;
                    } else {
                        hour = (hour + 24) - GMToffset; }

// correct midnight to follow standard format
                    if (hour == 24) hour = 0;

if (hour < 10) {
                        if (DayLightSavings) {
                            sprintf(buffer, "EDT:  %d", hour);
                        } else {
                            sprintf(buffer, "EST:  %d", hour);
                        }
                    } else {
                        if (DayLightSavings) {
                            sprintf(buffer, "EDT: %d%d", hour);
                        } else {                     
                        sprintf(buffer, "EST: %d%d", hour); }
                    }
                    if (minute < 10) {
                        sprintf(buffer + 7, ":0%d", minute);
                    } else {
                        sprintf(buffer + 7, ":%d%d", minute); }
                    if (seconds < 10) {
                        sprintf(buffer + 10,":0%d%s", seconds);
                    } else {
                        sprintf(buffer + 10, ":%d%d%s", seconds); }
                    sprintf(buffer + 13, "%s", "\0");
                    if(Flag == 1)
                    {
                        Flag = 0;
                        glcd_clear() ;
                        delay(250) ;
                    }
                    // OUTPUT the TIME on GLCD
                    glcd_tiny_draw_string(0, 4, buffer);
                    glcd_write() ;
                    // Parse to integer date field
                    p = strchr(p, ',') +1;  // A/V?
                    p = strchr(p, ',') +1;  // lat
                    p = strchr(p, ',') +1;  // N/S?
                    p = strchr(p, ',') +1;  // lon
                    p = strchr(p, ',') +1;  // E/W?
                    p = strchr(p, ',') +1;  // speed
                    p = strchr(p, ',') +1;  // angle
                    p = strchr(p, ',') +1;  // move pass for date DDMMYY
                    // nmea date field looks like: 090914 (European)
                    uint32_t fulldate = atoi(p);
                    day    = (fulldate / 10000);
                    month  = (fulldate % 10000) / 100;
                    year    = (fulldate % 100);
                    sprintf(buffer, "              %s","");  // clearbuffer
                    if (day < 10) {
                        sprintf(buffer, "0%d", day);
                    } else {
                        sprintf(buffer, "%d%d", day); }
                    if (month < 10) {
                        sprintf(buffer + 2,"-0%d", month);
                    } else {
                        sprintf(buffer + 2,"-%d%d", month); }
                    sprintf(buffer + 5, "-20%d%d%s", year);
                    sprintf(buffer + 10,"%s",'\0');
                    // OUTPUT the DATE on GLCD
                    glcd_tiny_draw_string(0, 2, buffer);
                    glcd_write() ;
                }  // if (strstr(nmea, "GPRMC"))
            }      // if(c == '


)
        }           // if(c)
      }             // flag
    }               // loop()


/* [] END OF FILE */

Ok, and we both agree that a convention that can't be discussed without breaking grammar rules is stupid?

If you insist on not breaking grammar rules

Using strings makes better use of memory than Strings in the limited memory environment of most Arduinos.

How about a discussion about an iPhone ?

iPhones are far too expensive

I mean really...with that convention, how am I supposed to write a title or start a sentence about "strings?"

Kind of like blind men describing an elephant. In this programming environment, a "string" usually refers to an ordered set of bytes, "c-strrings" refers to a method of placing the bytes in a memory location with a null byte appended to the byte string to delimit that byte string in that memory location. A "String" refers to a method of storing byte strings in memory locations using a dynamic indexing system for those strings. Note that some of the info put out by some is more opinion than technical fact, or depends on specific conditions to validate that opinion.

UKHeliBob:
If you insist on not breaking grammar rules

Using strings makes better use of memory than Strings in the limited memory environment of most Arduinos.

How about a discussion about an iPhone ?

iPhones are far too expensive

iPhone is a proper noun. Proper nouns are normally capitalized all of the time, but Apple is just cool like that. I imagine the grammar rule would be "use whatever case was used by the person who created the name," but that wasn't a grammar rule I had to worry about when I was in school.

A string is a noun resulting from a naming convention. It would normally follow all of the rules applicable to the capitalization of nouns.

I don't see why we can't just talk about strings like they do in the C++ spec. It's worked for decades. Sure, sometimes people would confuse "strings" with "string objects," but with a sentence or two it'd be cleared up.

Now in this forum, we have threads where people confuse "strings" with "string objects," and people act like saying "string" and "String" back to them is supposed to clear things up. I don't know who decided to try this experiment, but it didn't work.

BigBobby:
Now in this forum, we have threads where people confuse "strings" with "string objects," and people act like saying "string" and "String" back to them is supposed to clear things up. I don't know who decided to try this experiment, but it didn't work.

There are 37.5 angels on the head of my pin.

...R

There are 37.5 angels on the head of my pin.

Are they actually on the pin or are they floating ?