I've a working solution to send and receive floats trough serial with this piece code below.
#include <SPI.h>
#include <Wire.h>
//Envoi/réception de donnée via port série
#define SOP '<'
#define EOP '>'
#define SEP ','
String field[10];
uint8_t idx = 0;
bool done = false;
void setup() {
Serial.begin(9600);
delay(1000);
}
void loop() {
if (Serial.available() > 0) {
done = parsePacket(Serial.read());
}
if (done) {
Serial.println(' ');
Serial.println(field[0]);
Serial.println(field[1]);
Serial.println(field[2]);
Serial.println(field[3]);
Serial.println(field[4]);
}
}
bool parsePacket(char c)
{
bool ready = false;
Serial.write(c);
switch (c)
{
case SEP:
idx++;
field[idx] = ' ';
break;
case SOP:
idx = 0;
field[idx] = ' ';
break;
case EOP:
ready = true;
break;
default:
field[idx] += c ;
; // skip
}
return ready;
}
The data is formatted like this
<2.74,262.05,678.85,10,234>
And each piece of data is correctly parsed in an array of String.
2.74
262.05
678.85
10
234
...
As the sended datas can have various types, I would like to keep a working solution to receive floats, ints or string, and parse each field later on the receiver.
Is there a way to use char array instead of class String but keeping it simple and readable. After many tries I'm not without being able to have a working alternative to String. Also I've leading spaces etc...
if you don't mind awaiting the EOP end marker to start parsing, then you should receive the message in a buffer and then parse. You could use strtok() to find the SEP separators and keep pointers into the buffer for further analysis. of course if you reuse the buffer to receive the next command line, then the pointers will be pointing at garbage (so duplicate the command in that case before parsing)
how will you know about the type of data?
seems you speak french, you can have a look at my tutorial
would lead to something like this
const byte tailleMessageMax = 50;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
const char SOP = '<';
const char EOP = '>';
const char* SEP = ",";
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (Serial.available() && messageEnCours) {
int c = Serial.read();
if (c != -1) {
switch (c) {
case EOP:
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
bool parseMessage()
{
const byte maxDataPoints = 50;
char * dataPtr[maxDataPoints];
byte dataCount = 0;
if (message[0] != SOP) return false;
// sinon on suppose que c'est OK
char * ptr = strtok(&(message[1]), SEP);
while (ptr && (dataCount < maxDataPoints)) {
dataPtr[dataCount++] = ptr;
ptr = strtok(nullptr, SEP);
}
for (byte i = 0; i < dataCount; i++) {
Serial.print(i);
Serial.write('\t');
Serial.println(dataPtr[i]);
}
return true;
}
void setup() {
Serial.begin(115200);
}
void loop() {
if (! ecouter()) {
if (!parseMessage()) Serial.println(F("Message erroné"));
}
}
(with barely any test overflowing the number of data - they will just be ignored)
Great! Exactly what I need, thanks. You've saved my day.
Your proposal is really easy to read and understand. It had successfully parsed my data but only the first read, here was the result:
Are you sending CR/LF from the serial monitor? Try setting the line ending to none.
The code does not handle them, you could just ignore them as they come in.
switch (c) {
case '\r': // ignoré
case '\n': // ignoré
break;
case EOP:
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
You could also search for the < to start parsing instead of checking the first character.
this is where I do the test on the first character: if (message[0] != SOP) return false;
and the I start parsing after the '<', at index 1 char * ptr = strtok(&(message[1]), SEP);
you could use strchr() to check for the presence and position of the '<' and start parsing only from thereon.
No problem at all. I was asking to find a way to work without the class String, because I've read argument to replace it by cstrings. But the readability using Serial.readStringUntil is awesome.
Ah, sorry, I missed that. Perhaps because you said string not String that first time.
There are a lot of forum members who warn about the dangers of Strings. There are others who have written articles and test sketches that seem to demonstrate that they are generally safe, especially if certain things are avoided. Myself, I have decided to keep an open mind until my experiences convince me one way or the other.