Switching over 2+ different states with a push button

Hi all,

First, thank you all, for this forum and all the tips I've found so far without even having to setup an account ! but on this on, I'm really stuck so I'm calling for help :confused:

I have a traffic light (a real one!) connected to an arduino. I'm working on different functions to turn off and on the 3 lights :

  • STATE1 = 3 lights always ON
  • STATE2 = each light is ON for 3 sec
  • STATE3 = each light is ON when the low, mid or high frequency is above average (I have a microphone connected to it and I do the FFT of its output analog signal)

These 3 functions work perfectly but I would like to switch between each other with a button. I went through a lot of tutorials, but not so many are studying the case of more than 2 cases. And I can't find the right algorithm to do the following.

Initial state : all lights ON (STATE1)
STATE1 run

Press button -> switch to STATE2
STATE2 run

Press button -> switch to STATE3
STATE3 run

Press button -> switch to STATE1
STATE 1 run

etc...

Here is the beginning of code I took from here and I modified to fit my problem. But I didn't go too far because I really don't see how to set this up. So the code doesn't work.

My main issues are the following :

  • I don't see how to make different cases similar to what is written in the project I took the code from
  • With STATE2 running during 9 sec, how can I switch to STATE3 ? Can the Arduino remember that I pressed the button and switch state at the end of STATE2 ?
  • How can my initial state (in my code) can also be STATE1 ?

Some help would be more than appreciated !

Cheers

const int state=0;

const int  buttonPin = 2;    // the pin that the pushbutton is attached to
int previousState = LOW;   // the previous reading from the input pin
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;


void setup()
{
    pinMode(12, OUTPUT); // the three lights
    pinMode(11, OUTPUT);
    pinMode(10, OUTPUT);

    pinMode(buttonPin, INPUT_PULLUP); //connect to 20Kohm internal resistance and 5V
}


void loop (){

    // Initial state
    digitalWrite(12, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(10, HIGH);


    int reading = digitalRead(buttonPin);
    if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay)
    {

         digitalWrite(12, HIGH);
         delay(3000);
         digitalWrite(12, LOW);
 
         digitalWrite(11, HIGH);
         delay(3000);
         digitalWrite(11, LOW);

         digitalWrite(10, HIGH);
         delay(3000);
         digitalWrite(10, LOW);

     state++;
    }

Initial state : all lights ON (STATE1)
STATE1 run

Press button -> switch to STATE2
STATE2 run

Press button -> switch to STATE3
STATE3 run

Press button -> switch to STATE1
STATE 1 run

I don't have time right now to answer in detail, but:

  • For an initial state, use an enum as shown in example 4 here, where it sets ledstate = LEDOFF to start
  • To click between states, have a look at the state change detect example which increments a counter with each new new press.

Hi 12Stepper,

Thanks for your answer. I've seen the code with the increment counter at each change of state, and I like the idea of using a modulo, but I feel I gonna get some trouble with modulo 1 and modulo2.

The link to the state machine example is very interesting and this is the same idea used by the person I took the code from. My main question with this technic is : what happen if I press the button during the 9s cycle ? Does it switch to the next STATE afterward ? I'll try tomorrow

Thank you again

PonyoDC:
what happen if I press the button during the 9s cycle

Nothing, because it's made of delay()s.

To get decent response from inputs, you need a delay()-less approach using millis()-based timing so nothing gets blocked.

I've found a solution !

I'm using the button library to make things easier.

My other problem was the infinite loop during the 2 sec delay for each light. I don't see any way to solve this problem since the program stops for 2 sec. But the trick is to keep pressing the button until it finishes its loop.

I've also tried with the "case" method which worked well, but I want the function in the case to be infinite so I need a while loop anyway.

#include "Button.h"

// Button parameters
Button button1(2);


void setup()
    {

    button1.begin();

    pinMode(12, OUTPUT);
    pinMode(10, OUTPUT);
    pinMode(11, OUTPUT);

    }
    


void loop (){
  

  while(button1.pressed() == false)  // all lights ON
  {
    digitalWrite(12, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(10, HIGH);
  }



  while(button1.pressed() == false) // each light is ON one by one for 2 sec
  {
    digitalWrite(12, HIGH);
    digitalWrite(11, LOW);
    digitalWrite(10, LOW);
    delay(2000);

    digitalWrite(12, LOW);
    digitalWrite(11, HIGH);
    digitalWrite(10, LOW);
    delay(2000);

    digitalWrite(12, LOW);
    digitalWrite(11, LOW);
    digitalWrite(10, HIGH);
    delay(2000);
  }

}

PonyoDC:
My other problem was the infinite loop during the 2 sec delay for each light. I don't see any way to solve this problem since the program stops for 2 sec. But the trick is to keep pressing the button until it finishes its loop.

.... the keyword there being delay, so the program does indeed stop for 2 sec.

The sketch below is the blink without delay example, with another led and a button. The second led responds immediately to the button, even though the original led is blinking away happily. Each time through loop() it checks if it's time to toggle the original led, which it does or doesn't do as appropriate. It immediately gets to the new lines of code at the bottom which then reads the new button and ons or offs the new led, and back to the top of loop() ready to check if it's time to toggle the led.

/*
  Blink without Delay ***WITH ANOTHER LED ON A BUTTON***

  Turns on and off a light emitting diode (LED) connected to a digital pin,
  without using the delay() function. This means that other code can run at the
  same time without being interrupted by the LED code.

  The circuit:
  - Use the onboard LED.
  - Note: Most Arduinos have an on-board LED you can control. On the UNO, MEGA
    and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN
    is set to the correct LED pin independent of which board is used.
    If you want to know what pin the on-board LED is connected to on your
    Arduino model, check the Technical Specs of your board at:
    https://www.arduino.cc/en/Main/Products

  created 2005
  by David A. Mellis
  modified 8 Feb 2010
  by Paul Stoffregen
  modified 11 Nov 2013
  by Scott Fitzgerald
  modified 9 Jan 2017
  by Arturo Guadalupi

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
*/

// constants won't change. Used here to set a pin number:
const int ledPin =  2;// the number of the LED pin
const int ledPin2 = 7;
const int buttonPin = 4;

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }

  //THESE LINES ADDED TO TURN THE OTHER LED ON / WITH BUTTON
  //    THIS LED RESPONDS IMMEDIATELY
  if (!digitalRead(buttonPin))  digitalWrite(ledPin2, HIGH);
      else digitalWrite(ledPin2, LOW);


  }//loop