Parsing a string to obtain mixed values

Dear List,
I am using TCPIP to send a string to my Arduino YUN using a socket.

The basic code is within a loop and looks like this:

 String string = "";
    while(yclient.connected()){        // this loop will continue whilts a connection is active
      if(yclient.available()){
       char received = yclient.read();    // this will read a single byte and returns -1 if nothing to read
    
        if(received != '\n' && received != 4){
          string += received
        }

This leaves me with a string called string.

I would like to parse this string to obtain integer or string variables. For example, if I sent the following:

"Hello;1,2,3,4\n"

I would like to separate each part to get, as in this case, one string and 4 integer values.

Can anyone point me towards a nice way of doing this.
If it is simpler, I can only send integer or float values and avoid the string data.

Thanks,
Nick

This leaves me with a string called string.

Actually it leaves you with a String called string which is not the same thing at all.

If it were a C style string (a char array terminated with a null) then I would suggest that you use strtok() to parse it into its elements and atio() to convert parts of it to integers . See Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking for how to collect the incoming bytes in a string.

There are strong feelings here as to whether Strings should be used on a microcontroller with limited RAM and strings are regarded by most as a better alternative so expect different opinions on the subject.

NickH:

 String string = "";

while(yclient.connected()){        // this loop will continue whilts a connection is active
      if(yclient.available()){
       char received = yclient.read();    // this will read a single byte and returns -1 if nothing to read
   
        if(received != '\n' && received != 4){
          string += received
        }

Ask yourself "Will received ever equal 4, and if so, does it make any difference at all to what we want to do within the if?"
One thing you don't check, though you seem to know why you should check it, is whether or not you actually read anything.

This leaves me with a string called string.

Almost correct. It leaves you witha String called string. A String (an object) is not a string (an array of char).
A String does have some nice things about it, but it will pay you to learn how to use strings (char arrays), as Strings use lots of memory and fragment it badly.

I would like to parse this string to obtain integer or string variables. For example, if I sent the following:

"Hello;1,2,3,4\n"

I would like to separate each part to get, as in this case, one string and 4 integer values.
If it is simpler, I can only send integer or float values and avoid the string data.

I can't help you with Strings, but if you decide to use char arrays, you can look up strtok() to split the string into parts, delimited by whatever characters you want. Then atoi() for converting strings to ints.

The best way to that is drawing a data structure like this (from waht you described)

A receive string is a number of values followed by a a newline
a value is the value itself followed by a delimiter and som optional whitespace
the value itself is eiter a string value or a integer value
a string value is a number of characters up to a delelimiter
a integer value is a optinal sign followed by a numer of digits
a delimter is either a ',' or a ';'
whitespace is usually space or tab

Upon this data structure you build a program structure with the usual if(alternatives) and while(iterations) statements

Thank you for the replies.....

I need to be careful with my String and string. Point taken.

I have made some changes. If I use a char array instead of string, then I can come up with the following function which loads up variables when called:

int whichFunc;
int arg1;
int arg2;
int arg3;


void ParseChar(char mystr[])
{
  char *str;
  char *p = mystr;
  int loopcount = 0;
  
  while (str = strtok (p, ",")) 
      {
       if (loopcount == 0){
         whichFunc = atoi(str);
         Serial.print("Here we have: ");
         Serial.println(whichFunc);
       }
       if (loopcount == 1){
         arg1 = atoi(str);
         Serial.print("Here we have: ");
         Serial.println(arg1);
       }
       if (loopcount == 2){
         arg2 = atoi(str);
         Serial.print("Here we have: ");
         Serial.println(arg2);
       }
       if (loopcount == 3){
         arg3 = atoi(str);
         Serial.print("Here we have: ");
         Serial.println(arg3);
       }
       p = NULL;
       loopcount += 1;
    }
}

So this works when the function is called from my main loop when the new line character is recognized. It seems a bit inelegant to me.

Alternatives that I came across are:

String ReadStr = yclient.readStringUntil('\n');  //  this will continue until the terminator specified is reached
int value = yclient.parseInt();  // this will read any integer from whatever was sent

These seem much simpler approaches if I stick to integer values but I think the downside is that the first one will block execution of my main loop until a new line character is read. The second one will work but again I will not know when the information is received.

I would be interested to learn if these are serious issues and what is considered good programming. When I send commands from my command program to the arduino, they will be sent in one go and so I imagine that the time overhead would be relatively small.

Something I have noticed is that the YUN has less memory than an UNO and so saving memory is likely to be important.

BW
Nick

arg1, arg2, arg3

Sounds like that should be an array to me, and look at the example on this page http://www.cplusplus.com/reference/cstring/strtok/ to see how to get a series of values using strtok() in a while loop. Add a counter inside the while loop and use it as an index to an array in order to save the variables extracted from the string.

I need to be careful with my String and string. Point taken.

Another point to be taken is that you need to use the string operation (String or c-string) that you best understand. The below page has a good listing of String operations available. Bottom is some old server code that uses the String functions to do various operations.

//zoomkat 5-24-10

#include <WString.h>
#include <Ethernet.h>

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 
  192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 
  192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask
Server server(84); //server port

String readString = String(100); //string for fetching data from address

///////////////////////
String teststring = String(100);
String finalstring = String(100);
String servo1 = String(6);
String servo2 = String(6);
int ind1 = 0;
int ind2 = 0;
int pos = 0;
//////////////////////

void setup(){

  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();

  //enable serial data print 
  Serial.begin(9600); 
  Serial.println(bot2);
}

void loop(){
  // Create a client connection
  Client client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString.append(c); 
        } 

        //if HTTP request has ended
        if (c == '\n') {

          /////////////
          //http://192.168.1.102:84/?-0p2000t1000-1p1500s1000
          //Serial.println(readString);
          //readString looks like "GET /?-0p1555-1p500t1000 HTTP/1.1"

          if(readString.contains("-")) { //test for servo control sring
            readString.replace('-', '#');
            pos = readString.length(); //capture string length
            //find start of servo command string (#)
            ind1 = readString.indexOf('-');
            //capture front part of command string
            teststring = readString.substring(ind1, pos);
            //locate the end of the command string
            ind2 = teststring.indexOf(' ');
            //capturing the servo command string from readString
            finalstring = readString.substring(ind1, ind2+ind1);
            //print "finalstring" to com port;
            Serial.println(finalstring); //print string with CR

            servo1 = finalstring.substring(ind1+1, 5);
            servo2 = finalstring.substring(ind1+6, ind2);
            Serial.println(servo1);
            Serial.println(servo2);





          }

          ////////////////////
          //GET /?Slidervalue0=1800&Submit=Sub+0 HTTP/1.1
          if(readString.contains("Slidervalue")) {
            ind1 = readString.indexOf('u');
            ind2 = readString.indexOf('&');
            finalstring = readString.substring(ind1+1, ind2);
            finalstring.replace('e', '#');
            finalstring.replace('=', 'p');
            Serial.println(finalstring);
          }
          ///////////////////

          //now output HTML data header
          client.println("HTTP/1.1 204 Zoomkat");
          client.println();
          client.println();
          delay(1);
          //stopping client
          client.stop();

          /////////////////////
          //clearing string for next read
          readString="";
          teststring="";
          finalstring="";

        }
      }
    }
  }
}