Go Down

Topic: split string by delimiters (Read 118146 times) previous topic - next topic

kalreg

Hello,

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


Code: [Select]
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?

UKHeliBob

Quote
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).
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

BigBobby

#2
Mar 17, 2016, 05:01 pm Last Edit: Mar 17, 2016, 05:09 pm by BigBobby
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:
Code: [Select]

#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:

Quote
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.

UKHeliBob

Quote
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
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

kalreg

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 :)

UKHeliBob

Quote
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
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

zoomkat

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. 

Code: [Select]

//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
    }
  }
}

Google forum search: Use Google Search box in upper right side of this page.
Why I like my 2005 Rio Yellow Honda S2000  https://www.youtube.com/watch?v=pWjMvrkUqX0

aarg

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.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

BigBobby

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.

Robin2

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
Two or three hours spent thinking and reading documentation solves most programming problems.

UKHeliBob

Quote
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)
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

BigBobby

#11
Mar 19, 2016, 05:13 pm Last Edit: Mar 19, 2016, 05:14 pm by BigBobby
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:
Code: [Select]

#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?"

UKHeliBob

Quote
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.
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

BigBobby

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!

mrburnette

and another way

Code: [Select]
/*
  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 */

Go Up