5

So, I've just started programming Arduino (and also in general), so I'm doing basic things, like switching on and off LED's.

I've made them light in a sequence and then turning them off (in the same sequence), and now I wanted to use 2 buttons, one to make them do the same thing as before (light and turning LED's off in sequence) repeatedly, and the other to turn them off.

The problem is that curently they only light up once, and not repeatedly, I think I would've needed another loop, but i found out that you can't use 2 of them.

Can anyone help me? here's the code

int LED1 = 2;
int LED2 = 3;
int LED3 = 4;
int LED4 = 5;
int LED5 = 6;
int buttonON = 7;
int buttonOFF = 8;

void setup()
{
  pinMode(LED1, OUTPUT); 
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(LED5, OUTPUT);
  pinMode(buttonON, INPUT_PULLUP);
  pinMode(buttonOFF, INPUT_PULLUP);
}

void loop ()
{
  if (digitalRead(buttonON)== LOW)
  {
    delay(300), digitalWrite(LED1, HIGH);
    delay(50), digitalWrite(LED2, HIGH);
    delay(50), digitalWrite(LED3, HIGH);
    delay(50), digitalWrite(LED4, HIGH);
    delay(50), digitalWrite(LED5, HIGH);
    delay(300), digitalWrite(LED1, LOW);
    delay(100), digitalWrite(LED2, LOW);
    delay(100), digitalWrite(LED3, LOW);
    delay(100), digitalWrite(LED4, LOW);
    delay(100), digitalWrite(LED5, LOW);
  }
 if (digitalRead(buttonOFF)== LOW)
 {
  digitalWrite(LED1, LOW);
  digitalWrite(LED2, LOW);
  digitalWrite(LED3, LOW);
  digitalWrite(LED4, LOW);
  digitalWrite(LED5, LOW);
 }
}

P.S. if anyone has other general tips I would really apreciate them

4
  • 3
    insert a layer of separation between the button press and the LED lighting ... use a flag variable .... if (digitalRead(buttonON)== LOW) ledsON = true; .... then further down in the code if (ledsON) { / led lighting code } ... you can set/clear ledsON flag any number of times, so you could check the button after every led is lit ... that way you don't have to hold the button until the sequence is finished
    – jsotola
    Jul 20, 2021 at 2:44
  • 1
    good way of asking question ;) Nicely written, formatted +1
    – ArduinoFan
    Jul 20, 2021 at 9:43
  • 1
    I take it you are new to programming?
    – user253751
    Jul 20, 2021 at 11:17
  • yes, like i said, I've just started, and decided to start programming with Arduino
    – AndreFro
    Jul 20, 2021 at 18:04

3 Answers 3

3

You have written your question beautifully. In your case, according to your logic, the button has to be kept pressed, so that the LEDs are lit in the order you desire. The single loop() function is part of the Arduino sketch but it is never a limitation.

One way to solve your problem is to make the LED drive logic independent of the button press state.

  • Once you detect the button press, store the event in one variable.
  • Let us call it ButtonStatus.
  • So, soon after the keypress is detected, update the ButtonStatus variable to TRUE.
  • Your LED drive logic can check this variable value to decide whether to drive the LEDs or not
  • Soon after you detect another press of the second button, you can update the variable value to FALSE.
    Here is the Arduino simulation output for your code and the code suggested by @cat

enter image description here

below is after adapting the solution from cat enter image description here

The simulation links are here: Original problem solution 1

int LED1 = 2;
int LED2 = 3;
int LED3 = 4;
int LED4 = 5;
int LED5 = 6;
int buttonON = 7;
int buttonOFF = 8;
int ledEffectON;
void setup()
{
  pinMode(LED1, OUTPUT); 
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(LED5, OUTPUT);
  pinMode(buttonON, INPUT_PULLUP);
  pinMode(buttonOFF, INPUT_PULLUP);
}

void loop ()
{
  if (digitalRead(buttonON)== LOW)
      ledEffectON = true;

  if (digitalRead(buttonOFF)== LOW)
      ledEffectON = false;

  if(ledEffectON)
  {
    delay(300), digitalWrite(LED1, HIGH);
    delay(50), digitalWrite(LED2, HIGH);
    delay(50), digitalWrite(LED3, HIGH);
    delay(50), digitalWrite(LED4, HIGH);
    delay(50), digitalWrite(LED5, HIGH);
    delay(300), digitalWrite(LED1, LOW);
    delay(100), digitalWrite(LED2, LOW);
    delay(100), digitalWrite(LED3, LOW);
    delay(100), digitalWrite(LED4, LOW);
    delay(100), digitalWrite(LED5, LOW);
  }
}

Link to docs on Pushbuttons. Wokwi Arduino simulator simulates debounce by default - just like real-world switched. Debounce is a very important technique that you should consider adding to the code. To disable the "bounce" option, use the below trick!

{ "bounce": "0" }
3
  • 2
    Thanks, really apreciate the time you spent on explaining it
    – AndreFro
    Jul 20, 2021 at 11:10
  • 2
    Very nice diagrams! Jul 20, 2021 at 12:12
  • @Juraj brought in resistors. I have also updated the links referred to. Now you should see the current limiting resistors as well!
    – ArduinoFan
    Jul 21, 2021 at 18:28
6

By using delay() it blocks the rest of the code. The programme needs to keep track of two buttons and update the lightshow at appropriate times, therefore a non-blocking style of coding is needed similar to the Blink Without Delay example where the delay() is replaced with a timer using millis() that is checked periodically. This allows other things to be checked periodically as well, e.g. the buttons.

Also the button contacts tend to bounce several times for several milliseconds when they change state sending a train of on/off pulses to the MCU, so they need to be debounced. You can find many debouncing algorithms online, but here's a simple debouncer that I put on GitHub.

void loop ()
{
  static bool doLightshow = false;

  static Debouncer buttonOn(buttonON);
  static Debouncer buttonOff(buttonOFF);

  buttonOn.Update();
  buttonOff.Update();

  if (buttonOn.Fall())
  {
    Serial.println("buttonOn falling edge.");
    doLightshow = true;
  }
  else if (buttonOff.Fall())
  {
    Serial.println("buttonOff falling edge.");
    doLightshow = false;
  }

  if (doLightshow)
  {
    // Do lightshow.
    . . .
  }
  else
  {
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    digitalWrite(LED3, LOW);
    digitalWrite(LED4, LOW);
    digitalWrite(LED5, LOW);
  }
}

Due to the way you've neatly written your code, you can probably pick out a pattern in the lightshow.

State Delay time LED pin LED state
0 300 LED1 HIGH
1 50 LED2 HIGH
2 50 LED3 HIGH
3 50 LED4 HIGH
4 50 LED5 HIGH
5 300 LED1 LOW
6 100 LED2 LOW
7 100 LED3 LOW
8 100 LED4 LOW
9 100 LED5 LOW

This lends itself well to creating a Finite State Machine that can be stored in an array of structures:

typedef struct LightshowData
{
  unsigned int delayTime;
  byte ledPin;
  bool ledState;
};

const byte numStates = 10;

const LightshowData lightshowData[numStates] =
{
  { 300, LED1, HIGH },
  {  50, LED2, HIGH },
  {  50, LED3, HIGH },
  {  50, LED4, HIGH },
  {  50, LED5, HIGH },
  { 300, LED1, LOW },
  { 100, LED2, LOW },
  { 100, LED3, LOW },
  { 100, LED4, LOW },
  { 100, LED5, LOW }
};

The state machine can now cycle through the array in a non-blocking manner:

    // Do lightshow.
    static byte state = 0;
    unsigned long currentTimestamp = millis();
    static unsigned long previousTimestamp = currentTimestamp;

    if (currentTimestamp - previousTimestamp >= lightshowData[state].delayTime)
    {
      previousTimestamp += lightshowData[state].delayTime;
      digitalWrite(lightshowData[state].ledPin, lightshowData[state].ledState);
      state = ++state % numStates;
      Serial.print("state = ");
      Serial.println(state);
    }

You may want to reset state to 0 after a negative edge on buttonOff, otherwise the lightshow will continue where it left off after being stopped and restarted.

Here's the Wokwi simulation of the lightshow with 250 Ω protection resistors for the LEDs.

Wokwi simulates button bounce by default to more accurately model the real world, so it's advisable to have some form of button debouncing.

2
  • 2
    thanks man, looks a bit difficult, but I'll certanly check it out
    – AndreFro
    Jul 20, 2021 at 11:11
  • 3
    When the loop() function does not need any time ( avoid delay and nested time consuming loops ) your program logic is completely different from before, but more flexible and easier, once you got used to it. Jul 20, 2021 at 11:12
2

You simply need to save the state of the LEDs in a new variable ...

void loop ()
{
  if (digitalRead(buttonON)== LOW)
      ledEffectON = true;

  if (digitalRead(buttonOFF)== LOW)
      ledEffectON = false;

  if(ledEffectON)
  {
    delay(300), digitalWrite(LED1, HIGH);
    delay(50), digitalWrite(LED2, HIGH);
    delay(50), digitalWrite(LED3, HIGH);
    delay(50), digitalWrite(LED4, HIGH);
    delay(50), digitalWrite(LED5, HIGH);
    delay(300), digitalWrite(LED1, LOW);
    delay(100), digitalWrite(LED2, LOW);
    delay(100), digitalWrite(LED3, LOW);
    delay(100), digitalWrite(LED4, LOW);
    delay(100), digitalWrite(LED5, LOW);
  }
}
4
  • 3
    wokwi.com/arduino/projects/304626706673566272 Wonderful solution!
    – ArduinoFan
    Jul 20, 2021 at 9:40
  • 3
    Thanks for the short and simple answer
    – AndreFro
    Jul 20, 2021 at 11:12
  • 3
    It's very seldom that buttonOFF is checked. It needs to be pressed when LED5 goes LOW Jul 20, 2021 at 11:17
  • 1
    is this not the same code as in ArduinoFan's answer?
    – Juraj
    Jul 21, 2021 at 8:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.