The outptut from my PC to the Arduino via Serial is something like <1023,0,511,1023,0,1032> but about 30 values long.
The following code captures complete packets starting with < and ending with >, and then splits the comma-separated values and stores them in array colours.
I am curious as to how other people do this, is my code ok?, how can it be improved?
//vars for Serial packet recieve
#define SOP '<'
#define EOP '>'
boolean started = false;
boolean ended = false;
char inData[80];
byte index;
//Vars to process packet
int colours[30] = {0};
char * val;
int count = 0;
void loop()
{
while(Serial.available() > 0)
{
char inChar = Serial.read();
if(inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if(inChar == EOP)
{
ended = true;
break;
}
else
{
if(index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if(started && ended)
{
val = strtok (inData,",");
colours[count] = atoi(val);
while ((val = strtok (NULL, ",")) != NULL)
{
colours[++count] = atoi(val);
}
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}
If you have 30 values within < and > then an 80 byte string is too short to hold them all. Assuming that the maximum integer is 4 characters, the whole string can be 152 characters long.
Your code won't blow up if the received string is too long but it throws away everything after the 79th character including the > character, so it won't process the input.
I know a little about the development cycle for code, and the amount of testing that goes into functions delivered in the C library, where strtok() is delivered. Knowing that the code was professionally developed, peer reviewed, and extensively tested, I would not begin to think that I could do a better job, using fewer resources, executing faster. I would, therefore, have used the strtok() function.
A method to capture what is sent then parse out the individual data parts.
//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
}
}
}
i really like your slim solution for this task!
Now i'm trying to do the same thing but with a UDP input. So i combined your code with the UDP example. Unfortunately it seams like switch...case dose not like to work with the char from the packetBuffer. But why? Now i got totally stuck and i need a little hint.
here is the code:
//
// FILE: parsePacket.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: demo
// DATE: 2014-dec-29
// URL:
//
// Released to the public domain
//
#include <SPI.h> // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h> // UDP library from: bjoern@cs.stanford.edu 12/30/2008
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 0, 222);
unsigned int localPort = 8888; // local port to listen on
// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
char ReplyBuffer[] = "acknowledged"; // a string to send back
EthernetUDP Udp; // An EthernetUDP instance to let us send and receive packets over UDP
int field[30];
uint8_t idx = 0;
bool done = false;
void setup()
{
Ethernet.begin(mac, ip);
Udp.begin(localPort);
Serial.begin(9600);
Serial.println("Start ");
}
void loop()
{
int packetSize = Udp.parsePacket();
if (packetSize > 0)
{
Udp.read(packetBuffer, packetSize); // read the packet into packetBuffer
Serial.println();
Serial.print("recieved massage: ");
Serial.println(packetBuffer);
done = parsePacket(packetBuffer);
for(int i=0;i<UDP_TX_PACKET_MAX_SIZE;i++) packetBuffer[i] = 0; //cleer buffer
}
if (done) // ready to process a package
{
Serial.print("fields:\t");
Serial.println(idx);
for (int i = 0; i < idx; i++)
{
Serial.print(i);
Serial.print("\t");
Serial.println(field[i]);
}
Serial.println("done");
}
// do other things here
}
bool parsePacket(int c)
{
bool ready = false;
switch (c)
{
case '0'...'9':
field[idx] = field[idx] * 10 + c - '0';
break;
case ',':
idx++;
field[idx] = 0;
break;
case '<':
idx = 0;
field[idx] = 0;
break;
case '>':
idx++;
ready = true;
break;
default:
; // skip
}
return ready;
}
Take a look at description of Serial.readBytesUntil(character, buffer, length).
Then try this PSEUDOCODE
char MyBuffer[] double array to store packets
int i = 0; starting index to array
while (Serial.available()) while loop when Serial buffer has data
{
read a packet of characters from Serial buffer until character ',' and writes it into MyBuffer , returns count of characters read in packet
Character Count = Serial.readBytesUntil(character, MyBuffer*, length); removes packet read from Serial buffer* i++; read next packet } where character = ',' parsing character buffer = MyBuffer l length > then max length of packet expected PS ignore capital I , it should be lower case - autocorrect at its best
if you know for sure the number of data and the specific format, you can try using sscanf
char str[] = "<1023,0,511,1023,0,1032>"
int first, second, third, fourth, fifth, sixth;
int result = sscanf(str, "<%d,%d,%d,%d,%d,%d>", &first, &second, &third, &fourth, &fifth, &sixth);
//result should be 6 if sscanf successes (because we scan 6 numbers)
Again this code is not robust as you must follow the pattern.