// https://wokwi.com/projects/411769421805937665
// a modification of
// https://wokwi.com/projects/409963746798376961
// a modificaton of:
// https://wokwi.com/projects/409963275966630913
// to use a post-fix/Reverse Polish Notation protocol
// Code from https://www.gammon.com.au/forum/?id=11425&reply=1#reply1
// Other simulations https://forum.arduino.cc/t/wokwi-simulations-for-arduino-built-in-examples/1304754
// See also Nick Gammon's Serial examples
// Serial Example 1: https://wokwi.com/projects/409963075177910273
// Serial Example 2: https://wokwi.com/projects/409963275966630913
/*
Non-Blocking Serial Parsing, RPN-style with switch-case
Another way of processing incoming data without blocking, is to
use single letter commands in a Post-fix/Reverse Polish Notation
form.
Effectively this means looking at each byte in the
input stream, and handling it with the current state.
As an example, say you had this coming into the serial port:
4500R 80S 3G
Where R is RPM, S is speed, and G is the gear setting, using
the current accumulated number.
The code below acts on the bytes as they come in. When it gets
a letter "R", "S" or "G" it copies the number ot the register and
acts. Otherwise it processes incoming digits by multiplying the
previous result by 10, and adding in the new one. Whitespace
is skipped.
This has the advantage of handling long messages without even
needing any buffer, thus saving RAM. You can also process message
as soon as the state changes, rather than waiting for end-of-line.
*/
// Example state machine reading serial input
// Author: DaveX
// Date: 2024-19-24
// Modified from https://www.gammon.com.au/forum/?id=11425&reply=1#reply1
// ######### Global variables
//
long currentValue; // accumulator for number parsing
bool numberMode = false; // activly parsing a number
int verbose = 0 ; //
long Rvalue, Svalue, Gvalue;
struct XYZTstack { // HP41-like calculator stack
long X;
long Y;
long Z;
long T;
} stack;
void processLine () // Act on a \n newline
{
// do something with line
char buff[50];
snprintf(buff, 50, "Settings are %ldRPM %ldSpeed %ldGear ", Rvalue, Svalue, Gvalue);
Serial.println(buff);
if (verbose > 0) {
snprintf(buff, 50, "X:%ld Y:%ld Z:%ld T:%ld\n", stack.X, stack.Y, stack.Z, stack.T);
Serial.println(buff);
}
} // end of processGear
// Calculator stack operations ########################
long popValue() {
long retval = stack.X;
// Serial.print("Popping:");
// Serial.println(retval);
stack.X = stack.Y;
stack.Y = stack.Z;
stack.Z = stack.T;
return retval;
// currentValue = 0;
}
void pushValue() {
// Serial.print("Pushing:");
// Serial.println(currentValue);
stack.T = stack.Z;
stack.Z = stack.Y;
stack.Y = stack.X;
stack.X = currentValue;
currentValue = 0;
}
void completeNumber() {
if (numberMode) {
pushValue();
numberMode = false;
}
}
void processIncomingByte (const byte c)
{
// non-blocking serial parsing with single-character post-fix/RPN actions
//
if ( ! ((c >= '0') && (c <= '9')) ) {
completeNumber();
}
switch (c)
{
case 'R': // RPM command
Rvalue = popValue();
break;
case 'S': // Speed command
Svalue = popValue();
break;
case 'G': // Gear command
Gvalue = popValue();
break;
// ##### Number and command parsing for caclulator
case '0' ... '9': // digits -- collect a series of them
numberMode = true;
currentValue *= 10;
currentValue += c - '0';
break;
case 'c': // change sign
stack.X *= -1;
break;
case 'p': // print
Serial.print(stack.X);
break;
case 's': // swap
currentValue = stack.Y;
stack.Y = stack.X;
stack.X = currentValue;
currentValue = 0;
break;
case 'd': // duplicate
currentValue = stack.X;
pushValue();
break;
case 'r': // roll
currentValue = stack.X;
stack.X = stack.Y;
stack.Y = stack.Z;
stack.Z = stack.T;
stack.T = currentValue;
currentValue = 0;
break;
case '+':
{ long x1 = popValue();
long x2 = popValue();
currentValue = x1 + x2;
pushValue();
}
break;
case '-':
currentValue -= popValue();
pushValue();
break;
case '*':
currentValue = popValue() * popValue();
pushValue();
break;
case '/': // divide
{
long dividend;
long divisor = popValue();
if (divisor != 0 ) {
dividend = popValue();
currentValue = dividend / divisor;
} else { // don't do a divide by zero
currentValue = divisor;
}
pushValue();
}
break;
case '%': // modulus
{
long divisor = popValue();
long dividend = popValue();
currentValue = dividend % divisor;
pushValue();
}
break;
case ' ': // ignore whitespace
case '\t':
break;
case '\n': // end-of-line tag
processLine();
break;
case 'v': // set verbose mode
verbose = popValue();
break;
default:
Serial.print("Character '");
Serial.print(char(c));
Serial.println("' is unknown.");
break;
} // end of switch on incoming byte
} // end of processIncomingByte
// ########### Arduino Basics #############################
void setup ()
{
Serial.begin (115200);
Serial.print(
F(R"(RPN single char parser: 0-9-RSGp+-*/%srdv )"));
} // end of setup
void loop ()
{
while (Serial.available ())
processIncomingByte (Serial.read ());
// do other stuff in loop as required
} // end of loop