Help Beginner with LED flash for specified time

I've read over the forum looking for an answer to my question but because I'm a beginner I suspect I won't even know when I run across it.

I have a nano, I'm using Arduino 2.3.4 on Mac and I've managed to find a sketch that blinks an LED when a button is pressed, then shuts off the LED when it's pressed again and so forth, which is what I want (thanks to UKHeliBob). All I had to do was change line 36 to LOW so the LED was off until the button was pressed.

Ultimately, what I'd like to do is have the LED flash when the button is pressed, shut off when the button is pressed again, and if the button isn't pressed the 2nd time, the LED will just flash for a set amount of time, say 2 hours then return to the off state.

If it helps, what I'm making is a flashing LED for my mailbox that's down the road (rural area) so I know when the mailman has been by. I have it set up now using components like a 555 timer, etc. It's triggered by opening the mailbox door which touches a switch. The led flashes letting me know he's been by and when I open the door again, it stops the flashing. If I'm gone, it simply flashes for 2 hours after he triggers it then shuts off. It works great but I think it'd make a neat project for me to learn Arduino.

I hope this posts correctly:

const byte buttonPin = 2;
const byte ledPin = 5;
boolean flashing = false;
byte currentButtonState = HIGH;
byte previousButtonState = HIGH;
unsigned long currentTime;
unsigned long onoffStarted;
long flashPeriod = 1000;

void setup()
{
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  currentButtonState = digitalRead(buttonPin);
  if (currentButtonState != previousButtonState && currentButtonState == LOW)  //button has become pressed
  {
    flashing = !flashing;
  }
  previousButtonState = currentButtonState;          //save the button state for the next check

  if (flashing)
  {
    currentTime = millis();
    if (currentTime - onoffStarted > flashPeriod)  //if the flash period ended ?
    {
      digitalWrite(ledPin, !digitalRead(ledPin));  //flip the state of the LED
      onoffStarted = currentTime;                  //save the time when it occured
    }
  }
  else
  {
    digitalWrite(ledPin, LOW);  //force the LED off if not flashing
  }
}

Bashed out in 5 minutes on New Year's Eve: no warranty is expressed or implied. Enjoy. :slight_smile: Could be much simpler with a button library that handled the debouncing, but as I said, bashed out in 5 minutes.

const byte buttonPin = 2;
const byte ledPin = 5;
enum { 
   NOT_FLASHING,
   FLASHING
} state = NOT_FLASHING;
unsigned long onoffStarted;
unsigned long lastChangeTime;
unsigned long flashPeriod = 1000UL;
unsigned long flashTimeout = 2UL * 60UL * 60UL * 1000UL;

void setup() {
   pinMode(buttonPin, INPUT_PULLUP);
   pinMode(ledPin, OUTPUT);
}

void loop() {
   
   switch( state ) {
      
      case NOT_FLASHING:
         if( buttonWasPressed() ) {
            onoffStarted = lastChangeTime = millis();
            digitalWrite(ledPin, HIGH);
            state = FLASHING;
         }
         break;
         
      case FLASHING:
         if( buttonWasPressed() || millis() - onoffStarted > flashTimeout ) {
            digitalWrite(ledPin, LOW);
            state = NOT_FLASHING;
         } else if( millis() - lastChangeTime >= flashPeriod ) {
            lastChangeTime = millis();
            digitalWrite(ledPin, !digitalRead(ledPin));
         }
         break;
   }
}

bool buttonWasPressed() {
   bool currentButtonState;
   static bool lastButtonState = HIGH;
   static bool buttonState = HIGH;
   static unsigned long buttonDebouncing = 0;
   const unsigned buttonDebounceDelay = 50;   // 50ms debounce
   bool result = false;
      
   currentButtonState = digitalRead(buttonPin);

   if( currentButtonState != lastButtonState ) {
      buttonDebouncing = millis();
   }

   if( buttonDebouncing && millis() - buttonDebouncing > buttonDebounceDelay ) {
      if( currentButtonState != buttonState ) {
         buttonState = currentButtonState;
         // return true when button is pressed and debounced
         result = buttonState == LOW;
       }
       buttonDebouncing = 0;
   }
   lastButtonState = currentButtonState;
   
   return result;
}

Thanks so much! I'll give it a whirl & report back shortly! Much appreciated!

So far so good! It flashes with the button states. I'm not sure about the timer because I haven't let it run for 2 hours but thanks to you I can "dissect" the sketch you provided to learn what the functions are which will kickstart my journey into Arduino.

Baby step 1: I'm going to go through and see if I can adjust the time so I can check that it shuts off. :slight_smile:

Thanks again you've been most helpful!

I know your project is good in intent, but recall 2002 and Luke "Smiley Face" Helder...

https://ceicher.com/face_recognition/

Hi @rushl ,

your sketch was on the right way, just a few things had to be changed:

/*
  Forum: https://forum.arduino.cc/t/help-beginner-with-led-flash-for-specified-time/1337748
  Wokwi: https://wokwi.com/projects/418890592487643137
*/

const byte buttonPin = 2;
const byte ledPin = 5;
boolean flashing = false;
byte currentButtonState = HIGH;
byte previousButtonState = HIGH;
unsigned long currentTime;
unsigned long onoffStarted;
long flashPeriod = 1000;

void setup()
{
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  currentButtonState = digitalRead(buttonPin);
  if (currentButtonState != previousButtonState)  //buttonstate has changed
  {
    delay(30);                                         // Poor man's debouncing ... ;-)
    previousButtonState = currentButtonState;          //save the button state for the next check
    if (currentButtonState == LOW) {                   // Change the flash state only if the change was from HIGH to LOW
      flashing = !flashing;
      if (!flashing) {                                 // We only need to do this once when going to "not flashing"
        digitalWrite(ledPin, LOW);                     //force the LED off if not flashing
      }
    }
  }

  if (flashing)
  {
    currentTime = millis();
    if (currentTime - onoffStarted > flashPeriod)  //if the flash period ended ?
    {
      digitalWrite(ledPin, !digitalRead(ledPin));  //flip the state of the LED
      onoffStarted = currentTime;                  //save the time when it occured
    }
  }
}

You can check it out on Wokwi: https://wokwi.com/projects/418890592487643137

Some comments:

  • I use a very simple way of "debouncing" with a delay(30) everytime the button state changes. Think that's ok for a small sketch with no further time critical functions.
  • The led can be switched off once when flashing goes to not flashing, no need to do this again and again in loop().

Good luck!
ec2021

@xfpd Yikes! I had no idea. I'm definitely not bombing my mailman. I actually like the guy! Lol! I did tell him about my mailbox project and since there is no light pollution where we live and we often get our mail after dark, I even put a little SMD "dome light" in the box for him. It also comes on when he opens the door.

@ec2021 Thanks for the information/sketch! I will give that a try, too, and check out your link.

Looking through the sketch that Van_Der_Decken posted, I see the 2UL 60UL 60UL 1000UL line. I read that that is the timer so those are the adjustments I need to make to change the runtime. I just started reading about it so I'm not sure if I need to change all the numbers or just the 2UL. I suspect just the 2UL because I feel like maybe all the other numbers are needed for the program to understand the 2UL.

Edit to add: I went to the Wokwi link. That's really cool! You can see it in action! With your sketch, @ec2021 , I had to change line 8 to "true" so it'd flash. Is that correct? Also, how would I change the timer duration? Again, I barely have any idea what I'm looking at so I certainly could have missed the numbers for it

1 Like

Welcome to the forum!

Yeth. Why use 20th century technology? :expressionless:

I looked at @ec2021's project to see how the turning off after two hours was handled and found it missing.

So I added it. I also moved a few things, and put in timed delay-less logic for the button handling. I have no (zero) problem with "poor man's" debouncing but thought you might like to see how to avoid even a usually harmless delay() call.

Wokwi won't let me save the project copy I worked on, dunno why sometimes I can but not always. Here's the code which can be substituted for the original in the simulation.

/*
  Forum: https://forum.arduino.cc/t/help-beginner-with-led-flash-for-specified-time/1337748
  Wokwi: https://wokwi.com/projects/418890592487643137
*/

const byte buttonPin = 2;
const byte ledPin = 5;
boolean flashing = false;
byte currentButtonState = HIGH;
byte previousButtonState = HIGH;
unsigned long currentTime;
unsigned long onoffStarted;

unsigned long flashPeriod = 777;        // half-blink period
unsigned long howLongToFlash = 13000;   // shut off after a time (13 seconds here for testing)

void setup()
{
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  static unsigned long lastTurnedOn;
  static unsigned long lastButtonTime;    // for debouncing

  if (flashing)
  {
    currentTime = millis();
    if (currentTime - onoffStarted > flashPeriod)  // if the flash period ended ?
    {
      digitalWrite(ledPin, digitalRead(ledPin) == LOW ? HIGH : LOW);  // flip the state of the LED
      onoffStarted = currentTime;                  // save the time when it occured
    }
  }

  if (flashing && millis() - lastTurnedOn > howLongToFlash) {
    flashing = false;
    digitalWrite(ledPin, LOW);
  }

  if (millis() - lastButtonTime < 30) return;          // we done. too soon to look at the button again.

  currentButtonState = digitalRead(buttonPin);
  if (currentButtonState != previousButtonState)       // buttonstate has changed
  {
    previousButtonState = currentButtonState;          // save the button state for the next check
    lastButtonTime = millis();                         // and don't look until the debounce period is elapsed...

    if (currentButtonState == LOW) {                   // Change the flash state only if the change was from HIGH to LOW
      flashing = !flashing;
      if (!flashing) {                                 // We only need to do this once when going to "not flashing"
        digitalWrite(ledPin, LOW);                     // force the LED off if not flashing
      }
      else lastTurnedOn = millis();
    }
  }
}

Tested, works good. For a challenge, modify the part that times out after a long period so it will not chop short the on period of theLED...

a7

Hi @alto777 ,

thanks, I missed to add this function ...

Here is your version on Wokwi:

https://wokwi.com/projects/418893876002893825

Wish you a happy new year!
ec2021

1 Like

Thanks @ec2021, HNY back at you.

The wokwi did have an issue (which seems to be back in some form) around the problem I saw. Oddly even copying and pasting files into a new anonymous project carries something along, I dunno it doesn't work the way it once did.

I don't even remember how to log in, so on the machines I have available just now I'm just another "Anonymous Maker" or whatever.

a7

You are welcome!

I took the chance to add the missing timed reset to the sketch in a new Wokwi project:

https://wokwi.com/projects/418894200161797121

Sketch
/*
  Forum: https://forum.arduino.cc/t/help-beginner-with-led-flash-for-specified-time/1337748
  Wokwi: https://wokwi.com/projects/418894200161797121

  Now with a timed reset function ;-)

  ec2021

*/

#define TEST

const byte buttonPin = 2;
const byte ledPin = 5;

#ifdef TEST
const unsigned long autoResetAfter  {10000UL};     // 10000 ms = 10 sec
#else
const unsigned long autoResetAfter  {120 * 60000UL}; // 120 x 60000 ms = 2h (with 60 x 1000 ms = 1 min)
#endif

boolean flashing = false;
byte currentButtonState = HIGH;
byte previousButtonState = HIGH;
unsigned long currentTime;
unsigned long onoffStarted;
unsigned long flashPeriod = 1000;
unsigned long lastFlashStartTime = 0;

void setup()
{
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  currentButtonState = digitalRead(buttonPin);
  if (currentButtonState != previousButtonState)  //buttonstate has changed
  {
    delay(30);                                         // Poor man's debouncing ... ;-)
    previousButtonState = currentButtonState;          //save the button state for the next check
    if (currentButtonState == LOW) {                   // Change the flash state only if the change was from HIGH to LOW
      flashing = !flashing;
      if (!flashing) {                                 // We only need to do this once when going to "not flashing"
        digitalWrite(ledPin, LOW);                     //force the LED off if not flashing
      } else {
        lastFlashStartTime = millis();
      }
    }
  }

  if (flashing)
  {
    currentTime = millis();
    if (currentTime - onoffStarted > flashPeriod)  //if the flash period ended ?
    {
      digitalWrite(ledPin, !digitalRead(ledPin));  //flip the state of the LED
      onoffStarted = currentTime;                  //save the time when it occured
    }
    if (millis() - lastFlashStartTime >= autoResetAfter) {
      flashing = false;
      digitalWrite(ledPin, LOW);                     //force the LED off if not flashing
    }
  }
}

@rushl : Just comment the line "#define TEST" out or remove it so that the line

const unsigned long autoResetAfter  {120 * 60000UL}; // 120 x 60000 ms = 2h (with 60 x 1000 ms = 1 min)

is used by the compiler. (The UL behind 60000 tells the compiler to use the data type unsigned long for the calculation)

Have fun!

Wow! Thanks again, guys! The Wokwi thing makes this so interesting!
This, coming from someone who was dazzled when I made an led blink yesterday. Lol!

Honestly, though, it is very helpful to see if the changes made do something & I can't wait to tinker around with it! I really appreciate all of you taking the time to help me along.

That's what this forum is good for :wink:

For a very first post here you made a good job!

Stay interested and curious and have fun with Arduino!
ec2021

1 Like

Click a link on wokwi and wokwi sends an email with a sign-on link.

You do not need to be signed-on to name and save a file with a sharable link. I use one computer not signed-on (to wokwi) and one signed-on. Develop on the not-signed-on and save the good stuff on the signed-on.

2 Likes

One more question. I'd like to add an additional LED that doesn't flash (the domelight in the mailbox). I'd like it to function the same as the flashing LED, minus the flashing. That is to say, I'd like it to come on when the button is pressed, off when pressed again and if no second press, on for the timer duration.

Is this possible or is it considered too many "operations" at once?

This is the sketch I'm using:

const byte buttonPin = 2;
const byte ledPin = 5;
boolean flashing = false;
byte currentButtonState = HIGH;
byte previousButtonState = HIGH;
unsigned long currentTime;
unsigned long onoffStarted;

unsigned long flashPeriod = 777;        // half-blink period
unsigned long howLongToFlash = 7200000;   // shut off after a time (2 hours hopefully)

void setup()
{
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  static unsigned long lastTurnedOn;
  static unsigned long lastButtonTime;    // for debouncing

  if (flashing)
  {
    currentTime = millis();
    if (currentTime - onoffStarted > flashPeriod)  // if the flash period ended ?
    {
      digitalWrite(ledPin, digitalRead(ledPin) == LOW ? HIGH : LOW);  // flip the state of the LED
      onoffStarted = currentTime;                  // save the time when it occured
    }
  }

  if (flashing && millis() - lastTurnedOn > howLongToFlash) {
    flashing = false;
    digitalWrite(ledPin, LOW);
  }

  if (millis() - lastButtonTime < 30) return;          // we done. too soon to look at the button again.

  currentButtonState = digitalRead(buttonPin);
  if (currentButtonState != previousButtonState)       // buttonstate has changed
  {
    previousButtonState = currentButtonState;          // save the button state for the next check
    lastButtonTime = millis();                         // and don't look until the debounce period is elapsed...

    if (currentButtonState == LOW) {                   // Change the flash state only if the change was from HIGH to LOW
      flashing = !flashing;
      if (!flashing) {                                 // We only need to do this once when going to "not flashing"
        digitalWrite(ledPin, LOW);                     // force the LED off if not flashing
      }
      else lastTurnedOn = millis();
    }
  }
}

I think you could do this youself.

You'll need another LED and series current limiting resistor (we tend to omit them in the simulator as the LEDs there cannot be burned out).

Placed on another pin that is an OUTPUT.

I hope that much is obvious.

Now… read the code and see every place flashing is turned on or off, and just turn on or off the extra LED at the same time.

Leave the LED that should just be on out of the logic that makes the flashing LED flash.

Alternately, you could just make the new LED follow the variabke which is flashing… probably simpler and clearer, as it would just be another if statement like the three or four that are in the loop at this time.

In case you haven't noticed, you can read code just like reading anything, and reading code you didn't write and figuring out how it works is part of learning.

I have often said no good writer of any kind of writing is not also widely read.

a7

Great! Now that I know it's possible I'll do some more reading & try to add it in.

I'll report back with what I come up with.
Thanks again!

Yes. I note that you placed it inside the exist test for flashing, and it gave me the idea to inhibit terminating the flashing during a flash on period:

  if (flashing)
  {
    currentTime = millis();
    if (currentTime - onoffStarted > flashPeriod)  //if the flash period ended ?
    {
      digitalWrite(ledPin, !digitalRead(ledPin));  //flip the state of the LED
      onoffStarted = currentTime;                  //save the time when it occured
    }
    if (millis() - lastFlashStartTime >= autoResetAfter && digitalRead(ledPin) == LOW ) {
      flashing = false;
    }
  }

There are still a few surprises, a perfectionist or someone coding for NASA would ferret out all the things that just happen the way they do and fix them to do something intentional.

Try making the flash period 4000 milliseconds, e.g. And it still chops off a flash if you stop with the pushbutton, but I can call this a Good Thing as it provides immedaite feedback you've pressed the button.

Ppl make careers out of this kind of stuff. I'm sure we all have a few devices around the house that have quirky behaviour - too little attention to detail during development.

a7

I always put the resistor in series in Wokwi projects to avoid that beginners forget about it :wink:

@rushl :

There are actually a number of issues that are not exactly implemented in the online simulator.

  • Even if you don't connect GND and VCC of certain components to the board the sketch will still work.
  • Button "bouncing" is simulated but can be switched off (clicking on a button symbol while the Wokwi sketch is not running opens a menu where you can switch "Bounce" on/off
  • Creating a voltage divider by resistors does not give the expected output:

However, most of the simulation is really good to test the logic of sketches!

Just be aware that there is nothing more realistic than a real wired application :wink:

This is actually quite handy.

If I am lazy, I just use a bounce-free button and get on with whatever I am up to, lnowing that if I realize the project it will have to be dealt with.

Also handy are times when I am testing other ppl's code and they have either neglected to debounce the button(s) they are using, or I don't like the look of how they did handle it.

If I turn off the bouncing and the sketch starts to work, or works differently, I know I was right to be suspicious.

a7