This is another small tutorial that shall demonstrate how to receive commands over a serial interface in a non-blocking manner using the SafeString-library
If you are in a hurry to throw together code as fast as possible jump to posting # 2
non-blocking means the code has a fast running function loop() that is able to
- check for button-presses
- check for IO-pin changing their state
- read in sensors
- etc
and in parallel listen to the serial interface all the time
and in case all characters of a command are received execute the code dedicated / connected to this command
Especially if you are a newcomer:
If you have questions about the demo-code just ask the questions.
There are no better experts than newcomers to judge and improve demo-codes that shall explain something.
If you have ideas for improvment or variants post your ideas.
The first code-version has some additional features. using functions and using non-blocking timing.
here is the demo-code with intensive comments
// SafeStringReader_Cmds.ino
//
// Example of NON-Blocking read commmands from the Arduino Monitor input and acts on them
// the available commands are start stop
// See the SafeStringReader_CmdsTimed.ino for an example using a struct to hold the commands and their functions
//
// Commands are delimited by space dot comma NL or CR
// If you set the Arduino Monitor to No line ending then the last command will be ignored until it is terminated by a space or ,
// Use the settings Newline or Carrage Return or Both NL & CR
//
// These commands can be picked out of a line of user input
// start stop
// The input line can be as long as you like 100's of Kb long, but only two small buffers need to parse the commands
//
// download and install the SafeString library from
// www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
// overview about the functionality of this code:
// inside function loop() four things are done:
// 1. count up a variable infinitely to demonstrate the fast running loop
// 2. Blink the onboard LED to demonstrate non-blocking timing
// 3. Check for commands send over the serial interface
// and in case a command is received analyse the command and execute
// the function related to the command
// 4. print the value of the counter-variable once every second to
// demontsrate the fast running loop
#include "SafeStringReader.h"
// create an myInputReader instance of SafeStringReader class
// that will handle commands upto 5 chars long
// delimited by space, comma or CarrageReturn or NewLine
// the createSafeStringReader( ) macro creates both the SafeStringReader (myInputReader) and the necessary SafeString that holds input chars until a delimiter is found
// args are (ReaderInstanceName, expectedMaxCmdLength, delimiters)
// create the "object" with name myInputReader that does read from the serial interface
// maximum length of accepted inputcommands is 5 characteras
// allowed delimiters are:
// space: the " "
// comma: ,
// carriage return: \r
// new line: \n
createSafeStringReader(myInputReader, 5, " ,\r\n");
boolean running = true;
unsigned long loopCounter = 0;
unsigned long allwaysCounter = 100000000;
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
unsigned long MyTestTimer = 0; // Timer-variables MUST be of type unsigned long
const byte OnBoard_LED = 13;
void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);
if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}
void setup() {
Serial.begin(115200);
Serial.println( F("Setup-Start") );
Serial.println();
Serial.println(F(" SafeString-Reader-Demo"));
Serial.println(F(" Commands are stop start"));
Serial.println(F(" Set the Arduino IDE monitor to Newline, or Carriage return or Both NL & CR"));
Serial.println(F(" See the SafeStringReader_CmdsTimed.ino for an example using a struct to hold the commands and their functions."));
SafeString::setOutput(Serial); // enable error messages and SafeString.debug() output to be sent to Serial
if (running) {
Serial.println(F(" Counter Started"));
}
myInputReader.connect(Serial); // where SafeStringReader will read from in this demo the standard Serial
myInputReader.echoOn(); // echo back all input, by default echo is off
}
void handleStartCmd() {
running = true;
Serial.println();
Serial.print(F("> start at Counter:"));
Serial.println(loopCounter);
}
void handleStopCmd() {
running = false;
Serial.println();
Serial.print(F("> stop at Counter:"));
Serial.println(loopCounter);
}
void ManageSerialCommands() {
if (myInputReader.read()) { // check if all characters of a command including delimiter are received
if (myInputReader == "start") {
handleStartCmd();
}
else if (myInputReader == "stop") {
handleStopCmd();
} // else ignore unrecognized command
} // else no delimited command yet
// rest of code here is executed while the user typing in commands
if (running) {
loopCounter++;
if ((loopCounter % 100000) == 0) {
// print the current counter each time it is a multiple of 100000
Serial.print(F("Counter:"));
Serial.println(loopCounter);
}
}
}
void loop() {
allwaysCounter++; // does what its name says allways counting up more and more demonstrating the non-blocking character of the code
BlinkHeartBeatLED(OnBoard_LED, 250); // demontrates non-blocking timing
ManageSerialCommands(); // call this non-blocking function in every iteration of loop
if ( TimePeriodIsOver(MyTestTimer, 1000) ) {// check if 1000 milliseconds have passed by
// once every 1000 milliseconds execute code below
if (running) { // only in case variable running has value true
Serial.print(F("allwaysCounter:"));
Serial.println(allwaysCounter);
}
}
}
best regards Stefan