I have a state machine that uses one case to watch for a button press. If the user doesn't press the button within the timeout period, they go back to the previous state. If they do press it before the timeout is up, then they go to the next state.
Code:
/*
State Machine with button trigger
Uses button to proceed
*/
// Nick Gammon button handling
// Button reading, including debounce without delay function declarations
const byte Go_button = 12; // the Arduino pin we are connecting the push button to
byte GoButtonOldState = HIGH; // assume switch open because of pull-up resistor
boolean GoButtonPressed = 0; // a flag variable
// Button debounce
const unsigned long debounceTime = 10; // milliseconds
unsigned long buttonPressTime; // when the switch last changed state
int ShowState = 0; // for controlling the state machine - what is the show doing?
int ShowRun = 3; // How long show will run. Time in seconds. 1000 = 1 second.
int ResetTime = 3; // Time before show can be triggered again. Time in seconds. 1 = 1 second.
// Timer
const int timerInterval = 4000;
unsigned long previousTimerMillis = 0;
unsigned long currentMillis = 0;
// Servo
#include <Servo.h>
Servo myservo;
int pos = 0;
// Random number
int randomNumberLow = 0;
int randomNumberHigh = 0;
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT); // Set this pin as an output for the "READY" light
pinMode(Go_button, INPUT_PULLUP); // initialize the pushbutton pin as an input
myservo.attach(10);
myservo.write(90); // Center servo
randomSeed(analogRead(0));
}
void loop() {
currentMillis = millis();
ReadButton();
ChangeState();
}
void ChangeState() {
switch (ShowState) {
case 0: // button not pressed, waiting
// Serial.println("Case 0 - Ready");
break;
case 1: // button pressed, start show
Serial.println("Case 1 - Waiting");
ShowState++;
break;
case 2: // show is going, move servo
Serial.println("Case 2 - Start");
digitalWrite(LED_BUILTIN, LOW);
randomNumberLow = random(0, 60);
randomNumberHigh = random(61, 180);
for (pos = randomNumberLow; pos <= randomNumberHigh; pos += 1) { // goes from low to high position
myservo.write(pos); // tell servo to go to position
delay(15); // waits for the servo to reach the position
}
ShowState++; // Automatically move to next state
break;
case 3: // Wait for button press
Serial.println("Case 3 - Wait for press");
digitalWrite(LED_BUILTIN, HIGH);
pressToProceed();
break;
case 4: // Unused
Serial.println("Case 4 - Done");
digitalWrite(LED_BUILTIN, LOW);
ShowState = 0; // back to the start
break;
}
delay(1); // delay in between reads for stability
}
void pressToProceed() {
ReadButton(); // If button pressed, ShowState is incremented and we go to Case 4
if (currentMillis - previousTimerMillis >= timerInterval) {
ShowState = 2; // Button not pressed in time, so go back to Case 2
previousTimerMillis += timerInterval; // save the time
}
}
void ReadButton() { // Button reading with non-delay() debounce - thank you Nick Gammon!
byte GoButtonState = digitalRead (Go_button);
if (GoButtonState != GoButtonOldState) {
if (millis () - buttonPressTime >= debounceTime) { // debounce
buttonPressTime = millis (); // when we closed the switch
GoButtonOldState = GoButtonState; // remember for next time
if (GoButtonState == LOW) { // When button pressed...
Serial.println ("Button closed"); // DEBUGGING: print that button has been closed
ShowState++;
}
} // end of debounce time up
} // end of state change
}
Sometimes the case advances before the timerInterval is up, and if you watch the Serial Monitor, you will see case 3 go to case 2 within a second, instead of waiting four seconds. Like this:
Button closed
Case 1 - Waiting
Case 2 - Start
Case 3 - Wait for press
Case 2 - Start
Case 3 - Wait for press
Case 2 - Start
Case 3 - Wait for press
Case 2 - Start
Instead, you should see "Case 3 - Wait for press" scroll by for four seconds before timing out and going back to Case 2.
But sometimes this works right. What should I look at?
Thanks!