Button first press runs a sequence second press stops it and waits to repeat?

Hello all,

I have worked through as many examples as I could find but I am not getting something right.

I understand this program is basic and not the best way to sequence something but here is what I have so far. Just starting out please have a look. Right now the button is pressed and it runs the sequence on time and waits for button press to start again.

I would like to press the button the first time, loop sequence until button is pressed a second time then stop sequence and wait for button press to repeat above.

// cycle test

const int buttonPin = 12;
const int USB1 = 1;                 //assigns digital output 1 the name USB1
const int fan1 = 2;                 //assigns digital output 2 the name fan1
const int LED1 = 3;                 //assigns digital output 3 the name LED1
const int USB2 = 4;                 //assigns digital output 4 the name USB2
const int fan2 = 5;                 //assigns digital output 5 the name fan2
const int LED2 = 6;                 //assigns digital output 6 the name LED2
const int USB3 = 7;                 //assigns digital output 7 the name USB3
const int fan3 = 8;                 //assigns digital output 8 the name fan3
const int LED3 = 9;                 //assigns digital output 9 the name LED3
; int x = 0

          ; int buttonState = 0;                          // variables will change:
int oldButtonState = LOW;


void setup()
{
  oldButtonState = digitalRead(buttonPin);

  pinMode(buttonPin, OUTPUT);
  pinMode(USB1, OUTPUT);             //initalizes all 9 outputs
  pinMode(fan1, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(USB2, OUTPUT);
  pinMode(fan2, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(USB3, OUTPUT);
  pinMode(fan3, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(buttonPin, INPUT);

}

void loop()
{
  //check if button pressed, if so enter program condition (inside if statement)
  int newButtonState = digitalRead(buttonPin);

  if (newButtonState == HIGH && oldButtonState == LOW) {
    if (x == 0) {

      // Start sequencing all loads in order on boards 1 thru 3
      delay(1000);

      // Turn on USB1
      digitalWrite(USB1, LOW);         // close USB1 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(USB1, HIGH);        // open USB1 contacts
      delay(600);                      // delay xxxx sec

      // Turn on fan1
      digitalWrite(fan1, LOW);         // close fan1 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(fan1, HIGH);        // open fan1 contacts
      delay(600);                      // delay xxxx sec

      // Turn on LED1
      digitalWrite(LED1, LOW);         // close LED1 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(LED1, HIGH);        // open LED1 contacts
      delay(600);                      // delay xxxx sec

      delay(1000);                     // Board #2

      // Turn on USB2
      digitalWrite(USB2, LOW);         // close USB2 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(USB2, HIGH);        // open USB2 contacts
      delay(600);                      // delay xxxx sec

      // Turn on fan2
      digitalWrite(fan2, LOW);         // close fan2 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(fan2, HIGH);        // open fan2 contacts
      delay(600);                      // delay xxxx sec

      // Turn on LED2
      digitalWrite(LED2, LOW);         // close LED2 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(LED2, HIGH);        // open LED2 contacts
      delay(600);                      // delay xxxx sec

      delay(1000);                     // Board #3

      // Turn on USB3
      digitalWrite(USB3, LOW);         // close USB3 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(USB3, HIGH);        // open USB3 contacts
      delay(600);                      // delay xxxx sec

      // Turn on fan3
      digitalWrite(fan3, LOW);         // close fan3 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(fan3, HIGH);        // open fan3 contacts
      delay(600);                      // delay xxxx sec

      // Turn on LED3
      digitalWrite(LED3, LOW);         // close LED3 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(LED3, HIGH);        // open LED3 contacts
      delay(600);                      // delay xxxx sec`

      delay(100);

      int X = 1;

    } else {

      x = 0;

    }
  }
  oldButtonState = newButtonState;
}

this is a simple state machine - here is an example how you could deal with different states (ignoring bouncing with just a small delay)

Basically you start in the PAUSED state when you press the button you move to the PRESSED_WAIT_RELEASE_TO_RUN state when you release the button you move to the RUNNING state when you press the button you move to the PRESSED_WAIT_RELEASE_TO_PAUSE state when you release the button you move to the PAUSED state and cycle ready to start again.

then the loop() basically updates the state and if you are in the RUNNING state does something (that should not be too long so that you can capture button presses timely)

const byte buttonPin = 2;

enum : byte {PAUSED, PRESSED_WAIT_RELEASE_TO_RUN, RUNNING, PRESSED_WAIT_RELEASE_TO_PAUSE} state;

void setup() {
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP);
  state = PAUSED;
}

void updateState()
{
  switch (state) {
    case PAUSED:
      if (digitalRead(buttonPin) == LOW) {
        // button is being pressed, switch state
        state = PRESSED_WAIT_RELEASE_TO_RUN;
        Serial.println("PRESSED");
        delay(20); // poor's man debounce :)
      }
      break;

    case PRESSED_WAIT_RELEASE_TO_RUN:
      if (digitalRead(buttonPin) == HIGH) {
        // button is being released, switch state
        state = RUNNING;
        Serial.println("RELEASED");
        delay(20); // poor's man debounce :)
      }
      break;

    case RUNNING:
      if (digitalRead(buttonPin) == LOW) {
        // button is being pressed, switch state
        state = PRESSED_WAIT_RELEASE_TO_PAUSE;
        Serial.println("PRESSED");
        delay(20); // poor's man debounce :)
      }
      break;

    case PRESSED_WAIT_RELEASE_TO_PAUSE:
      if (digitalRead(buttonPin) == HIGH) {
        // button is being released, switch state
        state = PAUSED;
        Serial.println("RELEASED");
        delay(20); // poor's man debounce :)
      }
      break;
  }
}

void loop() {
  updateState();
  if (state == RUNNING) {
    Serial.println(millis()); // THIS SHOULD BE KEPT SHORT, LIKE ONE STEP IN YOUR SEQUENCE
  }
}

connect a wire to pin 2 and touch GND to simulate a button press, lift the wire to simulate the button release

The 24 second wait for the sequence to finish before the button is read again is going to be a bit awkward, don’t you think ?

You need to restructure your program and use millis() for not blocking timing.

Hi, J_M_L

Thanks for your information!!!

This is allot of information to absorb for me as I'm new to all this. I think I can grasp what your method is but i am not sure of all the different functions and how the instructions work yet. So the state machine is a method to plan how the program will flow? and the case statements are how this machine gets executed? I appreciate all your help.

Humble Beginner Mike

a state machine is a simple concept: your arduino is receiving "events" and these "events" can change the current state to a new state

Based on current state, you decide what to do

The structure of the code above is simple, when I start I assume the system is PAUSED. Then in the loop() I test first if i have received an event (here the only event we care about is that state of the button, pressed or not). in the PAUSED state, I only want to deal with the button pressed event, so I look if the button is LOW (with an INPUT_PULLUP, LOW means button pressed) and if it is, I change state to PRESSED_WAIT_RELEASE_TO_RUN in order to remember that now I'm no longer PAUSED, the button is pressed and I'll be ready to RUN as soon as the user releases the button.

In the PRESSED_WAIT_RELEASE_TO_RUN state, I check if the button is HIGH, which means the user just released the button and in that case it's time to run so I change the state to RUNNING (which is then used in the loop to decide to do stuff).

similarly if I press again the button while running, then I go to a new state waiting for the release and upon release I go back to the PAUSED state

makes sense?

Hi J_M_L

I do understand most of what you are explaining, Thanks again for your detail!

I am not sure what this does.

void updateState() { switch (state) { case PAUSED:

Sooo this is where I would insert the code i want to loop?

void loop() { updateState(); if (state == RUNNING) { Serial.println(millis()); // THIS SHOULD BE KEPT SHORT, LIKE ONE STEP IN YOUR SEQUENCE } }

Until this happens?

case PRESSED_WAIT_RELEASE_TO_PAUSE: if (digitalRead(buttonPin) == HIGH) { // button is being released, switch state state = PAUSED; Serial.println("RELEASED"); delay(20); // poor's man debounce :) } break;

Thanks in advance! Mike

what you want to happen needs to be replacing the Serial.println(millis()); I had put in the loop

  if (state == RUNNING) {
    Serial.println(millis()); // THIS SHOULD BE KEPT SHORT, LIKE ONE STEP IN YOUR SEQUENCE
  }

if you run my code you'll see that if you press and release the button, the console starts displaying the value of millis and if you press the button again it stops printing (at least it should, I did not test :) )

Hi J_M_L

Yes i have run your code and it does what you had intended! Nice work!

I have inserted part of my sequence for testing. I will keep adding my loop elements to see what happens. This is cool!!

I am not sure what this does.

void updateState() { switch (state) { case PAUSED:

Well the updateState function needs to be called often as this is the one maintaining the state of your system - it checks what you do on the button

Hi J_M_L

So I've been adding my loop elements to your code and notice the more I add instructions to the loop the longer it takes the micro to read the switch to enter pause mode.

So it appears all the items in the loop must be executed before it reads the code at the beginning of the script to read the button. Is that correct?

How can I perform all my loop functions but not wait to read the switch until they are all gone through?

Thanks Mike

You need to break up your long code in smaller chunks - what are you trying to do ?

Hi J_M_L

I want to run the sequence:

 // Start sequencing all loads in order on boards 1 thru 3
      delay(1000);

      // Turn on USB1
      digitalWrite(USB1, LOW);         // close USB1 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(USB1, HIGH);        // open USB1 contacts
      delay(600);                      // delay xxxx sec

      // Turn on fan1
      digitalWrite(fan1, LOW);         // close fan1 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(fan1, HIGH);        // open fan1 contacts
      delay(600);                      // delay xxxx sec

      // Turn on LED1
      digitalWrite(LED1, LOW);         // close LED1 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(LED1, HIGH);        // open LED1 contacts
      delay(600);                      // delay xxxx sec

      delay(1000);                     // Board #2

      // Turn on USB2
      digitalWrite(USB2, LOW);         // close USB2 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(USB2, HIGH);        // open USB2 contacts
      delay(600);                      // delay xxxx sec

      // Turn on fan2
      digitalWrite(fan2, LOW);         // close fan2 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(fan2, HIGH);        // open fan2 contacts
      delay(600);                      // delay xxxx sec

      // Turn on LED2
      digitalWrite(LED2, LOW);         // close LED2 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(LED2, HIGH);        // open LED2 contacts
      delay(600);                      // delay xxxx sec

      delay(1000);                     // Board #3

      // Turn on USB3
      digitalWrite(USB3, LOW);         // close USB3 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(USB3, HIGH);        // open USB3 contacts
      delay(600);                      // delay xxxx sec

      // Turn on fan3
      digitalWrite(fan3, LOW);         // close fan3 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(fan3, HIGH);        // open fan3 contacts
      delay(600);                      // delay xxxx sec

      // Turn on LED3
      digitalWrite(LED3, LOW);         // close LED3 contacts
      delay(600);                      // delay xxxx sec
      digitalWrite(LED3, HIGH);        // open LED3 contacts
      delay(600);                      // delay xxxx sec`

You need to get rid of all the delays Study the post anchored at the top of the forum about using millis()

Hi J-M-L, I have read and tried to write the code using millis() but I cannot get it to work right... Arrrrrrgggggg I tried replacing all the output sections using delay in my loop with millis code but I cant seem to get it working..

Look over post #3 in this recent thread.

Thanks dougp

That's way over my head for the beginner I am...but thanks anyway!

I just want to press a button once. provide a high to low transition and delay using millis then repeat this 9 more times on different outputs and loop this until the button is pressed a second time to stop loop.

unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;
const unsigned long period = 2000;  //the value is a number of milliseconds

const int buttonPin = 11;
const int USB1 = 2;                 //assigns digital output 10 the name USB1
const int fan1 = 3;                 //assigns digital output 2 the name fan1
const int LED1 = 4;                 //assigns digital output 3 the name LED1
const int USB2 = 5;                 //assigns digital output 4 the name USB2
const int fan2 = 6;                 //assigns digital output 5 the name fan2
const int LED2 = 7;                 //assigns digital output 6 the name LED2
const int USB3 = 8;                 //assigns digital output 7 the name USB3
const int fan3 = 9;                 //assigns digital output 8 the name fan3
const int LED3 = 10;
enum : byte {PAUSED, PRESSED_WAIT_RELEASE_TO_RUN, RUNNING, PRESSED_WAIT_RELEASE_TO_PAUSE} state;

void setup() {
  startMillis = millis();  //initial start time
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP);
  state = PAUSED;
  pinMode(buttonPin, OUTPUT);
  pinMode(USB1, OUTPUT);             //initalizes all 9 outputs
  pinMode(fan1, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(USB2, OUTPUT);
  pinMode(fan2, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(USB3, OUTPUT);
  pinMode(fan3, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void updateState()
{
  switch (state) {
    case PAUSED:
      if (digitalRead(buttonPin) == LOW) {
        // button is being pressed, switch state
        state = PRESSED_WAIT_RELEASE_TO_RUN;
        Serial.println("PRESSED");
        delay(20); // poor's man debounce :)
      }
      break;

    case PRESSED_WAIT_RELEASE_TO_RUN:
      if (digitalRead(buttonPin) == HIGH) {
        // button is being released, switch state
        state = RUNNING;
        Serial.println("RELEASED");
        delay(20); // poor's man debounce :)
      }
      break;

    case RUNNING:
      if (digitalRead(buttonPin) == LOW) {
        // button is being pressed, switch state
        state = PRESSED_WAIT_RELEASE_TO_PAUSE;
        Serial.println("PRESSED");
        delay(20); // poor's man debounce :)
      }
      break;

    case PRESSED_WAIT_RELEASE_TO_PAUSE:
      if (digitalRead(buttonPin) == HIGH) {
        // button is being released, switch state
        state = PAUSED;
        Serial.println("RELEASED");
        delay(20); // poor's man debounce :)
      }
      break;
  }
}

void loop() {
  updateState();
  if (state == RUNNING)
  {
    // Turn on USB1

    digitalWrite(USB1, LOW);         // close USB1 contacts
    if (currentMillis - startMillis >= period)  //test whether the period has elapsed
      startMillis = currentMillis;  //IMPORTANT to save the start time of the current LED state.
    {
      digitalWrite(USB1, HIGH);        // open USB1 contacts
      if (currentMillis - startMillis >= period)  //test whether the period has elapsed
        startMillis = currentMillis;  //IMPORTANT to save the start time of the current LED state.
    }

    // Turn on USB1
    digitalWrite(USB1, LOW);         // close USB2 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(USB1, HIGH);        // open USB2 contacts
    delay(600);                      // delay xxxx sec


    // Turn on fan1
    digitalWrite(fan1, LOW);         // close fan1 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(fan1, HIGH);        // open fan1 contacts
    delay(600);                      // delay xxxx sec

    // Turn on LED1
    digitalWrite(LED1, LOW);         // close LED1 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(LED1, HIGH);        // open LED1 contacts
    delay(600);                      // delay xxxx sec

    delay(1000);                     // Board #2

    // Turn on USB2
    digitalWrite(USB2, LOW);         // close USB2 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(USB2, HIGH);        // open USB2 contacts
    delay(600);                      // delay xxxx sec

    // Turn on fan2
    digitalWrite(fan2, LOW);         // close fan2 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(fan2, HIGH);        // open fan2 contacts
    delay(600);                      // delay xxxx sec

    // Turn on LED2
    digitalWrite(LED2, LOW);         // close LED2 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(LED2, HIGH);        // open LED2 contacts
    delay(600);                      // delay xxxx sec

    delay(1000);                     // Board #3

    // Turn on USB3
    digitalWrite(USB3, LOW);         // close USB3 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(USB3, HIGH);        // open USB3 contacts
    delay(600);                      // delay xxxx sec

    // Turn on fan3
    digitalWrite(fan3, LOW);         // close fan3 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(fan3, HIGH);        // open fan3 contacts
    delay(600);                      // delay xxxx sec

    // Turn on LED3
    digitalWrite(LED3, LOW);         // close LED3 contacts
    delay(600);                      // delay xxxx sec
    digitalWrite(LED3, HIGH);        // open LED3 contacts
    delay(600);                      // delay xxxx sec`
    delay(5000);                      // delay xxxx sec`


  }
}

As simple as this project sounds, it is quite complex for a beginner - because you are juggling two concepts without taking a breath.

The state-machine is fundamental to operating your device, and J-M-L has introduced it nicely, but in the ‘run’ state, you can’t forget about maintaining the state-machine - yet have to keep the outputs changing, while not ignoring possible button presses.

If you understood all that, you know where you have to be, but wrapping your new head around it can be tricky!

Try @LarryD’s State Machine Tutorial. The thread includes flowcharts to make visual what is happening.

When RUNNING state gets activated (so in the state machine updateState() function) capture the start time in a global unsigned long and set up a global byte variable currentInterval = 0; Then in the loop you could do something like

  updateState();
  if (state == RUNNING)  {
   // check in which time interval we are and take action upon entering that interval

   // ========= Interval [0...t1] =========
    if (millis()-startTime <= t1) {
       if (currentInterval <= 0) {
          // here you are entering for first time in the first interval [0...t1], ensure USB1 is as you want
         ....
         currentInterval = 1;
      }
      // here you can do other things whilst knowing you are in that interval

    } else

   // ========= Interval ]t1...t2] =========
    if (millis()-startTime <= t2) {
       if (currentInterval <= 1) {
          // here you are entering for first time in the second interval ]t1...t2], ensure USB1 is as you want and trigger new set of output
         ....
         currentInterval = 2;
      }
      // here you can do other things whilst knowing you are in that interval

    } else

   // ========= Interval ]t2...t3] =========
    if (millis()-startTime <= t3) {
       if (currentInterval <= 2) {
          // here you are entering for first time in the third interval ]t2...t3], take action
         ....
         currentInterval = 3;
      }
      // here you can do other things whilst knowing you are in that interval

    } else

   // ========= Interval ]t3...t4] =========
    if (millis()-startTime <= t4) {
       if (currentInterval <= 3) {
          // here you are entering for first time in the fourth interval ]t3...t4], take action
         ....
         currentInterval = 4;
      }
      // here you can do other things whilst knowing you are in that interval

    } // else .... etc you get the idea

...
  }
....

(code typed here into the forum, untested)
You obviously need to define constant t1,t2,t3,t4,… which are the relative moments when things need to happen. This way you know where you are relative to the button press event and ensure correct action is taken. This is another small state machine inside the RUNNING state

====
As a side note

const int buttonPin = 11;
const int USB1 = 2;                 //assigns digital output 10 the name USB1
const int fan1 = 3;                 //assigns digital output 2 the name fan1
const int LED1 = 4;                 //assigns digital output 3 the name LED1
const int USB2 = 5;                 //assigns digital output 4 the name USB2
const int fan2 = 6;                 //assigns digital output 5 the name fan2
const int LED2 = 7;                 //assigns digital output 6 the name LED2
const int USB3 = 8;                 //assigns digital output 7 the name USB3
const int fan3 = 9;                 //assigns digital output 8 the name fan3
const int LED3 = 10;

it is good to have relevant names for the pins but pins are best declared as const byte and you need to ensure comments are matching what you do. There is NO excuse for keeping stupid comments in a code, they confuse everyone…

Point taken, I changed code without changing my comments...Man if i were only perfect again!