button that keeps reading data

hello!
i am working on a simple project that turns ON when the button is pressed once, and OFF when pressed again.
the problem is, once the button is pressed, i have to wait for the entire IF statement to run, then i can turn the "circuit" OFF again if i time the press correctly.
any help?

const int LED1 = 7;
const int LED2 = 8;
const int LED3 = 9;
const int Button = 5;

int buttonState = 0, i = 0;
void setup()
{
  Serial.begin(9600);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(Button, INPUT);
}


void loop()
{
  buttonState = digitalRead(Button);
  if (buttonState == HIGH)
  {
    i++;
  }
  if (i == 1)
  {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    delay(500);
    digitalWrite(LED2, LOW);
    digitalWrite(LED3, HIGH);
    delay(500);
    digitalWrite(LED3, LOW);
  }
  else
  {
    i = 0;
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    digitalWrite(LED3, LOW);
  }
}

study the example “blink without delay”

You probably want to detect whether the button state has changed, instead of whether it is HIGH. The Arduino State Change Detection example shows you how.

And of course, everything else stops while the Arduino is stuck in delay(). The Blink Without Delay example shows you how to avoid that.

jremington:
You probably want to detect whether the button state has changed, instead of whether it is HIGH. The Arduino State Change Detection example shows you how.

And of course, everything else stops while the Arduino is stuck in delay(). The Blink Without Delay example shows you how to avoid that.

okay well, i did study both examples, i fully understood the button one, but still haven't figured out the milli() one, how do you combine both? with 3 LEDs?

when the button is pressed once, you are in a mode where

  • you want Led1 always on
  • you want Led2 HIGH/Led3 LOW for 500ms and Led2 LOW/Led3 HIGH for 500ms --> so every 500ms you have to do something (turn one off, turn one ON). that's what blink without delay shows

when the button is pressed again

  • you want all off

Think of a state machine. You have 3 states: OFF, STATE1, STATE2
OFF == all leds off
STATE1 == led1 ON, Led2 ON, led3 OFF
STATE2 == led1 ON, led2 OFF, led3 ON

Every time through loop

  • check to see if your button has changed state (by the way - how do you have it wired up? It will need a pullup/down resistor unless you declare it as INPUT_PULLUP. Do you have one installed)
    if the button has changed state, you either go the state OFF or state1. Note the time you changed state
    if the amount of time has passed and you are not OFF, go to either STATE1 or STATE2 depending on which state you are in. Note the time again.

-repeat forever!

As you understood the button one, below concentrates on the millis() part. The button part is simulated with a serial input; set serial monitor to "no line ending" if you want to test this code.

Your blinking has a few steps in which different leds light up. This calls for a finite statemachine where you can step through those steps (now called states).

Add the below near the top of your code; it defines the steps/states.

// possible states for the statemachine
enum SM_BLINKSTATE
{
  ON_ON_OFF,
  ON_OFF_ON,
  ON_OFF_OFF,
  OFF_OFF_OFF,
  WAIT_START,
};

The names are based on the state of the leds; the last name indicates that the program is waiting for the 'command' to start the sequence.

Next add a few variables to loop()

void loop()
{
  // current state; we start with the WAIT_START
  static SM_BLINKSTATE currentState = WAIT_START;
  // keep track when we have to do the next action in the state machine
  static uint32_t nextUpdateTime;
  // current time
  uint32_t currentTime = millis();

}

It defines a variable for the current step/state as well as two variables for the timing. The static in front of currentState and nextUpdateTime indicate that those variables will be remembered when the function ends; it's like global variables but they are only known inside the function where the are declared.

Next you can implement the state machine; below uses a switch/case but you can use different approaches.

void loop()
{
  // current state; we start with the WAIT_START
  static SM_BLINKSTATE currentState = WAIT_START;
  // keep track when we have to do the next action in the state machine
  static uint32_t nextUpdateTime;
  // current time
  uint32_t currentTime = millis();

  //////////////////////////////
  // button handling
  //////////////////////////////

  more code will follow here to start/stop the sequence

  //////////////////////////////
  // done with button handling
  //////////////////////////////

  //////////////////////////////
  // state machine
  //////////////////////////////

  switch (currentState)
  {
    case ON_ON_OFF:
      // if it's time
      if (currentTime >= nextUpdateTime)
      {
        // replace this with your digitalWrites
        Serial.print(currentTime); Serial.println(" ON_ON_OFF");
        // set the next update time
        nextUpdateTime += 500;
        Serial.println(nextUpdateTime);
        // goto the next state
        currentState = ON_OFF_ON;
      }
      break;
    case ON_OFF_ON:
      if (currentTime >= nextUpdateTime)
      {
        Serial.print(currentTime); Serial.println(" ON_OFF_ON");
        nextUpdateTime += 500;
        Serial.println(nextUpdateTime);
        currentState = ON_OFF_OFF;
      }
      break;
    case  ON_OFF_OFF:
      if (currentTime >= nextUpdateTime)
      {
        Serial.print(currentTime); Serial.println(" ON_OFF_OFF");
        nextUpdateTime += 500;
        Serial.println(nextUpdateTime);
        currentState = ON_ON_OFF;
      }
      break;
    case OFF_OFF_OFF:
      Serial.print(currentTime); Serial.println(" OFF_OFF_OFF");
      currentState = WAIT_START;
      break;
    case WAIT_START:
      break;
  }

}

With currentState set to WAIT_START, this will happily wait there till the state changes to one of the other states. The button handling will toggle the currentState between OFF_OFF_OFF and the fist step/state (ON_ON_OFF).
Once in ON_ON_OFF, it will check if it's time to actually do something and if so will set the leds to the desired state (simulated by a serial print), determine the next update time and switch to the next state. It will cycle from the step/state ON_ON_OFF via ON_OFF_ON and ON_OFF_OFF back to ON_ON_OFF and so on.

If the button handling decides to change the state to OFF_OFF_OFF, it will immediately do so and next switch to the WAIT_START state.

In the last step you implement the button handler; as said, I used the serial monitor to simulate it

void loop()
{
  // current state; we start with the WAIT_START
  static SM_BLINKSTATE currentState = WAIT_START;
  // keep track when we have to do the next action in the state machine
  static uint32_t nextUpdateTime;
  // current time
  uint32_t currentTime = millis();

  //////////////////////////////
  // button handling
  //////////////////////////////
  
  // this simulates the button; replace it with a state change detection of the button
  if (Serial.available() > 0)
  {
    Serial.read();

    // toggle variable i based on state change of button
    i = !i;
    Serial.print("i = "); Serial.println(i);
    
    if (i == 0)
    {
      // switch all leds off
      currentState = OFF_OFF_OFF;
    }
    else
    {
      // start the blink sequence
      currentState = ON_ON_OFF;
      // set the next update time to the current time so it's immediately executed
      nextUpdateTime = currentTime;
    }
  }

  //////////////////////////////
  // done with button handling
  //////////////////////////////

  //////////////////////////////
  // state machine
  //////////////////////////////

  above state machine here
}

Based on the state of your variable i, the currentState will be set to OFF_OFF_OFF which switched the leds off or to ON_ON_OFF whicg is the first step/state of your sequence. You should rename i to something more sensible (e.g. runState). Further only beginners and idiots use single letter global variables.

The full code with a renamed i

const int LED1 = 7;
const int LED2 = 8;
const int LED3 = 9;
const int Button = 5;

int buttonState = 0, runState = 0;

// possible states for the statemachine
enum SM_BLINKSTATE
{
  ON_ON_OFF,
  ON_OFF_ON,
  ON_OFF_OFF,
  OFF_OFF_OFF,
  WAIT_START,
};

void setup()
{
  Serial.begin(57600);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(Button, INPUT);
}

void loop()
{
  // current state; we start with the WAIT_START
  static SM_BLINKSTATE currentState = WAIT_START;
  // keep track when we have to do the next action in the state machine
  static uint32_t nextUpdateTime;
  // current time
  uint32_t currentTime = millis();

  //////////////////////////////
  // button handling
  //////////////////////////////

  // this simulates the button; replace it with a state change detection of the button
  if (Serial.available() > 0)
  {
    Serial.read();

    runState = !runState;
    Serial.print("runState = "); Serial.println(runState);
    if (runState == 0)
    {
      // switch all leds off
      currentState = OFF_OFF_OFF;
    }
    else
    {
      // start the blink sequence
      currentState = ON_ON_OFF;
      nextUpdateTime = currentTime;
    }
  }

  //////////////////////////////
  // done with button handling
  //////////////////////////////

  //////////////////////////////
  // state machine
  //////////////////////////////

  switch (currentState)
  {
    case ON_ON_OFF:
      // if it's time
      if (currentTime >= nextUpdateTime)
      {
        // replace this with your digitalWrites
        Serial.print(currentTime); Serial.println(" ON_ON_OFF");
        nextUpdateTime += 500;
        Serial.println(nextUpdateTime);
        currentState = ON_OFF_ON;
      }
      break;
    case ON_OFF_ON:
      if (currentTime >= nextUpdateTime)
      {
        Serial.print(currentTime); Serial.println(" ON_OFF_ON");
        nextUpdateTime += 500;
        Serial.println(nextUpdateTime);
        currentState = ON_OFF_OFF;
      }
      break;
    case  ON_OFF_OFF:
      if (currentTime >= nextUpdateTime)
      {
        Serial.print(currentTime); Serial.println(" ON_OFF_OFF");
        nextUpdateTime += 500;
        Serial.println(nextUpdateTime);
        currentState = ON_ON_OFF;
      }
      break;
    case OFF_OFF_OFF:
      Serial.print(currentTime); Serial.println(" OFF_OFF_OFF");
      currentState = WAIT_START;
      break;
    case WAIT_START:
      break;
  }
}

Hope this helps; can be improved.