Hi Guys, I am with a problem with serial. I tried to implement a protocol with some commands and a char ´X´ as the end of string marker. Arduino Always replies at least with the string "OKX" The commands are always as bellow;
VERSION=?X ; requesting the version of the code in arduino ,arduino replies with a string
SETTEMP=100,10X ; setting target temp 100 C with a span of 10C, arduino replies with "OKX"
SETTEMP=?X ; requesting the last parameter of settemp, arduino replies with a string
SETDURATION=3000X ; setting max time to control, arduino replies with "OKX"
The problem is that some time it seems that retrieving the data from serial, the last char of the previous command is not removed from serial and the current string has unexpected char and arduino does not recognize the command. Example:
SETTEMP=100,5X , arduino parse this string properly, cleans the inputstring and following if I send SETDURATION=3000X, the string received is "5SETDURATION=3000X"
#include <max6675.h>
/* Definições: GPIOs do Arduino utilizado na comunicação com o
MAX6675 */
#define GPIO_SO 8
#define GPIO_CS 9
#define GPIO_CLK 10
/* Definição: baudrate da comunicação com Serial Monitor */
#define BAUDRATE_SERIAL_MONITOR 9600
/* Criação de objeto para comunicação com termopar */
MAX6675 termopar(GPIO_CLK, GPIO_CS, GPIO_SO);
int Relay_PIN = 4;
// ****************** Variáveis ******************************
boolean showdebug = true; // to control debug message to be displayed
String Version1Str = "RogerThermoK V"; // Version identification
String Version2Str="2.1 ";
String Version3Str="@ 17-Mar-2021 ";
String Version4Str="@ 11:00";
String VersionStr;
boolean stringComplete = false; // whether the string is complete
String inputString = ""; // a String to hold incoming data
unsigned long tempodecorrido;
unsigned long previousMillis ;
bool Running;
float temperatura;
String ParamStr[2] ; // Max 2 parameters allowed
float setpoint;
float setpointspan;
int timeinterval;
unsigned long TimeIni;
unsigned long TimeNow;
float uppertemp;
float downtemp;
boolean relaystate;
long duration;
int timenow;
// ***********************************************************
void ParseSerialData(String StringToParse, String & strCommand, String & strParameter) {
bool ParseError;
if (showdebug) {
Serial.println("Parsing...");
Serial.print("StringToParse : ");
Serial.println(StringToParse);
}
int iPositionCharX = StringToParse.indexOf('X'); // get the possition of the EOS (End Of String char)
int iPositionCharEqual = StringToParse.indexOf('='); // get the position of the separator
int limit = strlen(StringToParse.c_str()); // get the length of the String received
ParseError = false;
if ((iPositionCharX) == -1) // Check if EOS char was received -1 means no received
{
// no EOS FOUND
ParseError = true;
strCommand = "ERROR=1";
if (showdebug) {
Serial.println("Missing EOS char detected.");
}
}
if (not(ParseError)) {
// Check for missing parameters
if (iPositionCharEqual == (iPositionCharX - 1))
{
// Missing parameter
ParseError = true;
strCommand = "ERROR=2";
if (showdebug) {
Serial.println("Missing Paramater detected.");
}
}
if (not(ParseError)) {
// Extract the command and Parammeter
if (iPositionCharEqual > 2) {
strCommand = StringToParse.substring(0, iPositionCharEqual); // extract the Command
strCommand.trim() ; // remove any leading and trailing whitespace
// Extract the Parameter
strParameter = StringToParse.substring(iPositionCharEqual + 1, iPositionCharX);
strParameter.trim() ; // remove any leading and trailing whitespace
}
else {
strCommand = StringToParse.substring(0, iPositionCharX) ; // remove the 'X' char from the string
strCommand.trim() ; // remove any leading and trailing whitespace
strParameter = ""; // There is no parameter, only command received
}
if (showdebug) {
Serial.print("StrCommand :");
Serial.println(strCommand);
Serial.print("strParameter:");
Serial.println(strParameter);
}
}
}
}
void ParseParameters(String StringToParse) {
// Rotina feita para pegar no max 2 parametros
String ParamStr1, ParamStr2;
bool Erro;
Erro = false;
int lenghtstring = StringToParse.length();
int iPositionCharComma = StringToParse.indexOf(',');
if (iPositionCharComma == 0) {
// Erro nesta merda !
Erro = true;
}
if ((iPositionCharComma == -1) && (Erro == false) ) {
StringToParse.trim();
ParamStr[1] = StringToParse;
}
if (iPositionCharComma > 1) {
ParamStr[1] = StringToParse.substring(0, iPositionCharComma);
ParamStr[1].trim();
ParamStr[2] = StringToParse.substring(iPositionCharComma + 1, lenghtstring);
ParamStr[2].trim();
}
}
// --------------------------
void setup() {
// initialize serial:
Serial.begin(BAUDRATE_SERIAL_MONITOR);
inputString.reserve(50); // reserve 50 bytes for the inputString:
if (showdebug) {
Serial.println(VersionStr);
}
// Set relay pin as output
pinMode(Relay_PIN, OUTPUT);
digitalWrite(Relay_PIN, LOW);
relaystate = false;
Running == false;
}
AngeloGomes:
I read it, although I disagree about the Serialevent comment there and I cannot ignore it as my Loop() performs time-consuming tasks.
If you mean that your loop contains long delays, then your serial inputs will be ignored and possibly overflow. That will happen regardless of whether you use C strings or the String class.
The code attached seems to do what you want. Update (see later post for corrected code)
First check out my tutorial on Taming Arduino Strings for how to use Arduino Strings reliably, but not really your problem here.
There are a few problems with your code
i) serialEvent is not interrupt driven as you might expect. Rather is runs just once at the end of each call to loop() if Serial data is available so it is worse then collecting Serial data in your loop code because there you can collect data as often as you like during the loop(). I see by your comments you are aware of this.
ii) your Serial prints will slow your loop down.
a) increase the #define BAUDRATE_SERIAL_MONITOR 115200
b) checkout my tutorial on Arduino Serial I/O for the Real World for how to add a non-blocking Serial output buffer
I have replaced your serialEvent with a readStringUntil method
The advantage of using readStringUntil(input,'X',50) is you can call it multiple times in the loop() to keep checking for input and then call the processing method plus other methods
Just for interest here is a SafeString version of the code.
It uses SafeStringReader to replace all the readuntil code and
has much more robust toFloat() etc numeric conversions
Apart from replacing Strings with SafeStrings the main change to the code is to replace
Arduino String toFloat() (and the low level c-string method it is based on) will just return 0 for invalid floats.
SafeString.toFloat(result) returns false for invalid floats and leaves the result unchanged.