Correct: see https://www.arduino.cc/reference/en/language/functions/communication/serial/settimeout/
What if my command is too long and readString() can't finish reading them before it timeout?
Timeout in general defines the time how long a routine waits when nothing happens (like: No character received, no Acknowledge received, ...). If in this case the transmitting part or the transmission path (just wire in this case, but think of the internet) delays/pauses for too long, the timeout will avoid that your application gets stuck. Even if it was different (what it is not) and as the timeout defaults to 1000 ms, even with 9600 Bd you may send around 960 characters during that time ... Go to 115200 Bd and you may transmit about 11.520 characters/sec.
When you asked for "space" you might have a look at
https://www.asciitable.com/
All data from 0 to 255 can be transmitted by Serial. Some have a defined role (control characters, numbers, letters, graphic elements), most of it coming from the good old time of teletype writers and V24 terminals. Not all of the characters will be visible and only a subset of them will be displayed as shown in the table. You can take advantage of the definitions e.g. by using some of the today mostly unused control characters for your own purposes.
Here is a simple example how one may set up the Serial command server, just take from it what you like ... It does not check for all possible errors but may give you some hints ...
/* ec2021
Skeleton of a Simple Serial Command Server Application
*/
const String c_version = "Command Server Skeleton V1.0";
const int MaxCmdLen = 40;
String cmd = "";
int Direction = 0;
void PrintHelp() {
Serial.println(F("-------------------------"));
Serial.println(c_version);
Serial.println(F("-------------------------"));
Serial.println(F("? for Help"));
Serial.println(F("Axxx Call routine A with value 0 <= xxx <= 999"));
Serial.println(F("Bxx Call routine B with value 0 <= xx <= 99"));
Serial.println(F("Dx Set Direction value = [0,3,6,9] clockwise"));
Serial.println(F("F Set direction to FORWARD"));
Serial.println(F("Px Set PWM to power level x with 0<= x <= 9"));
Serial.println(F("FORWARD Set direction to FORWARD"));
Serial.println(F("RIGHT Set direction to RIGHT"));
Serial.println(F("BACKWARD Set direction to BACKWARD"));
Serial.println(F("LEFT Set direction to LEFT"));
Serial.println(F("-------------------------"));
}
boolean ValueIsValid(String ValueString, int minimum, int maximum) {
if (ValueString.length() < 1) {
Serial.println(F("No Data"));
return false;
}
char c = ValueString.charAt(0);
if ((c == '-' || c == '+') && (ValueString.length() > 1)) c = ValueString.charAt(1); // If it is not a digit, it may by a plus or minus
if (!isDigit(c)) {
Serial.println(F("ERROR: Not a Digit"));
return false;
}
// Any rubbish after the digit is neglected, just checking if there is a floating point
if (ValueString.indexOf(".") > 0) {
Serial.println(F("ERROR: Floating Point Value not allowed here"));
return false;
}
if (ValueString.toInt() < minimum) {
Serial.print(F("ERROR: Below Minimum of "));
Serial.println(minimum);
return false;
};
if (ValueString.toInt() > maximum) {
Serial.print(F("ERROR: Above Maximum of "));
Serial.println(maximum);
return false;
};
return true;
}
void ProcedureA(String Data) {
Serial.print(F("You called A with "));
if (Data.length() > 0) {
Serial.println(Data);
}
else {
Serial.println(F("no Data"));
return;
};
if (ValueIsValid(Data, 0, 999)) {
Serial.print(F("Data has been evaluated as : "));
Serial.println(Data.toInt());
}
}
void ProcedureB(String Data) {
Serial.print(F("You called B with "));
if (Data.length() > 0) {
Serial.println(Data);
}
else {
Serial.println(F("no data"));
return;
};
if (ValueIsValid(Data, 0, 99)) {
Serial.print(F("Data has been evaluated as : "));
Serial.println(Data.toInt());
}
}
void ProcedureD(String Data) {
Serial.print(F("You called D with "));
if (Data.length() > 0) {
Serial.println(Data);
}
else {
Serial.println(F("no data"));
return;
};
if (ValueIsValid(Data, 0, 11)) {
int dir = Data.toInt();
switch (dir) {
case 0 : Direction = 0;
Serial.println(F("Direction = FORWARD"));
break;
case 3 : Direction = 3;
Serial.println(F("Direction = RIGHT"));
break;
case 6 : Direction = 6;
Serial.println(F("Direction = BACKWARD"));
break;
case 9 : Direction = 9;
Serial.println(F("Direction = LEFT"));
break;
default : Serial.println(F("ERROR: Invalid direction!"));
}
}
}
void ProcedureP(String Data) {
Serial.print(F("You called P with "));
if (Data.length() > 0) {
Serial.println(Data);
}
else {
Serial.println(F("no data"));
return;
}
if (ValueIsValid(Data, 0, 9)) {
Serial.print(F("Data has been evaluated as : "));
Serial.println(Data.toInt());
}
}
void CheckCommands() {
if (Serial.available() > 0) { // read the incoming command
char c = Serial.read();
if (c != char(13)) {
cmd = cmd + c;
}
else {
Evaluate(cmd);
cmd = "";
};
if (cmd.length() > MaxCmdLen) {
cmd = "";
Serial.println(F("ERROR: Command too long!"));
};
}
}
void Evaluate(String Command) {
byte mode = 0;
// set a mode > 0 if there is a complete word command
if (Command.startsWith(F("FORWARD"))) mode = 1;
if (Command.startsWith(F("RIGHT"))) mode = 2;
if (Command.startsWith(F("BACKWARD"))) mode = 3;
if (Command.startsWith(F("LEFT"))) mode = 4;
switch (mode) {
case 1 : ProcedureD(F("0"));
break;
case 2 : ProcedureD(F("3"));
break;
case 3 : ProcedureD(F("6"));
break;
case 4 : ProcedureD(F("9"));
break;
}
if (mode == 0) {
char Cmd = Command.charAt(0);
switch (Cmd) {
case 'A' : ProcedureA(Command.substring(1));
break;
case 'B' : ProcedureB(Command.substring(1));
break;
case 'D' : ProcedureD(Command.substring(1));
break;
case 'F' : if (Command.length() == 1) {
ProcedureD(F("0"));
}
else {
Serial.print(F("ERROR: Invalid command = "));
Serial.println(Command);
}
break;
case 'P' : ProcedureP(Command.substring(1));
break;
case '?' : PrintHelp();
break;
default : Serial.println(F("? for Help"));
break;
}
}
}
void setup() {
Serial.begin(115200);
Serial.print(F("Hello, you just started "));
Serial.println(c_version);
}
void loop() {
CheckCommands();
}