Arduino serial data parsing - arduino

I'm writing an app to control my robot with my Android phone over Bluetooth, everything is goes well, data is echoed and verified, but I'm having some trouble with the protocol, specifically I want my robot's wheels to turn when I send a command such as s,10,100 or s,-30,-10... (values in percent).
My problem is that when I want to parse my wheel speed command on my Arduino I must parse from up to 4 separate bytes to int, for example s,-100,-100 makes my robot go backwards at full speed, but how do I parse this so I can call setSpeed(left, right); with leftand right equal to -100?
I know I can separately analyse every byte and put them together to get an integer, but it's not very elegant and there's probably a better solution to all this already, unfortunately I haven't found it yet.
EDIT
Here's my Arduino function for parsing my commands:
void parseCommand(char* command, int* returnValues)
{
// parsing state machine
byte i = 2, j = 0, sign = 0;
int temp = 0;
while(*(command + i) != '\0')
{
switch(*(command + i))
{
case ',':
returnValues[j++] = sign?-temp:temp;
sign = 0;
temp = 0;
break;
case '-':
sign = 1;
break;
default:
temp = temp * 10 + *(command + i) - 48;
}
i++;
}
// set last return value
returnValues[j] = sign?-temp:temp;
}
You call it this way when parsing something like s,100,-100 (must be \0 terminated):
char serialData[16];
void loop()
{
if(Serial.available() > 0)
{
Serial.readBytesUntil('\0', serialData, 15);
switch(serialData[0])
{
case 's':
int speed[2];
parseCommand(serialData, speed);
setSpeed(speed[0], speed[1]);
break;
}
// always echo
Serial.write(serialData);
// end of message is maked with a \0
Serial.print('\0');
// clear serialData array
memset(serialData, 0, sizeof(serialData));
}
}

Just read character by character into a state machine. It's simple and efficient.
To read in a number digit by digit, do this: Start with zero. For each digit, multiply the number by ten and add the value of the digit. So, for example, reading 97 would work like this:
You read in a digit with no prior digit, you start with 0.
You read in 9 and compute (0*10)+9 -> 9
You read in 7 and compute (9*10)+7 -> 97
You read in a non-digit, you output the 97.
Here's a fuller example s,10,100:
You start in the "ready to read command state".
You read "s", "s" is the command. You switch to the "ready to read first comma" state.
You read the first comma, you switch to the "ready to figure out the sign of the first parameter" state.
You read a digit. Since this wasn't a "-", the first parameter is positive. You set the first number to the value of the digit, 1. You are now in "reading first number" state.
You read a digit, 0. You set the first number to 1*10+0 -> 10. You are still in "reading first number" state.
You read a comma. You are now in the "ready to figure out sign of the second parameter" state.
You read 1. The second number is positive (since this wasn't a "-"). You set the second number to 1. You are in the "reading second number" state.
You read 0. The second number is now set to 1x10+0 -> 10. You are still in "reading second number" state.
You read 0. The second number is now set to 10x10+0 -> 100. You are still in "reading second number" state.
You read an end of line. You execute your results: The command is "s", the first number is positive, the first number is 10, the second number is positive, the second number is 100.
You switch back to "ready to read command" state.

I like the answer by David Swartz, but thought I'd play devil's advocate.
Reading the data in as binary can be elegant, it just depends on what you need to do with it.
In the following example, data is read from serial until it sees the binary delimiter 0X7F. The bytes read are stored in the inData char array. Take a look at the documentation for Serial.readBytesUntil()
char inData[16];
int bRead;
bRead = Serial.readBytesUntil(0x7F,inData,4);
This byte can then be cast to an integer or otherwise manipulated. Keep in mind the maximum value for this would be +/-126 because this is a signed char (127 is a delimiter and wouldn't be seen as a value).
You could access these values by with something like the following:
Serial.print("Bytes Read: ");
Serial.println(bRead);
Serial.println("First Byte");
Serial.println((int)inData[0]);
Serial.println(
map((int)inData[0],0,126,0,1024)
);
Serial.println("Second Byte");
Serial.println((int)inData[1]);
I tested this out with the following bash command (after making sure serial speeds were set properly):
echo -ne '\x01\x02\x7F' > /dev/ttyACM0
Some rough sample code that I wrote can be found Here

Related

serial.parseInt() returns a 0 value on its own

I am trying to read some integers from serial, so I used Serial.parseInt() to read two values(0 or 1), the thing is the code runs perfectly for the first time, but then the serial start reading the value 0 on its own, without me sending anything, here is part of the code I am using:
while (!Serial.available()) {}
A = Serial.parseInt();
Serial.println(A);
if (A == 1) {
Book = "";
Serial.println("add tag");
while (Book == "") {
delay(2000);
Book = tagB();
}
Serial.println(Book);
sendToE1("", Book);
delay(2000);
}
}
}
Serial.parseInt returns 0 when it times out with none or invalid characters received.
As you're loop is waiting for available data befor you call Serial.parseInt the only explanation is that you're sending something else but numbers. Most likely a linefeed or carriage return from your terminal program or a println function...
You should probably call Serial.peek to check if the next byte is a number befor you call Serial.parseInt. If you know for sure that you did not send anything but that single integer and some non-numeric character you can also clear the input buffer befor parsing a possible next integer.
But if you're sending an integer and a termination character you might as well just not use Serial.parseInt. Read your data into an array until you receive the termination character. Then convert your data to an integer yourself.
Using Serial.parseInt only makes sense if you know you're about to receive an integer representation within a certain time interval.
You probably need to set the monitor to "No line ending".

Arduino: while (Serial.available()==0) gives input

I am trying to input GPS coordinate into the serial monitor to use in my drone project
However, whenever I try to input GPS coordinate, it automatically writes one of the GPS coordinates without my input. For example, GPS latitude is shown as 0.00, but the program waits for GPS Longitude info.
For a detailed situation please look at the picture attached.
int GPSNumCor;
void setup() {
// put your setup code here, to run once:
Serial.begin (115200);
Serial.print("What is the number of your GPS Coordinate? ");
while (Serial.available() == 0);
GPSNumCor = Serial.parseInt();
Serial.println(GPSNumCor);
delay (200);
float GPSLat[GPSNumCor], GPSLon[GPSNumCor];
for (int i = 0; i < GPSNumCor; i++)
{
if (i == 0)
{
Serial.println("What is your 1st GPS Coordinate");
}
if (i == 1)
{
Serial.println("What is your 2nd GPS Coordinate");
}
if (i == 2)
{
Serial.println("What is your 3rd GPS Coordinate");
}
if (i > 2)
{
Serial.print("What is your ");
Serial.print(i + 1);
Serial.println(" th GPS Coordinate");
}
delay(200);
Serial.print ("Latitude: ");
while (Serial.available() == 0);
GPSLat[i] = Serial.parseFloat();
Serial.println(GPSLat[i]);
Serial.print("Longitude: ");
while (Serial.available() == 0);
GPSLon[i] = Serial.parseFloat();
Serial.println(GPSLon[i]);
}
}
It has to wait for all input until I make an input to the program, but it does not wait.
I know while (Serial.available()==0) is a way to go, but I do not know why it would not work.
First, there's no reason to use while (Serial.available() == 0);. The parseFloat function you are about to use waits for data to be available and, if it didn't, merely checking for zero wouldn't be sufficient anyway because that would stop waiting as soon as a single character was available.
So here's why that while loop is a bad idea:
If you really do need to wait for the input before calling parseFloat, this won't do it. It only waits until at least one character is received and the coordinates may be more than one character.
The parseFloat function doesn't return until it has read an entire float anyway. So it already waits for you.
But that's not your problem. Think about the input stream, say it's "11.0<newline>22.0newline44.0". Where is the code to read the spaces between those numbers? When parseFloat tries to read a space, it returns a zero, as the documentation says. That's why you're getting zeroes -- you don't have any code to do anything with the separators between the floats.
Think about how parseFloat must work when it reads "12.34newline". First it reads the 1 and has no idea whether that's the whole number of not, so it keeps checking. Then it reads the "2.34" and still has no idea it has the whole number. Not until it sees the newline does it know that 12.34 is the correct float to return. But it does not consume the newline. Why? Because that might mean something.
With the code you showed, your next call to parseFloat will then try to read the newline and see that this is not a valid character to be part of a floating point number. So, as the documentation says, it will return zero.
Look closely at parseFloat's documentation to find out how to correctly match the delimiters in your serial stream. The parseFloat function has the ability to behave differently, consuming and ignoring delimeters rather than returning zero.
I don't know how it work, I just add Serial.read() in every time I want to read.
If u don't want to add Serial.read(), u can also use old version like 1.6.0, it still work fine but it don't work when u make like C# Serial app.
Just add Serial.read(), it work fine every place.
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
while(Serial.available()==0){}
int r=Serial.parseInt();
Serial.println(r);
Serial.read(); // it work fine
while(Serial.available()==0){}
int g=Serial.parseInt();
Serial.println(g);
Serial.read();
}
In the Serial Monitor window, in the drop-down menu on the bottom-right, change from "Newline" to "No line ending" and that will solve the problem (by preventing the Serial Monitor from automatically entering zero value(s)).
Both the parseInt() and parseFloat() have a hard time reading other data types (this also includes white spaces such as new lines) than the ones specified, and as a result they automatically return zero.
Reference: This page on Programming electronics offers valuable, detailed explanations (look for a paragraph with bold text):
https://www.programmingelectronics.com/parseint/

Arduino sketch with Serial.available() passes twice

When I test Serial.available() or Serial.available() > 0 in my loop() function, it appears to return true twice each time I enter serial data. The second time, it sets the throttle value in my code to 0. Here is the code:
#include <Servo.h>
Servo rc_rotor;
int throttle = 0; // servo position indicates throttle position.
String s_throttle_set = "Throttle set to: ";
String s_throttle_read = "Reading throttle value: ";
String s_action_error = "No action known for input value: ";
void setup()
{
rc_rotor.attach(9);
Serial.begin(9600);
while(! Serial);
Serial.println("Throttle value: 0 through 255, or 999 to read current value.");
}
void loop()
{
rc_rotor.write(throttle);
delay(20);
if (Serial.available())
{
int temp_throttle = Serial.parseInt();
if (temp_throttle >= 0 && temp_throttle <= 180)
{
throttle = temp_throttle;
Serial.println(s_throttle_set + throttle);
}
else if (temp_throttle == 999)
{
Serial.println(s_throttle_read + throttle);
}
else
{
Serial.println(s_action_error + temp_throttle);
}
}
}
Please note this code is not my final masterpiece. Much of it is from publicly available examples. Anyway, the statement if (Serial.available()) succeeds twice. By that I mean, it is true when I type in a value such as 125, and a moment later it will be 'true' again when I have typed in nothing additional. I only expect one value to go through this way. The result is that my throttle is being set to the value I enter, and then almost immediately re-set to 0. Why would something like this happen?
It turns out there is no mysterious problem with the hardware or the code, as I first suspected there was. Actually, the solution is simply to select "no line ending" in the Arduino Serial Monitor's dropdown option (by default, I guess mine was set to "New Line"). Without the additional character being inserted by the Serial Monitor, everything behaves as expected.
One thing I did not expect is how the Arduino software interprets the newline. I debugged by printing the ascii values that were making it through my if-statement. First of all, the Serial Monitor sent the number I typed in, followed moments later by ascii 10, which is the line feed character. Fine, no problem. But then Serial.parseInt() chewed on that line feed for a moment (there was a slight but noticeable delay), then fed the numeral 0 to my function. It took me a little while to figure out why. Here is an explanation from the Serial part of the Arduino Language Reference:
parseInt()
Description
Looks for the next valid integer in the incoming serial stream.
parseInt() inherits from the Stream utility class.
In particular:
Initial characters that are not digits or a minus sign, are skipped;
Parsing stops when no characters have been read for a configurable
time-out value, or a non-digit is read;
If no valid digits were read
when the time-out (see Serial.setTimeout()) occurs, 0 is returned;
So Serial.available() is true after the line feed character enters the buffer, but there is no "valid digit" according to Serial.parseInt(). Understandably... we are looking at an empty buffer that finally times out. Therefore Serial.parseInt() returns 0 and the code that follows proceeds with that value.
The code in the question assumed that the only input would be integers coming over the serial connection, which is actually a pretty fragile assumption. If you need to use Serial.parseInt in a situation where empty buffers (null), line feeds or other unexpected characters might come through, it should just be a matter of filtering the input better than I did in the example code.

Parse multiple Integers recieved via serial - Arduino

I want to parse two integers that are sent from one arduino to another. The integers can be between 1 and 3 digits and they can be the same but still I must be able to tell which one is which.
Tried to search the already answerd questions regarding this but cannot find any good answear.
I have tried with this on the reciever (I print the inputstring2 on a LCD screen where I also reset the Inputstring2):
if(incomingByte == 'b'){
incomingByte = Serial.read();
while(incomingByte >= '0' && incomingByte <= '9'){
inputString2 += incomingByte;
incomingByte = Serial.read();
}
stringComplete2= true;//
The error is that I sometimes get only one of the digits if the integer is 25 I only get 2...
Sender:
Serial1.print('a');
Serial1.print(temp2);
Serial1.print('b');
Serial1.print(encoderValue);
Serial1.print('n');
When you send the value, also send a "tail character" to indicate to the receiver that the data characters has no more characters, i.e.:
Serial1.print('a');
Serial1.print(temp2);
Serial1.print('a');
Serial1.print('b');
Serial1.print(encoderValue);
Serial1.print('b');
In this way you enclose the data in a packet (a data a), so on the receiving end you test for the start of the packet, then read the remaining characters until you read the end of the packet.

scanning a float, getting seemingly random values

I was given an assignment to create a procedure that scans a float, called getfloat.
for some reason, I am getting random values. If I enter "1" it prints 49.Why does this happen? And also, when i input values, I can't see them on the screen? when I use scanf for example i see what i hit, on the little black screen. but now the screen is just blank, and when i click enter it shows a bad output:
Example - input: -1. Output: 499.00000
Here is my code:
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <ctype.h>
void getfloat(float* num);
void main()
{
float num=0;
printf("Enter the float\n");
getfloat(&num);
printf("\nThe number is %lf\n",num);
getch();
}
void getfloat(float* num)
{
float c,sign=1,exponent=10;
c=getch();
if((!isdigit(c))&&(c!='+')&&(c!='-')) //if it doesnt start with a number a + or a -, its not a valid input
{
printf("Not a number\n");
return;
}
if(c=='-') //if it starts with a minus, make sign negative one, later multiply our number by sign
sign=-1;
for(*num=0;isdigit(c);c=getch())
*num=(*num*10)+c; //scan the whole part of the number
if(c!='.') //if after scanning whole part, c isnt a dot, we finished
return;
do //if it is a dot, scan fraction part
{
c=getch();
if(isdigit(c))
{
*num+=c/exponent;
exponent*=10;
}
}while(isdigit(c));
*num*=sign;
}
There are a number of issues.
1) Your posted code does not match your example "input: -1. Output: 499.00000", I get 0 due the lack of a getch() after finding a '-'. See #6.
1) 'c' is a character. When you enter '1', c took on a code for the letter 1, which in your case being ASCII coding, is 49. To convert a digit from its ASCII value to a number value, subtract 48 (the ASCII code for the letter '0', often done as c - '0'
*num=(*num*10)+c;
*num+=c/exponent;
becomes
*num = (*num*10) + (c-'0');
*num += (c-'0')/exponent;
2) Although you declare c as a float, recommend you declare it as an int. int is the return type from getch().
3) Function getch() is "used to get a character from console but does not echo to the screen". That is why you do not see them. Consider getchar() instead.
4) [Edit: delete Avoid =-. Thank-you #Daniel Fischer]
5) Your exponential calculation needs rework. Note: your exponent could receive a sign character.
6) When you test if(c=='-'), you do not then fetch another c. You also might want to test for else if(c=='+') and consume that c.
Good luck in your C journey.
49 is the Ascii code for the number 1. So when (0'<=c && c <='9') you need to subtract '0' to get the number itself.
A small hint: 49 is the ASCII for the character 1. You are using getch(), which gives you the return value char.

Resources