New to Arduino, Need Help With Turning LED's on after set amount of time

Hi there, I'm trying to figure out how to turn on LED's after an elapsed period of time (anywhere from 5 to 30 seconds) once a button is pressed. I'm admittedly a little lost and was wondering if anyone might have suggestions on how to implement the timer function in my code. I've only been using the software for a week and I'm new to coding in general, so I'd appreciate any advice y'all could offer!

Here's the code I have at the moment for turning the LED on and off with a button.

int button = 10;
int led = 9;
int status = false;

void setup(){
pinMode(led, OUTPUT);
pinMode(button, INPUT_PULLUP); 
}

void loop(){

if (digitalRead(button) == true) {
status = !status;
digitalWrite(led, status);
} while(digitalRead(button) == true);
delay(50);
}

Hello, do yourself a favour and please read How to get the best out of this forum and modify your post accordingly (including code tags and necessary documentation of your ask).

--
even if that kinda works, digitalRead returns HIGH or LOW not true or false...
buttons are usually bouncing

Sorry about that, thanks for pointing it out!

tanks for adding code tags

When you are in INPUT_PULLUP, a press is read as LOW

the button should be wired: pin <---> button <---> GND
the led should have a suitable current limiting resistor

1 Like

consider

#if 0
int button = A1;
int led    = LED_BUILTIN;
#else
int button = 10;
int led    = 9;
#endif

enum { Off = LOW, On = HIGH };

void setup (){
    pinMode (led, OUTPUT);
    pinMode (button, INPUT_PULLUP);
}

void loop (){
    if (LOW == digitalRead (button)) {
        digitalWrite (led, On);
        delay (5000);               // 5 seconds
        digitalWrite (led, Off);
    }
}
1 Like

Whenever I click on a post to read I'm always waiting for that 'consider...' plus the solution :smiley:

I interpreted the Q slightly differently in that "how to turn on LED's after an elapsed period of time. So I went with this:

#define button 7
#define led LED_BUILTIN
#define delayTime 5000 //5 Seconds

void setup (){
    pinMode (led, OUTPUT);
    pinMode (button, INPUT_PULLUP);
}

void loop (){
    static unsigned timeCapture;
    // If the button is pressed we capture the time
    if (!digitalRead(button))
      timeCapture = millis();
      
     // If the delay time has elapsed since we captured it, we toggle the LED.
    if (millis() - timeCapture >= delayTime) 
      digitalWrite(led, !digitalRead(led));
}

Which waits for the elapsed period of time before acting on the LED.

That period to begin elapsing only after the button is released. And reset if the button is pressed again during the waiting period.

@vzm this code


if (digitalRead(button) == true) {
status = !status;
digitalWrite(led, status);
} while(digitalRead(button) == true);
delay(50);

is probably not doing what you think.


    if (digitalRead(button) == true) {
        status = !status;
        digitalWrite(led, status);
     } 

     while(digitalRead(button) == true);


     delay(50);

The semicolon after the while statement ruins the poor man's denouncing logic you were going for. And a do-while loop would work better here as well.

a7

1 Like

I'd say that line of code was there to wait until the button was released. what was missing was a delay after the first press detection (which should be LOW instead of true) to absorb the initial press bouncing. The last delay was to absorb the release bouncing.
something like this

if (digitalRead(button) == LOW) { // press detected
  status = !status;
  digitalWrite(led, status);
  delay(50);  // anti bounce
  while(digitalRead(button) == LOW); // wait for release
  delay(50);  // anti bounce
} 
1 Like

It does yes, it depends on how you want it to function, and mostly on the context of what is happening around it in the rest of the code.

1 Like

Yes. I usually write the nearly identical process this way

    if (digitalRead(button) == LOW) {
        status = !status;
        digitalWrite(led, status);

        do {
            delay(50);
        } while(digitalRead(button) == LOW);

       delay(50); 
    } 

If I'm being lazy or am impatient about getting on with something other than a real button handler.

I've seen noobs thinking there is such a thing as an if/while statement.

As for the button in @anon46966594's code resetting the period, that was merely an observation not criticism. The whole thing the OP wants to do is a mystery, to me anyway.

a7

2 Likes

I’ll try some of these out after a nap! Thanks for all the suggestions everyone.

Basically I’m making a sequence of 6 LEDS that light up one at a time over the course of 3 minutes (each lighting up at intervals of 30 seconds pass) once I press a button, and turn off once it’s pressed again. So at 30 seconds the first LED turns on, at a minute the second, and so forth. They’d all stay lit once 3 minutes have passed, but they could be turned off once pressing the button again. I’m aware that timers are a thing, but I haven’t found any videos yet that are pointing me in the right direction. Likewise, I’m not sure if I can achieve this effect with delays. I figured I’d start with just getting one to work and go from there. Sorry if my description is a bit confusing.

Getting clearer. :expressionless:

However you get it to go, I recommend that something happen immediately you press the button, like the first LED to turn on would turn on at the button press, then the rest would come on sequentially. Or the Nth LED when all on would go out immediately.

Also missing I think is what you would want to happen if the button is pressed again in the middle of either the turning on or the turning off sequence… stop? Start over? Start going the other direction? Skip to the next LED in the sequence?

My point being that the more you can figure out before you (or anyone) goes charging off to write the code, the better.

Any question you don't answer for yourself ahead of time will have an an answer provided by how the code works and may not be what you want.

a7

That's an excellent point, I never put much consideration into the circumstance of pressing the button in the middle of the sequence. I guess for simplicity's sake I'd just turn it off. I'm not trying to do anything terribly fancy other than get the LEDs to turn on one at a time and stay lit as aforementioned. Maybe once I get more experience under my belt with Arduino I can try that out though, those suggestions would be really fun to use for some projects.

Thanks for the continued feedback though, Alto.

So it sounds kind of like a countdown timer? Like maybe a cooking timer for boiling an egg or something.

Personally, I think I might do it sometime like this:

-On release of the button light up all leds
-Every thirty seconds turn off the next LED
-At the end of the sequence just sit waiting for the next button press
-If the button is pressed mid-sequence then restart the countdown/and relight all the LEDs

Nah, it's technically counting up. I laser cut and assembled a monolith with the Arecibo message engraved. It was a radio transmission of about three minutes that could be translated into a diagram similar to the Voyager-1 plaque that briefly describes humanity, our place in the solar system, etc. Basically, I'm using the LED's to represent the length of time of the transmission itself, and gradually as it lights up, it reveals more of the diagram. So after the three minutes have passed, you can see the entire diagram as it's lit from behind. Kind of like how you can't actually translate the visual diagram without the full radio transmission. Hope that makes sense.

Ah nice! Some sort of individually addressable LEDs would be cool so you could fade in the brightness/colour/saturations or do something like slowly flashing red until it's "received" and then set to green.

Anyway try this, I'm not claiming it's the best solution but it does seems to behave as you have described. The sequence starts when the button is released, everything stays on when it gets to the final LED and pressing the button halfway through restarts everything.

Let me know what you think!

https://wokwi.com/arduino/projects/313141271967302209

#define NUM_LEDS 6

#define LED1_PIN 21
#define LED2_PIN 20
#define LED3_PIN 19
#define LED4_PIN 18
#define LED5_PIN 17
#define LED6_PIN 16

#define BTN_PIN 7

int LEDS[NUM_LEDS] = { LED1_PIN, LED2_PIN, LED3_PIN, LED4_PIN, LED5_PIN, LED6_PIN };
unsigned long timerInterval = 1000; // 1 Second
unsigned long nextActionAt = 0;
int nextLED = 0;

void restartSequence() {
  for (int i = 0; i < NUM_LEDS; i++) 
    digitalWrite(LEDS[i], LOW);
  nextLED = 0;
  nextActionAt = millis() + timerInterval;
}


void setup() {
  Serial.begin(9600);
  pinMode(BTN_PIN, INPUT_PULLUP);

  for (int i = 0; i < NUM_LEDS; i++)
    pinMode(LEDS[i], OUTPUT);
}



void loop () {
    if (!digitalRead(BTN_PIN)) 
      restartSequence();

    if (nextActionAt && millis() >= nextActionAt) { 
        digitalWrite(LEDS[nextLED++], HIGH);
        nextActionAt += timerInterval;

        if (nextLED > NUM_LEDS)
          nextLED = nextActionAt = 0;
    }
} 
2 Likes

@anon46966594 Nice! That code works well. Thanks for taking the time to wokwi it.

(How did you get the wires to be invisible? Imma show this to the wokwi boys.)

Anyway, I notice that the sequence does not start right away. I mean the first LED comes on only after the delay period.

Once the timer value is set to something longer than 1000 ms , this becomes annoying. To me. And the tendency for many ppl would be to keep pressing the button, which would simply keep resetting the mechanism. Irony defined.

A simple change in the restartSequence function

  nextActionAt = 1;    // right away!

will set the first interval to 1 ms, nearly immediately launching the sequence and light up the first LED.

I added "real" debouncing because a) why not? and b) I have no doubt that with additions to the functionality, it will become worth having done.

I left the button code right in the loop function. But it uses a flag, which gets set when the button is pressed. The rest of the code can notice the flag, handle its meaning and reset said flag.

Lastly I must point out this from #16

  if (nextActionAt && millis() >= nextActionAt) { 

which is very clever - by setting nextActionAt to zero, this line shows a means for turning off the machinery. A special value of 0 means don't run the timed event thing.

My changes introduced (or uncovered) a few unanticipated and undesirable behaviours, so I changed the logic for scheduling the next action just a wee bit.

Here, invisible wires and all (?) is mostly the logic of #16 with a few tweaks as outlined above.

#define NUM_LEDS 6

#define LED1_PIN 21
#define LED2_PIN 20
#define LED3_PIN 19
#define LED4_PIN 18
#define LED5_PIN 17
#define LED6_PIN 16

#define BTN_PIN 7

int LEDS[NUM_LEDS] = { LED1_PIN, LED2_PIN, LED3_PIN, LED4_PIN, LED5_PIN, LED6_PIN };
unsigned long timerInterval = 5000; // 1 Second
unsigned long nextActionAt = 0;
int nextLED = 0;

void restartSequence() {
  for (int i = 0; i < NUM_LEDS; i++) 
    digitalWrite(LEDS[i], LOW);
  nextLED = 0;
  nextActionAt = 1;    // right away!
}


void setup() {
  Serial.begin(9600); Serial.println("Hello There!\n");
  pinMode(BTN_PIN, INPUT_PULLUP);

  for (int i = 0; i < NUM_LEDS; i++)
    pinMode(LEDS[i], OUTPUT);
}


void loop () {
  static unsigned long lastButtonTime = 0;
  static unsigned long lastButtonState = 1;   // input normally pulled up

  unsigned long now = millis();
  bool flag = false;

  if (now - lastButtonTime > 25) {
    unsigned char theButton = digitalRead(BTN_PIN);
    if (theButton != lastButtonState) {
      lastButtonState = theButton;
      lastButtonTime = now;
      if (!theButton)
        flag = true;
    }
  }

  if (flag) {
    Serial.println("Restarting...");
    restartSequence();
    flag = false;
  }

  if (nextActionAt && millis() >= nextActionAt) { 
      digitalWrite(LEDS[nextLED++], HIGH);
      nextActionAt = now + timerInterval;

      if (nextLED > NUM_LEDS)
        nextActionAt = 0; // never
  }
} 

Of course I did:

a7

consider what happens if the above causes nextActionAt to wrap and become a smaller value near zero while millis() still hasn't wrapped. the condition is almost guaranteed to be trued on the next iteration. (yes this isn't likely to happen for 49 days)

so it's better to do

    if (nextActionAt && (millis() - nextActionAt) > timerInterval) {

and what happens if "nextAction += timerInterval" or "nextAction = millis()" results in nextAction being zero. need to check if the computation is zero and probably add 1

To make the wires invisible just go into the diagram.json and change any colour to "" so "green" to ""

All very valid improvements! Happy to see them. To be honest I never really considered the code running continuously for a month+ but I guess such scenarios need to be taken into account.

Also in regards to nothing happening during the first time interval, I thought that was the intention from the OP as he was simulating a wait for a 'transmission' to finish. (at least that was my understanding)

Actually.... nextActionAt is almost always bigger than millis as it's the time ahead. millis()-nextActionAt would go negative (wrap back the other way) right? So I think it needs to be:

    if (nextActionAt && (nextActionAt - millis()) >= timerInterval) {