Skip to content

Instantly share code, notes, and snippets.

@mattfelsen
Last active February 2, 2023 22:28
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save mattfelsen/9467420 to your computer and use it in GitHub Desktop.
Save mattfelsen/9467420 to your computer and use it in GitHub Desktop.
Splitting strings by a delimiter for Arduino
//
// This is tested and works!
//
String input = "123,456";
int firstVal, secondVal;
for (int i = 0; i < input.length(); i++) {
if (input.substring(i, i+1) == ",") {
firstVal = input.substring(0, i).toInt();
secondVal = input.substring(i+1).toInt();
break;
}
}
//
// I tested a similar version in JS and it worked
// No guarantees!!
//
// Define number of pieces
const int numberOfPieces = 4;
String pieces[numberOfPieces];
// This will be the buffered string from Serial.read()
// up until you hit a \n
// Should look something like "123,456,789,0"
String input = "";
// Keep track of current position in array
int counter = 0;
// Keep track of the last comma so we know where to start the substring
int lastIndex = 0;
void setup(){
Serial.begin(9600);
}
void loop() {
// Check for data coming in from serial
if (Serial.available() > 0) {
// Read the first byte and store it as a char
char ch = Serial.read();
// Do all the processing here since this is the end of a line
if (ch == '\n') {
for (int i = 0; i < input.length(); i++) {
// Loop through each character and check if it's a comma
if (input.substring(i, i+1) == ",") {
// Grab the piece from the last index up to the current position and store it
pieces[counter] = input.substring(lastIndex, i);
// Update the last position and add 1, so it starts from the next character
lastIndex = i + 1;
// Increase the position in the array that we store into
counter++;
}
// If we're at the end of the string (no more commas to stop us)
if (i == input.length() - 1) {
// Grab the last part of the string from the lastIndex to the end
pieces[counter] = input.substring(lastIndex, i);
}
}
// Clear out string and counters to get ready for the next incoming string
input = "";
counter = 0;
lastIndex = 0;
}
else {
//if we havent reached a newline character yet, add the current character to the string
input += ch;
}
}
// Data is now available in pieces array
// pieces[0] is first item
// pieces[1] is second item, and so on
// You can call toInt() on the data to convert it to an int
// ex. int value = pieces[0].toInt();
}
@adielfernandez
Copy link

Partially works but it got me on the right track to get it working really well, so thanks! I made a few edits (some below) and packaged it together with other code and sent it out to the world.

-The last if statement needs to be in the for loop
-lastIndex and counter need to be reset for the next string
-Arduino doesnt allow declarations of arrays with variables for the length (i.e. String pieces[numberOfPieces] is now String pieces[4])
-I also changed the pieces String array to a long array and called toInt() on the substring.

Thanks again!

@mattfelsen
Copy link
Author

Thanks for putting together a working example for everyone! Good catches all around

  • Tthe last if statement worked in JS but now I see that i goes out of scope for C
  • Why move away from a String array? It'd be useful to be able to send strings like "1,48,bob" you know? Keep everything as Strings and then call .toInt() and things you need?

@adielfernandez
Copy link

Thanks! "1,48,bob" Yessss, thats totally true. Then you could send "coded" variables so the arduino knows to change a specific one instead of constantly streaming variables that might not need changing all the time. Good idea.

That if statement works in JS?? JS is weird...

@mattfelsen
Copy link
Author

Yes, JS doesn't have block-level scope, just global & function. Once the for loop exits, i retains its value until the end of the loop() function and then is destroyed. Maybe I should put up a site for doesjavascripthaveblocklevelscope.com…

Also, sure, you can codify useful, semantic string values into int values, but you could also not.

@balajyothi
Copy link

Hi JS, the above code is well working for number to split like 123,456.

But I have 10 bytes to be splitted,can you please suggest how it can be done with arduino.

@farshid616
Copy link

Thanks For this code, it's handy but i found a bug and i solved it.

  • Last pieces of splitted string missed last character. pieces[counter] = input.substring(lastIndex, i); in line 65 i must changed by input.length()

@mickeypop
Copy link

This has always worked for me

// String  var = getValue( StringVar, ',', 2); // if  a,4,D,r  would return D        
String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length();

    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}  // END

@PululuK
Copy link

PululuK commented Feb 26, 2019

@mickeypop work for me ! Thank you

@lailaKho753
Copy link

@mickeypop how if the data is more than two?

@mickeypop
Copy link

@lailaKho753
i'm not sure i understand your question.

the 2 is just an index to the array position i want returned. it could be any position within the string.

@agarcia393
Copy link

agarcia393 commented Mar 14, 2020

I'm really not sure how to use your code but i'm not getting anything but zero
I'm trying to read Serial like this >>> "542354,5234,2345,"
Any help will be awesome Thank You,

/// robot_1 aka car 1
#include <SoftwareSerial.h>
#include <Servo.h>//Loads commands to create Servo objects which generate PWM signals
#include <Adafruit_LSM303DLH_Mag.h>
#include <Adafruit_Sensor.h>


Servo leftDrive;  // create servo object to control a servo
Servo rightDrive; //another servo object for the left side
SoftwareSerial xbee(2, 3); //RX,TX for xbee conection


// Define number of pieces
const int numberOfPieces = 4;
String pieces[numberOfPieces];

// This will be the buffered string from Serial.read()
// up until you hit a \n
// Should look something like "123,456,789,0"
String input = "123,456,789,0";


// Keep track of current position in array
int counter = 0;

// Keep track of the last comma so we know where to start the substring
int lastIndex = 0;

void setup() {
  leftDrive.attach(6);  // attaches the servo on pin 9 to the servo object
  rightDrive.attach(5);  // attaches the servo on pin 9 to the servo object
  Serial.begin(9600);
  Serial.println("Initializing... \n");
      xbee.begin(9600);

}

void loop() {
  
  // Check for data coming in from serial
  if (Serial.available() >= 0) {
    // Read the first byte and store it as a char
    char ch = Serial.read();
     //String input = Serial.readString();
    // Do all the processing here since this is the end of a line
    if (ch == '\n') {
      for (int i = 0; i < input.length(); i++) {
        // Loop through each character and check if it's a comma
        if (input.substring(i, i + 1) == ",") {
          // Grab the piece from the last index up to the current position and store it
          pieces[counter] = input.substring(lastIndex, i);
          // Update the last position and add 1, so it starts from the next character
          lastIndex = i + 1;
          // Increase the position in the array that we store into
          counter++;
        }

        // If we're at the end of the string (no more commas to stop us)
        if (i == input.length() - 1) {
          // Grab the last part of the string from the lastIndex to the end
          pieces[counter] = input.substring(lastIndex, i);
        }
      }


      { // Code stuff for car
        ///////////////////////////////////////////////////////
        // Do other commands for car and stufff
        //Adafruit_LSM303DLH_Mag_Unified mag = Adafruit_LSM303DLH_Mag_Unified(12345);
        //sensors_event_t event;
        //        mag.getEvent(&event);
        //        float Pi = 3.14159;
        //        float heading = (atan2(event.magnetic.y, event.magnetic.x) * 180) / Pi;
        //        // Normalize to 0-360
        //        if (heading < 0) {
        //         heading = 360 + heading;
        //             }

        //leftDrive.write(pieces[0].toInt());
        //rightDrive.write(pieces[1].toInt());

//        int value = pieces[0].toInt();
//         Serial.write(value);
//        Serial.print(".............");
int value = pieces[0].toInt();
//Serial.println(value);
int value1 = pieces[4].toInt();
Serial.println(value1);



      }


      // Clear out string and counters to get ready for the next incoming string
      input = "";
      counter = 0;
      lastIndex = 0;
    }
    else {

      //if we havent reached a newline character yet, add the current character to the string
      input += ch;
    }

    

  }




}

@drmpf
Copy link

drmpf commented Jun 16, 2020

The new SafeString Arduino library, available from the Arduino Library Manager and from
https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
provides a number of tokenizing methods and accurate string to number conversions.
SafeStrings are easy to debug. SafeStrings provide detailed error messages, including the name of SafeString in question, to help you get your program running correctly.
SafeStrings are safe and robust. SafeStrings never cause reboots and are always in a valid usable state, even if your code passes null pointers or '\0' arguments or exceeds the available capacity of the SafeString.
SafeString programs run forever. SafeStrings completely avoid memory fragmentation which will eventually cause your program to fail and never makes extra copies when passed as arguments.
SafeStrings are faster. SafeStrings do not create multiple copies or short lived objects nor do they do unnecessary copying of the data.

There are a number of examples included with the library. Here is the trivial one for parsing user input to extract keywords and ignore the rest without blocking the rest of the sketch from running.

void loop() {
  input.read(Serial);

  if (input.nextToken(token, delimiters)) { // process at most one token per loop

    if (token == startCmdStr) {
      running = true;  Serial.print(F("start at ")); Serial.println(loopCounter);

    } else if (token == stopCmdStr) {
      running = false; Serial.print(F("stop at ")); Serial.println(loopCounter);

    } else if (token == resetCmdStr) {
      loopCounter = 0; Serial.print(F("reset Counter:")); Serial.println(loopCounter);

    }// else  // not a valid cmd ignore
  }

  // rest of code here is executed while the user typing in commands
  if (running) {
    loopCounter++;
    if ((loopCounter % 100000) == 0) {
      Serial.print(F("Counter:")); Serial.println(loopCounter);
    }
  }
}

The SafeString string to number conversion methods fix a number of problems. The usual Arduino conversions will returns 5 for the string "5a" while SafeString returns false since 5a is not a valid number. See the included library example sketch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment