//
// Based on: millis_and_finite_state_machine.ino
// https://wokwi.com/arduino/projects/314526434594914882
//
// An 'enum' is the best way to for a list of the states.
enum
{
STATE_INITIAL_MESSAGE,
STATE_IDLE,
STATE_FIRST_PRESS,
STATE_RELEASE_IN_BETWEEN,
STATE_SECOND_PRESS,
STATE_FAILED,
} state;
const int buttonPin = 2;
int last_button_value = HIGH; // pin 2 is HIGH when button is not pressed
unsigned long previousMillis; // a single 'previousMillis' used for everything
unsigned long elapsedMillisFirstPress; // how long was the button pressed the first time
const unsigned long pressMinimum = 500; // minimum time to press
const unsigned long pressMaximum = 5000; // maximum time to press
const unsigned long releaseMaximum = 3000; // maximum time release in the middle
void setup()
{
Serial.begin( 115200);
Serial.println( "The sketch has started.");
pinMode( buttonPin, INPUT_PULLUP);
}
void loop()
{
unsigned long currentMillis = millis();
// ---------------------------------------------------
// BUTTON
// ---------------------------------------------------
// Detect if the button changes, and pass
// that information on. In this case to the finite state machine.
// Only the event of the change is passed on, because
// this sketch did not need to know the state
// of the button, only a change.
// ---------------------------------------------------
bool buttonPressed = false;
bool buttonReleased = false;
int button_value = digitalRead( buttonPin);
if( button_value != last_button_value)
{
if( button_value == LOW) // low is button pressed
{
buttonPressed = true;
}
else
{
buttonReleased = true;
}
last_button_value = button_value;
}
// ---------------------------------------------------
// FINITE STATE MACHINE
// ---------------------------------------------------
// The final state machine uses information to
// make decisions.
// ---------------------------------------------------
switch( state)
{
case STATE_INITIAL_MESSAGE:
// Before going to the actual "idle" state, a message is printed.
Serial.println( "------------------------------------------");
Serial.println( "Press the button twice for a second or so.");
state = STATE_IDLE;
break;
case STATE_IDLE:
// This "idle" state is when there is nothing to do.
// This state is executed until a button is pressed.
// It "waits" until a button is pressed, but it does
// not really wait, since it is run over and over again.
if( buttonPressed)
{
previousMillis = currentMillis;
state = STATE_FIRST_PRESS;
}
break;
case STATE_FIRST_PRESS:
// The button is pressed at the moment.
// Check the time when it is released.
if( buttonReleased)
{
elapsedMillisFirstPress = currentMillis - previousMillis;
if( elapsedMillisFirstPress >= pressMinimum and elapsedMillisFirstPress <= pressMaximum)
{
previousMillis = currentMillis;
state = STATE_RELEASE_IN_BETWEEN;
}
else
{
state = STATE_FAILED;
}
}
break;
case STATE_RELEASE_IN_BETWEEN:
if( currentMillis - previousMillis > releaseMaximum)
{
state = STATE_FAILED;
}
else if( buttonPressed) // button pressed within time ?
{
previousMillis = currentMillis;
state = STATE_SECOND_PRESS;
}
break;
case STATE_SECOND_PRESS:
// The button is pressed at the moment for the second time
// Check the time when it is released.
if( buttonReleased)
{
unsigned long elapsedMillisSecondPress = currentMillis - previousMillis;
if( elapsedMillisSecondPress >= pressMinimum and elapsedMillisSecondPress <= pressMaximum)
{
Serial.println( "Success.");
if( elapsedMillisSecondPress > elapsedMillisFirstPress)
{
Serial.print( "The second press was longer by ");
Serial.print( elapsedMillisSecondPress - elapsedMillisFirstPress);
Serial.println( " milliseconds.");
}
else
{
Serial.println( "The second press was shorter.");
}
state = STATE_INITIAL_MESSAGE;
}
else
{
state = STATE_FAILED;
}
}
break;
case STATE_FAILED:
Serial.println( "Not accepted !");
state = STATE_INITIAL_MESSAGE;
break;
}
delay( 10); // a delay as a simple way to debounce the button
}