Reputation: 149
Arduino (C language) parsing string with delimiter (input through serial interface)
Didn't find the answer here :/
I want to send to my arduino through a serial interface (Serial.read()) a simple string of three numbers delimited with comma. Those three numbers could be of range 0-255.
Eg.
255,255,255
0,0,0
1,20,100
90,200,3
What I need to do is to parse this string sent to arduino to three integers (let's say r, g and b).
So when I send 100,50,30 arduino will translate it to
int r = 100
int g = 50
int b = 30
I tried lots of codes, but none of them worked. The main problem is to translate string (bunch of chars) to integer. I figured out that there will probably be strtok_r for delimiter purpose, but that's about it.
Thanks for any suggestions :)
Upvotes: 14
Views: 82030
Reputation: 31
The new SafeString Arduino library (available via the library manager) provides an stoken() method and a toLong() method which handles this case and avoids the heap fragmenation problems of the String class.
see
https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
for a detailed tutorial
#include "SafeString.h"
void setup() {
Serial.begin(9600);
createSafeString(appCmd, 50); // large enough for the largest cmd
createSafeString(token1, 10);
createSafeString(token2, 10);
createSafeString(token3, 10);
long r;
long g;
long b;
appCmd = "1,20a,100";
token1.clear();token2.clear();token3.clear(); // clear any old data
size_t nextIdx = 0;
nextIdx = appCmd.stoken(token1, nextIdx, ",");
nextIdx++; //step over delimiter
nextIdx = appCmd.stoken(token2, nextIdx, ",");
nextIdx++; //step over delimiter
nextIdx = appCmd.stoken(token3, nextIdx, ",");
nextIdx++; //step over delimiter
// now parse the numbers
bool have3ValidNumbers = true;
if (!token1.toLong(r)) {
have3ValidNumbers = false;
Serial.print("Red number invalid:");Serial.println(token1);
}
if (!token2.toLong(g)) {
have3ValidNumbers = false;
Serial.print("Green number invalid:");Serial.println(token2);
}
if (!token3.toLong(b)) {
have3ValidNumbers = false;
Serial.print("Blue number invalid:");Serial.println(token3);
}
if (have3ValidNumbers) {
Serial.print("The values are ");
Serial.print(" r:");Serial.print(r);
Serial.print(" g:");Serial.print(g);
Serial.print(" b:");Serial.print(b);
Serial.println();
}
}
void loop() {
}
The output for this input "1,20a,100" is
Green number invalid:20a
The 'standard' toInt() method would have returned 1 20 100 as the result.
For an input like "1,a,50" the 'standard' toInt() method would return 1 0 100
The SafeString toLong() method does more error checking when attempting to convert a string to an integer.
You should also add checks for <0 and >255 to ensure the input is valid range
Upvotes: 0
Reputation: 1730
For n number delimited in string
int end;
while((end=str.indexOf(","))!=-1){
String num = str.substring(0,end);
str= asc.substring(end+1,str.length());
Serial.println(num);
}
Upvotes: 0
Reputation: 969
To answer the question you actually asked, String objects are very powerful and they can do exactly what you ask. If you limit your parsing rules directly from the input, your code becomes less flexible, less reusable, and slightly convoluted.
Strings have a method called indexOf() which allows you to search for the index in the String's character array of a particular character. If the character is not found, the method should return -1. A second parameter can be added to the function call to indicate a starting point for the search. In your case, since your delimiters are commas, you would call:
int commaIndex = myString.indexOf(',');
// Search for the next comma just after the first
int secondCommaIndex = myString.indexOf(',', commaIndex + 1);
Then you could use that index to create a substring using the String class's substring() method. This returns a new String beginning at a particular starting index, and ending just before a second index (Or the end of a file if none is given). So you would type something akin to:
String firstValue = myString.substring(0, commaIndex);
String secondValue = myString.substring(commaIndex + 1, secondCommaIndex);
String thirdValue = myString.substring(secondCommaIndex + 1); // To the end of the string
Finally, the integer values can be retrieved using the String class's undocumented method, toInt():
int r = firstValue.toInt();
int g = secondValue.toInt();
int b = thirdValue.toInt();
More information on the String object and its various methods can be found int the Arduino documentation.
Upvotes: 27
Reputation: 1
String myString = "dfsdfgsafhffgsdvbsdvdsvsdvsdsdfdfsdsff|date|recipt|weight|time|date|";
// the setup routine runs once when you press reset:
void setup() {
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
int Index1 = myString.indexOf('|');
int Index2 = myString.indexOf('|', Index1+1);
int Index3 = myString.indexOf('|', Index2+1);
int Index4 = myString.indexOf('|', Index3+1);
int Index5 = myString.indexOf('|', Index4+1);
int Index6 = myString.indexOf('|', Index5+1);
String secondValue = myString.substring(Index1+1, Index2);
String thirdValue = myString.substring(Index2+1, Index3);
String fourthValue = myString.substring(Index3+1, Index4);
String fifthValue = myString.substring(Index4+1, Index5);
String firstValue = myString.substring(Index5+1, Index6);
//Serial.println(Index1);
//
Serial.println(secondValue);
Serial.println(thirdValue);
Serial.println(fourthValue);
Serial.println(fifthValue);
Serial.println(firstValue);
delay(14000);
}
Upvotes: -3
Reputation: 573
Use sscanf
;
const char *str = "1,20,100"; // assume this string is result read from serial
int r, g, b;
if (sscanf(str, "%d,%d,%d", &r, &g, &b) == 3) {
// do something with r, g, b
}
Use my code here if you want to parse a stream of string ex: 255,255,255 0,0,0 1,20,100 90,200,3
Parsing function for comma-delimited string
Upvotes: 12
Reputation: 171
Simplest, I think, is using parseInt()
to do this task:
void loop(){
if (Serial.available() > 0){
int r = Serial.parseInt();
int g = Serial.parseInt();
int b = Serial.parseInt();
}
}
does the trick.
Upvotes: 2
Reputation: 31
This is great!
The last comment about "thirdvalue = 0" is true from the code given in the most upvoted response by @dsnettleton. However, instead of using "lastIndexOf(',');" , the code should just add a "+1" to "secondCommaIndex" like @dsnettleton correctly did for commaIndex+1 (missing +1 is probably just a typo from the guy).
Here is the updated piece of code
int commaIndex = myString.indexOf(',');
int secondCommaIndex = myString.indexOf(',', commaIndex+1);
String firstValue = myString.substring(0, commaIndex);
String secondValue = myString.substring(commaIndex+1, secondCommaIndex);
String thirdValue = myString.substring(secondCommaIndex+1); //To the end of the string
Example)
For a myString = "1,2,3"
Note: Don't confuse INDEX with the LENGTH of the string. The length of the string is 5. Since the String indexOf counts starting from 0, the last index is 4.
The reason why just
String thirdValue = myString.substring(secondCommaIndex);
returns 0 when using .toInt() is because thirdValue = ",3" and not "3" which screws up toInt().
ps. sorry to write all the instructions out but as a mech eng, even I sometimes would like someone to dumb down code for me especially having been in consulting for the past 7 years. Keep up the awesome posting! Helps people like me out a lot!
Upvotes: 1
Reputation: 1
@cstrutton -Excellent suggestion on using 'indexOf' . it saved me a ton of time for my project. One minor pointer though,
I noticed the thirdvalue did not get displayed (was coming back as ZERO). Upon playing with it little-bit and going through the doc at http://arduino.cc/en/Tutorial/StringIndexOf I realized, I can use lastIndexOf for the last value.
Here are two lines of modifications that provided correct third value.
int lastCommaIndex = myString.lastIndexOf(',');
String thirdValue = myString.substring(lastCommaIndex+1); // To the end of the string
Upvotes: -3
Reputation: 6197
I think you want to do something like this to read in the data:
String serialDataIn;
String data[3];
int counter;
int inbyte;
void setup(){
Serial.begin(9600);
counter = 0;
serialDataIn = String("");
}
void loop()
{
if(serial.available){
inbyte = Serial.read();
if(inbyte >= '0' & inbyte <= '9')
serialDataIn += inbyte;
if (inbyte == ','){ // Handle delimiter
data[counter] = String(serialDataIn);
serialDataIn = String("");
counter = counter + 1;
}
if(inbyte == '\r'){ // end of line
handle end of line a do something with data
}
}
}
Then use atoi() to convert the data to integers and use them.
Upvotes: 1