I would like to send to arduino with serial monitor strings with variables. Something like "var 255" and arduino should write integer 255 to variable "var". Numbers and text will be seperated by space.
That's cool, you dont need our permission.
Do you have a question for the forum?
My code doesn't work, it wants a space at end of command ("var 1400 ") and it ignores the "var" part. I have no idea how to fix this.
String inputString = ""; // a String to hold incoming data
int Step;
int var;
void setup() {
// initialize serial:
Serial.begin(9600);
// reserve 200 bytes for the inputString:
inputString.reserve(200);
}
void loop() {
}
void serialEvent() {
while (Serial.available())
{
char inChar = (char)Serial.read();
if (inChar != 32) inputString += inChar;
if ((inChar == 32)||(inChar == 13)) // check string
{
if(inputString == "var") Step = 1;
if((inputString == "1400")&&(Step = 1))
{
var = 1400;
Serial.println("OK");
}
inputString = "";
}
if (inChar == 13) inputString = "", Step = 0;
}
}
Test_code.ino (735 Bytes)
Sigh..
POST YOU CODE IN CODE TAGS LIKE THIS
String inputString = ""; // a String to hold incoming data
int Step;
int var;
void setup() {
// initialize serial:
Serial.begin(9600);
// reserve 200 bytes for the inputString:
inputString.reserve(200);
}
void loop() {
}
void serialEvent() {
while (Serial.available())
{
char inChar = (char)Serial.read();
if (inChar != 32) inputString += inChar;
if ((inChar == 32)||(inChar == 13)) // Presledek
{
if(inputString == "var") Step = 1;
if((inputString == "1400")&&(Step = 1))
{
var = 1400;
Serial.println("OK");
}
inputString = "";
}
if (inChar == 13) inputString = "", Step = 0; // Će konec
}
}
Oh, I already did it for you.
-jim lee
This conditional is incorrect:
if((inputString == "1400")&&(Step = 1))
I think you meant:
if((inputString == "1400")&&(Step == 1))
For this:
if (inChar == 13) inputString = "", Step = 0;
You should have this:
if (inChar == 13)
{
inputString = "";
Step = 0;
}
My guess is you are not receiving a CR (13) and therefore a space (32) is required to trigger your conditional. You need to look at how your serial monitor is configured.
Doesn't look like your doing very much here..
void loop() {
}
-jim lee
This code does what you want. It links variable names to variables via functions that take the new value.
The input line can be terminated by Arduino IDE settings, "Carriage return" or "Newline" or "Both NL and CR" or "No line ending" in which case the line is processed after 1sec (non-blocking delay)
The check on the int value is very robust. Only valid ints are accepted.
The SafeStringReader has auto echo turned on so it displays the input as typed.
Sample output
Enter inputs like variable integer, e.g. var1 255
Valid variables are:- var1 var2 var3
abc
'abc' does not have a valid variable name.
var1
Variable var1 value '' is not an integer
var2 5a
Variable var2 value ' 5a' is not an integer
var3 244
Setting var3 to 244
// ReadVariableValues
// https://forum.arduino.cc/index.php?topic=731855.0
//
//
// download and install the SafeString library from
// www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
#include "SafeStringReader.h"
// create an sfReader instance of SafeStringReader class
// that will handle line upto 50 chars long
// delimited by CarrageReturn or NewLine or just input timeout 1sec if no line ending
const unsigned int MAX_CMD_LENGTH = 50;
createSafeStringReader(sfReader, MAX_CMD_LENGTH, "\r\n");
int var1;
int var2;
int var3;
// etc
void var1Cmd(int value) {
var1 = value;
}
void var2Cmd(int value) {
var2 = value;
}
void var3Cmd(int value) {
var3 = value;
}
typedef struct {
const char* cmd;
void (*cmdFn)(int); // a method taking a SafeString& arg and returning void
} COMMANDS_STRUCT;
COMMANDS_STRUCT commands[] = {
{"var1", var1Cmd},
{"var2", var2Cmd},
{"var3", var3Cmd}
};
const size_t NO_OF_CMDS = sizeof commands / sizeof commands[0];
void setup() {
Serial.begin(9600);
for (int i = 10; i > 0; i--) { // pause a little to give you time to open the Arduino monitor
Serial.print(i); Serial.print(' '); delay(500);
}
Serial.println();
SafeString::setOutput(Serial); // enable error messages and SafeString.debug() output to be sent to Serial
Serial.println(F(" Enter inputs like variable integer, e.g. var1 255"));
Serial.print(F(" Valid variables are:-"));
for (size_t i = 0; i < NO_OF_CMDS; i++) {
Serial.print(" "); Serial.print(commands[i].cmd);
}
Serial.println();
sfReader.connect(Serial); // where SafeStringReader will read from
sfReader.echoOn(); // echo back all input, by default echo is off
sfReader.setTimeout(1000); // 1000mS = 1sec timeout
}
void processCmd(SafeString & input) {
input.trim();
if (input.isEmpty()) {
return; // nothing to do
}
// get var int separated by space
cSF(sfVarName, MAX_CMD_LENGTH); // large enough to handle entire input if no space found
int idx = input.indexOf(' '); // look for space
input.substring(sfVarName, 0, idx);
// Check token against valid commands
for (size_t i = 0; i < NO_OF_CMDS; i++) {
if (sfVarName == commands[i].cmd) { // found one
// pickup the integer
cSF(sfValue, MAX_CMD_LENGTH);
input.substring(sfValue, idx);
int value = 0;
if (sfValue.toInt(value)) {
Serial.print(F(" Setting ")); Serial.print(sfVarName); Serial.print(F(" to ")); Serial.println(value);
commands[i].cmdFn(value); // call the cmd fn
} else {
Serial.print(F(" Variable ")); Serial.print(sfVarName); Serial.print(F(" value ")); Serial.print(sfValue); Serial.println(F(" is not an integer"));
}
return; // found cmd
}
}
// else no cmd found have finished processing this token, clear it so it is not output
Serial.print("'"); Serial.print(input);
Serial.println(F("' does not have a valid variable name."));
}
void loop() {
if (sfReader.read()) {
processCmd(sfReader); //sfReader holds the input line
} // else no line yet
}
drmpf:
According to ASCII they are named CR (13, Carriage Return) and LF (10, Line Feed).
True but Arduino IDE options are Newline (not LineFeed)
But just to clarify let me edit the post.
I tried this, but it wont accept "M0 12", only "M0 12 " whit a space at the end.
This get trigered for both "M0 12" and M0 12 " and oututs "Reset"
if (inChar == 13) inputString = "", Serial.println("Reset"), Step = 0; // If end
But this part ignores the char 13 for some reason, Serial monitor set to carriage return
if ((inChar == 32)||(inChar == 13)) // Space
{
if(inputString == "M0") Step = 1;
if((inputString == "12")&&(Step == 1)) Serial.println("OK");
inputString = "";
}
String inputString = ""; // a String to hold incoming data
int Step;
void setup() {
// initialize serial:
Serial.begin(9600);
// reserve 200 bytes for the inputString:
inputString.reserve(200);
}
void loop() {
}
void serialEvent() {
while (Serial.available())
{
char inChar = (char)Serial.read();
if (inChar != 32) inputString += inChar;
if ((inChar == 32)||(inChar == 13)) // Space
{
if(inputString == "M0") Step = 1;
if((inputString == "12")&&(Step == 1)) Serial.println("OK");
inputString = "";
}
if (inChar == 13) inputString = "", Serial.println("Reset"), Step = 0; // If end
}
}
Try this version, input line needs to be terminated with '\n'
String inputString = ""; // a String to hold incoming data
int var;
void setup() {
Serial.begin(9600);
for (int i = 10; i > 0; i--) { // pause a little to give you time to open the Arduino monitor
Serial.print(i); Serial.print(' '); delay(500);
}
Serial.println(); // reserve 200 bytes for the inputString:
inputString.reserve(200);
}
bool readLine() {
while (Serial.available()) {
char inChar = Serial.read();
if (inChar == '\n') {
return true;
}
// else
inputString += inChar;
}
return false;
}
void loop() {
if (readLine()) {
// got whole line terminated by \n
Serial.println(inputString);
inputString.trim(); // added in later edit.
int idx = inputString.indexOf(' '); // look for space
String varName = inputString.substring(0, idx);
if (varName == "var") {
// got varname
String valueStr = inputString.substring(idx);
valueStr.trim();
int newValue = valueStr.toInt();
if (valueStr == String(newValue)) { // ok
var = newValue;
Serial.print(F("Set var to ")); Serial.println(newValue);
} else {
Serial.print(F(" Variable ")); Serial.print(varName);
Serial.print(F(" value '")); Serial.print(valueStr); Serial.println(F("' is not an integer"));
}
} else {
Serial.print("'"); Serial.print(varName);
Serial.println(F("' not valid variable name."));
}
inputString = "";
}
}
Is this really needed?
for (int i = 10; i > 0; i--) { // pause a little to give you time to open the Arduino monitor
Serial.print(i); Serial.print(' '); delay(500);
}
when opening the Arduino Serial monitor you reboot the board (for many arduino boards)
Also checking for -1 returned by indexOf() would be good programming practice (generating an UB issue otherwise later on when you try to extract the substring if I remember well)
Is this really needed?
Not for Uno/Mega2560. Boards restart when the download finishes but some other boards do not restart (again) when you open the Monitor and some even don't like the Monitor being open when programming and sometimes my computer does not like it either So I just add this to all my sketches to let me know I have not missed any startup messages.
So no not needed, just habit of mine as I often cross compile on different boards to check results/errors.
checking for -1 returned by indexOf() would be good programming practice
Yes I used to think so until I had a close look at the substring and indexOf methods
The index args for substring and indexOf() are unsigned int, so -1 => a very large number and the code for substring and indexOf is very relaxed, and does not complain if the index is out of range but instead converts it to an appropriate number, usually length(), so
substring(0,-1) just returns the whole String, while substring(-1) returns an empty String.
Just what is needed in this case.
Since Arduino Strings do not have means of flagging errors, the methods try hard to give you a usable result even for out of bounds arguments.
But you do have to check in while ( ) loops when you step over the last index to start the next search, because
-1 + 1 == 0 and loop will just go back to searching from the front of the String and the loop will not stop
I had to change a lot of code in V4 of SafeString to mimic this type of behaviour. In V3 of SafeString it would have refused to use a (size_t)(-1) argument, now, in V4, SafeString just complains (if you are listening) and treats it the same way as Arduino Strings. There is a new test sketch in the SafeStrings examples that compares Arduino Strings and SafeStrings results for these cases.
OK got it. we usually see more often while (!Serial);
for some boards than an active 5s wait
good point on the unsigned int, indeed will work then.
I got the code working 99%.
Code reads variable name and value form serial and writes the value to matching variable. Now im trying to read only string "Mem" that will write back to serial monitor all values stored in variables (M0 and M1), but the If state wont trigger for some reason.
Can some one help me?
void OnlyString(String str)
{
if (str == "Mem") // <--- This if state wont triger when I send "Mem" over serial with carriage return
{
Serial.print("M0 = ");
Serial.println(M0);
Serial.print("M1 = ");
Serial.println(M1);
}
Serial.println(str); // <--- Outputs "Mem" to serial just fine
}
But this part works ok
void UsefulData(String var, uint16_t num)
{
Serial.println(var);
Serial.println(num);
if (var == "M0") M0 = num, Serial.println(M0); // <--- Detects "M0" in string perfectly
if (var == "M1") M1 = num, Serial.println(M1);
Serial.println("OK");
}
Full code:
String inputString = "", serialWord = "";
int serialStep;
int M0, M1;
void setup() {
Serial.begin(9600);
}
void loop() {
}
void serialEvent() {
while (Serial.available())
{
char inChar = (char)Serial.read();
if (inChar != 32) inputString += inChar;
if (inChar == 32) serialWord = inputString, inputString = "", serialStep = 1;
if ((inChar == 13)&&(serialStep == 1)) UsefulData(serialWord, inputString.toInt()), inputString = "", serialWord = "", serialStep = 0;
if ((inChar == 13)&&(serialStep == 0)) OnlyString(inputString), inputString = "", serialStep = 0;
}
}
void UsefulData(String var, uint16_t num)
{
Serial.println(var);
Serial.println(num);
if (var == "M0") M0 = num, Serial.println(M0);
if (var == "M1") M1 = num, Serial.println(M1);
Serial.println("OK");
}
void OnlyString(String str)
{
if (str == "Mem")
{
Serial.print("M0 = ");
Serial.println(M0);
Serial.print("M1 = ");
Serial.println(M1);
}
Serial.println(str);
}
Just a serving suggestion.
....
....
char inChar = Serial.read();
switch(inChar) {
case 32 :
serialWord = inputString;
inputString = "";
serialStep = 1;
break;
case 13 :
if (serialStep == 1) {
UsefulData(serialWord, inputString.toInt());
serialWord = "";
} else if (serialStep == 0) {
OnlyString(inputString);
}
inputString = "";
serialStep = 0;
break;
default : inputString += inChar; break;
}
Using a switch statement may help make the logic easier to follow.
Or not.. Just a user preference kind of thing.
-jim lee
This is the first of 3 posts on your project.
String usage.
Check out my tutorial on Taming Arduino Strings
For your code, it is so simple that there are not really any String problems.
But Taming Arduino Strings advises adding the reserve(20) back in for the two Strings
and changing the methods to
void UsefulData(const String& var, uint16_t num)
void OnlyString(String& str) // str updated by trim();
to avoid un-necessary String creations and data copying.
Your use of serialEvent() is un-common. Not a problem, just not often used.
This is what happens under the hood in Arduino
int main(void) { // reset starts running the microprocessor from here
// . .
setup();
for (;;) { // loop here for ever running loop() and serialEventRun()
loop();
// call serialEventRun(); which expands to
if (Serial.available()) serialEvent();
}
return 0;
}
So using serialEvent() is the same as
if (Serial.available()) {
// do stuff
}
The difference is that serialEvent() is only ever called once each loop().
Normally this is not a problem, but for larger more complicated sketches the loop() may be slow
due to processing/Serial prints and you can miss Serial data. Then you need to stop using serialEvent()
and poll the Serial more often during the loop() code
See my tutorials on
Arduino Serial I/O for the Real World and Multi-tasking in Arduino for what to do in that case
Again not a problem for the code you have at the moment.
See your slightly modified code below, with added comments and the fix.
Interesting use of the , to string statements together, the result is the last one, which you throw away.
I replaced them with ;
More code comments and Debug prints help a lot.
Your code 'works' but is very fragile. If you enter "Mem " with a trailing space it is ignored due to your state machine. trim() does not help in this case. Suggest you have another look at either of my two previous code solutions which are more robust.
String inputString = "", serialWord = "";
int serialStep;
int M0, M1;
void setup() {
Serial.begin(9600);
for (int i = 10; i > 0; i--) { // pause a little to give you time to open the Arduino monitor
Serial.print(i); Serial.print(' '); delay(500);
}
Serial.println();
inputString.reserve(20); // reserve 20 bytes for the inputString:
serialWord.reserve(20); // reserve 20 bytes for the serialWord:
Serial.println(F("setup() complete"));
}
void loop() {
}
void UsefulData(const String& var, uint16_t num)
{
Serial.println(var);
Serial.println(num);
if (var == "M0") M0 = num, Serial.println(M0);
if (var == "M1") M1 = num, Serial.println(M1);
Serial.println("OK");
}
void OnlyString(String& str) // str updated by trim();
{
Serial.print("'"); Serial.print(str); Serial.println("'");
str.trim(); // remove leading trailing white space NL CR etc
if (str == "Mem") // << this now works
{
Serial.print("M0 = ");
Serial.println(M0);
Serial.print("M1 = ");
Serial.println(M1);
}
}
// run the state machine with two states serialStep == 0 or 1
// starting state serialStep == 0 => serialStep == 1 when have read first word delimited by ' '
void serialEvent() {
while (Serial.available())
{
char inChar = (char)Serial.read();
if (inChar != ' ') { // note this adds '\n' to the inputString at endOfLine
inputString += inChar;
} else { // inChar == 32
serialWord = inputString;
inputString = "";
serialStep = 1;
}
if (inChar == '\n') { // == 13 // found end of line
// what state are we in
if (serialStep == 1) {
UsefulData(serialWord, inputString.toInt()); // toInt is not very good see my previous posts for better code
// start again
inputString = "";
serialWord = "";
serialStep = 0;
} else { // (serialStep == 0) i.e. got newline without a trailing ' '
OnlyString(inputString);
inputString = "";
serialStep = 0;
}
}
}
}