/*
Forum: https://forum.arduino.cc/t/millis-statt-delay-funktioniert-so-nicht/1381778
Wokwi: https://wokwi.com/projects/466267980743580673
A very simple state machine that demonstrates the use of
enum class for states
millis() instead of delays to control sequence flows
A sequence is started by Serial input 's' or 'S'.
The sequence ends after "sequenceDuration" [ms]
Version IV uses separate code to handle different functionality
Makes reading, understanding and maintaining the code easier
ec2021
2025/06/09
*/
constexpr byte ledPin {12};
constexpr byte buttonPin {11};
constexpr unsigned long sequenceDuration = 3500; // [ms]
constexpr unsigned long blinkInterval = 166; // [ms]
constexpr unsigned long printInterval = 1000; // [ms]
enum class STATE {IDLE, RUNNING};
STATE state = STATE::IDLE;
unsigned long seqStart = 0;
unsigned long lastTimeHere = 0;
unsigned long lastTimeLedChanged = 0;
byte ledState = LOW;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(115200);
Serial.println("Input s or S");
}
void loop() {
verySimpleStateMachine();
}
void verySimpleStateMachine() {
switch (state) {
case STATE::IDLE:
checkSerialAndButton();
break;
case STATE::RUNNING:
doSerialPrinting();
blinkLed();
checkAndHandleSequenceDone();
break;
}
}
boolean startSequence() {
bool returnValue = false;
if (Serial.available()) {
char c = Serial.read();
if (toupper(c) == 'S') {
returnValue = true;
}
}
if (digitalRead(buttonPin) == LOW) {
returnValue = true;
delay(30); // Simple Debouncing! Happens only when the button is pressed
}
return returnValue;
}
void checkSerialAndButton() {
if (startSequence()) {
// This is performed when a sequence shall start
// We store the start time
seqStart = millis();
// Do something that shall be done once everytime
// a sequence starts (here we only print something)
Serial.print("Start - Running - ");
// and now change the state to RUNNING
state = STATE::RUNNING;
}
}
void doSerialPrinting() {
if (millis() - lastTimeHere >= printInterval) {
// This is performed every printInterval while state == RUNNING
// We store the time when this if clause was entered for the next loop()
lastTimeHere = millis();
// We could do e.g. read sensor values or control a stepper or ...just print something
Serial.print('.');
}
}
void blinkLed() {
if (millis() - lastTimeLedChanged >= blinkInterval) {
// This is performed every blinkInterval while state == RUNNING
// We store the time when this if clause was entered for the next loop()
lastTimeLedChanged = millis();
// Let's switch the state of the led:
ledState = !ledState;
digitalWrite(ledPin, ledState);
}
}
void checkAndHandleSequenceDone() {
if (millis() - seqStart >= sequenceDuration) {
// This is performed when sequenceDuration is expired
// We could stop a motor, switch an led on or off, or just print something
Serial.println(" - Stop");
Serial.println("Input s or S");
// Let's make sure the led is off when entering the IDLE state
ledState = LOW;
digitalWrite(ledPin, ledState);
// As the sequenceDuration has expired and we have done our "final work" we go back to IDLE
state = STATE::IDLE;
}
}