Skip to main content
2 of 4
added 1 character in body
Jot
  • 3.2k
  • 1
  • 13
  • 21

The Arduino has a loop function, that runs over and over again.
The Arduino has a millis function for the time.
Those two combine beautifully with a state machine.

Once I made a function that returns the input value as BOTH_OFF, BOTH_ON, or SOMETHINE_ELSE, then the state machine automatically unfolds itself.

I hope you see how clean the code is. Every state has its own code. You don't have to think for hours how to make a spaghetti of if-statements. Although the sketch is now larger, there is no unnecessary code.
If you want to change something, you only have to find the state that you want to change. Adding a new state is also easy.

I have added some explanation in the sketch.

// A state machine is used, only to make it more clear what is going on.
//
// When a button is pressed, the input turns HIGH.
// Are there an external pulldown resistors ?
//
// Normally only one 'enum' is used for the state, that controls the code.
// This sketch has also a 'enum' for the buttons, which is called the input state.


// define the different states for this sketch.
// An 'enum' is like a number of #define with increasing numbers.
enum
{
  WAIT_FOR_BOTH_OFF,
  WAIT_FOR_BOTH_ON,
  SAFETY_WINDOW,
  RELAY_ON,
} state;       // an 'enum' is automatically an integer.

// define the different input states.
enum
{
  BOTH_OFF,        // no buttons are pressed.
  BOTH_ON,         // both buttons are pressed.
  SOMETHING_ELSE,  // probably one of the buttons is pressed.
};


const int ButtonPin1 = 2;
const int ButtonPin2 = 3;


// The previousMillis is used twice for two different things.
// That is okay in this state machine.
unsigned long previousMillis;

const unsigned long RelayOnTime = 1000;    // Length of time for relay
const unsigned long PushTimeWindow = 1000; // Length of time to allow two pushes

// Counters for the number of failed relay triggers and successful cycles.
// They can probably be normal integers as well.
unsigned long failed = 0;
unsigned long success = 0;


void setup()
{
  Serial.begin(9600);
  Serial.println("Hello");

  pinMode(ButtonPin1, INPUT);
  pinMode(ButtonPin2, INPUT);

  pinMode(LED_BUILTIN, OUTPUT);  // LED_BUILTIN for testing

  Serial.println("Going to WAIT_FOR_BOTH_OFF state");
  state = WAIT_FOR_BOTH_OFF;
}


void loop()
{
  unsigned long currentMillis = millis();

  // Collect all input data.
  int inputState = getInputState();

  // Process the data in a state machine.
  // The output part is also in the state machine.
  switch(state)
  {
    case WAIT_FOR_BOTH_OFF:
      // Both buttons must be released to be able to continue.
      if(inputState == BOTH_OFF)
      {
        // Prepare to go to the next state.
        Serial.println("Going to WAIT_FOR_BOTH_ON state");
        state = WAIT_FOR_BOTH_ON;
      }
      break;
    case WAIT_FOR_BOTH_ON:
      // This is the state when everything is idle and no buttons are pressed.
      // Normally both buttons are released,
      // but when both are pressed, then go to the next state.
      if(inputState == BOTH_ON)
      {
        // Prepare to go to the next state.
        Serial.println("Going to SAFETY_WINDOW state");
        previousMillis = currentMillis;
        state = SAFETY_WINDOW;
      }
      break;
    case SAFETY_WINDOW:
      if(inputState != BOTH_ON)
      {
        // During the safety time window, a button was released.
        // Return to idle.
        Serial.println("Going to WAIT_FOR_BOTH_OFF state");
        state = WAIT_FOR_BOTH_OFF;
      }
      else if(currentMillis - previousMillis >= PushTimeWindow)
      {
        // The buttons are still pressed.
        // The time window has reached the end.
        // Prepare to go to the next state.
        Serial.println("Going to RELAY_ON state");
        previousMillis = currentMillis;
        digitalWrite(LED_BUILTIN, HIGH);  // LED_BUILTIN for testing
        state = RELAY_ON;
      }
      break;
    case RELAY_ON:
      if(inputState != BOTH_ON)
      {
        // One of the buttons was released.
        // Stop the relay and return to idle.
        // Although the relay was triggered, it was not succesful.
        failed++;
        Serial.print("Failed=");
        Serial.println(failed);
        Serial.println("Going to WAIT_FOR_BOTH_OFF state");
        digitalWrite( LED_BUILTIN, LOW);  // LED_BUILTIN for testing
        state = WAIT_FOR_BOTH_OFF;
      }
      else if(currentMillis - previousMillis >= RelayOnTime)
      {
        // Everyting was okay.
        // The buttons are still pressed.
        // It was complete and succesful.
        // It is time to turn off the relay and return to idle.
        success++;
        Serial.print("Success=");
        Serial.println(success);
        Serial.println("Going to WAIT_FOR_BOTH_OFF state");
        digitalWrite(LED_BUILTIN, LOW);  // LED_BUILTIN for testing
        state = WAIT_FOR_BOTH_OFF;
      }
      break;
    default:
      Serial.println("Error, unknown state");
      break;
  }
}


// This function returns the input state.
// There is no StateChangeDetection, because a state machine is used.
int getInputState()
{
  int returnInputState;

  // Read the buttons and create a input state according to the 'enum' values.
  int ButtonState1 = digitalRead(ButtonPin1);
  int ButtonState2 = digitalRead(ButtonPin2);

  if(ButtonState1 == LOW && ButtonState2 == LOW)
  {
    returnInputState = BOTH_OFF;
  }
  else if(ButtonState1 == HIGH && ButtonState2 == HIGH)
  {
    returnInputState = BOTH_ON;
  }
  else
  {
    // Not both on, not both off, so it must be something else.
    returnInputState = SOMETHING_ELSE;
  }

  return(returnInputState);
}
Jot
  • 3.2k
  • 1
  • 13
  • 21