Ignore button while state machine executes?

I've got a state machine that is set up to watch for the press of a button, then turn on/off a group of relays, and after a set amount of time, stop the relays and reset the machine to wait for the button to be pressed again. I'm using Nick Gammon's LED Flasher library (bottom of this page - http://www.gammon.com.au/forum/?id=11411) to update the relays while the timer runs. However, what I've discovered is that if you press the button before the timer has run out, everything starts over again.

What would I do so the code will ignore the button press until the timer is done, and we are back to case 0 (waiting for a button press)?

/*
State Machine with button trigger
Using LedFlasher library to toggle relays 
 */

#include <LedFlasher.h>

const int buttonPin = A1;     // the number of the pushbutton pin (Analog 1)
const int ReadyLight = 13;    // For the green "Ready" light

int buttonState = 0;         // variable for reading the pushbutton/breakbeam status
long startTime ;            // start time for stop watch
long elapsedTime ;          // elapsed time for stop watch
int ShowState = 0;          // for controlling the state machine - what is the show doing?
int ShowRun = 8000;         // How long show will run.  Time in seconds. 1000 = 1 second.
int ResetTime = 8;          // Time before show can be triggered again.  Time in seconds. 1 = 1 second.

// LED Flasher library - set up
// Configure timing for each output
// If the show is 5000 (5 seconds) then (8, 200, 2500) will toggle the output once
LedFlasher floodLight (8, 200, 1500);         // pin 8, off for 200 mS, on for 1500 mS
LedFlasher shuttleBayDoors (9, 300, 600);
LedFlasher impulseEngine (10, 900, 100);
LedFlasher strobe (11, 500, 1000);
LedFlasher navigation (12, 1000, 2000);
LedFlasher torpedoes (7, 250, 500);
LedFlasher Z1 (4, 500, 500);

void setup() {

  // LED Flasher commands
  floodLight.begin ();
  shuttleBayDoors.begin ();
  impulseEngine.begin ();
  strobe.begin ();
  navigation.begin ();
  torpedoes.begin ();
  Z1.begin ();

  // relay module is reversed from LEDs, make sure it's all off by setting to HIGH
  // for testing with LEDs, these are flipped to LOW
  digitalWrite(8, HIGH);    
  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(11, HIGH);
  digitalWrite(12, HIGH);
  digitalWrite(7, HIGH);
  digitalWrite(4, HIGH);

  pinMode(ReadyLight, OUTPUT);    // Set this pin as an output for the "READY" light
  pinMode(buttonPin, INPUT);      // initialize the pushbutton pin as an input

}

void loop() {

  buttonState = digitalRead(buttonPin);    // read the state of the pushbutton value
  if (buttonState == 1)
  {
    ShowState = 1;           // change to case 1 - start the relays
    startTime = millis();    // store the start time
  }   


  switch (ShowState) {      // switchCase controls the show based on ShowState
  case 0:                   // button not pressed, waiting 
    digitalWrite(ReadyLight, HIGH);  // turn on Ready Light
    break;

  case 1:    // button pressed, start show
   digitalWrite(ReadyLight, LOW);    // turn off Ready Light
   ShowState = 2; 

  case 2:         // show is going, start relays

    elapsedTime =   millis();  // store elapsed time

    if (elapsedTime - startTime >= ShowRun)  // while this case loops, check to see if it's time to stop the show
    {       
      ShowState = 3;                         // stop the show, go to case 3
    }

    // start relays going.  update keeps them blinking while elapsedTime is watched.
    floodLight.update ();
    shuttleBayDoors.update ();
    impulseEngine.update ();
    strobe.update ();
    navigation.update ();
    torpedoes.update ();
    Z1.update ();
    
    break;
  
  case 3:    // LEDs off
    
    // turn off all relays, since some may be left on when show is stopped
    digitalWrite(8, HIGH);    
    digitalWrite(9, HIGH);
    digitalWrite(10, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(12, HIGH);
    digitalWrite(7, HIGH);
    digitalWrite(4, HIGH);

 // Reset time, run quick for...next loop

    for(int j = 0; j < ResetTime; j++){  
//      Serial.print(".");
      delay(1000);
    }
    ShowState = buttonState;
    break;

  case 4:    // Unused
//    Serial.println("case 4 - unused");
    break;
  } 
  delay(1);        // delay in between reads for stability
}

Jeff-

In that case you want to add a "state check" to the if statement for the button press.

So something like

  buttonState = digitalRead(buttonPin);    // read the state of the pushbutton value
  if ((buttonState == 1) && (ShowState == 0))
  {
    ShowState = 1;           // change to case 1 - start the relays
    startTime = millis();    // store the start time
  }

Hope this helps,

Brad
KF7FER

Brad,

Thank you! That did it.

Jeff

So something like

I'm curious why you would even read the state of the switch if it didn't matter, which is what OP suggested was the goal.

In that code, the state of the switch does matter. Maybe I'm misreading something.

PaulS:
I'm curious why you would even read the state of the switch if it didn't matter, which is what OP suggested was the goal.

Good point. I was trying to show the easiest way to modify the code to get the desired result and I'm afraid sometimes I get a bit to literal. If I may quote

What would I do so the code will ignore the button press until the timer is done

He did use the word ignore. But I do agree there is a better way to solve the problem and I missed it. Maybe a better example would be:

// Please note any comment lines that begin with "**"
void loop() {
  switch (ShowState) {      // switchCase controls the show based on ShowState
  case 0:                   // button not pressed, waiting 
    digitalWrite(ReadyLight, HIGH);  // turn on Ready Light

    if (digitalRead(buttonPin) == 1)
    {
      ShowState = 1;           // change to case 1 - start the relays
      startTime = millis();    // store the start time
    }   

    break;
  case 1:    // button pressed, start show
   digitalWrite(ReadyLight, LOW);    // turn off Ready Light
   ShowState = 2; 
   
   break;                            // **You missed the "break" here!
  case 2:                            // show is going, start relays
    elapsedTime =   millis();        // store elapsed time

    if (elapsedTime - startTime >= ShowRun)  // while this case loops, check to see if it's time to stop the show

	[...]
    }
    ShowState = 0; // **This line used to be 'ShowState = buttonState;'
    break;
  case 4:    // Unused
//    Serial.println("case 4 - unused");
    break;
  } 
  delay(1);        // delay in between reads for stability
}

So I believe this code is will be functionally equivalent to the OP's code and might even have one less bug (the posted code was missing the 'break' between states 1 and 2).

Is this better or did I miss your point?

Regards,

Brad
KF7FER

Paul, Brad - Thanks for the additional discussion.

Thanks for the catch on the missing "break" statement! However, a new bug was introduced...Brad, in your revised code example, case 3 got combined with case 2. Case 3 is important, because when using the LEDFlasher library, I found that when the timer is up and the code leaves case 2, one or more of the outputs may be left on, so what case 3 does is shut off all the outputs. Then there's a brief pause so the whole sequence won't be triggered again immediately.

Also, my comments in the code refer to both LEDs and relays - I originally set this up with LEDs and then replaced them with a module with 8 relays. It's one of those modules that works the opposite of LEDs - you set a pin to low to turn on the relay due to the circuit on the module.

Thanks again for the help!

Jeff

Jeff-

Sorry it wasn't more clear, but the "[...]" indicates that code from the original posting was omitted and should be included without any changes. I didn't intend to combine case 2 and 3... I just wanted to highlight the changed section.

I guess I should have left out any code after the "[...]" but I wanted to note the state change line.

I hope this makes sense.

Regards,

Brad.

EDIT: If it makes a better example, the complete revised code is as follows:

// Please note any comment lines that begin with "**"
void loop() {
  switch (ShowState) {      // switchCase controls the show based on ShowState
  case 0:                   // button not pressed, waiting 
    digitalWrite(ReadyLight, HIGH);  // turn on Ready Light

    if (digitalRead(buttonPin) == 1)
    {
      ShowState = 1;           // change to case 1 - start the relays
      startTime = millis();    // store the start time
    }   

    break;
  case 1:    // button pressed, start show
   digitalWrite(ReadyLight, LOW);    // turn off Ready Light
   ShowState = 2; 
   
   break;                            // **You missed the "break" here!
  case 2:                            // show is going, start relays
    elapsedTime =   millis();        // store elapsed time

    if (elapsedTime - startTime >= ShowRun)  // while this case loops, check to see if it's time to stop the show
      ShowState = 3;                         // stop the show, go to case 3
    }

    // start relays going.  update keeps them blinking while elapsedTime is watched.
    floodLight.update ();
    shuttleBayDoors.update ();
    impulseEngine.update ();
    strobe.update ();
    navigation.update ();
    torpedoes.update ();
    Z1.update ();
    
    break;
  case 3:    // LEDs off
    
    // turn off all relays, since some may be left on when show is stopped
    digitalWrite(8, HIGH);    
    digitalWrite(9, HIGH);
    digitalWrite(10, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(12, HIGH);
    digitalWrite(7, HIGH);
    digitalWrite(4, HIGH);

 // Reset time, run quick for...next loop

    for(int j = 0; j < ResetTime; j++){  
//      Serial.print(".");
      delay(1000);
    }
    ShowState = 0; // **This line used to be 'ShowState = buttonState;'
    break;
  case 4:    // Unused
//    Serial.println("case 4 - unused");
    break;
  } 
  delay(1);        // delay in between reads for stability
}

Brad - That makes perfect sense. I added the changes to my original code and it's working. Thanks again.

Jeff