more of this millis() business

Folks,

The objective is for the user to push a button and have an LED come on and blink for 30 seconds. After the 30 seconds are up, no more blinking until the button is pushed again. When I upload the code and push the button the LED starts blinking but it will not stop. I'm still quite new at coding.

const int alarmOnButton = 2;

const int buzzPin = 5;
const int buzzInterval = 500;

unsigned long previousBuzzMillis = 0;
unsigned long currentBuzzMillis;

unsigned long alarmDuration = 30000;
unsigned long previousDurationMillis;
unsigned long currentDurationMillis;

int buzzState = HIGH;
int buttonState;

void setup()
{
  pinMode(alarmOnButton, INPUT_PULLUP);
  pinMode(buzzPin, OUTPUT);

} 


void loop()
{

    currentDurationMillis = millis;
    
    buttonState = digitalRead(alarmOnButton);
    
    if (buttonState == LOW)
    {
        previousDurationMillis = currentDurationMillis;
        
        while (currentDurationMillis - previousDurationMillis <= alarmDuration)
        {
             currentBuzzMillis = millis();
             if (currentBuzzMillis - previousBuzzMillis >= buzzInterval)
             {
                      previousBuzzMillis = currentBuzzMillis;
                      if (buzzState == LOW)
                      {
                          buzzState = HIGH;
                      }
                          else
                          {
                            buzzState = LOW;
                          }
              
                    digitalWrite(buzzPin, buzzState);
             }

        }             
    }
}

Thank you.

Best,

Tony

/Users/john/Documents/Arduino/sketch_dec26a/sketch_dec26a.ino: In function 'void loop()':
/Users/john/Documents/Arduino/sketch_dec26a/sketch_dec26a.ino:28:27: warning: invalid conversion from 'long unsigned int (*)()' to 'long unsigned int' [-fpermissive]
   currentDurationMillis = millis;
                           ^~~~~~

You meant to write:
  currentDurationMillis = millis();

previousDurationMillis = currentDurationMillis;


    while (currentDurationMillis - previousDurationMillis <= alarmDuration)
    {

Using a 'while' like that won't work because 'previousDurationMillis' and 'currentDurationMillis' don't change. It might work is you use:

previousDurationMillis = millis();

    while (millis() - previousDurationMillis <= alarmDuration)
    {

Similar problem with the other 'while' loop.

Thank you. I corrected my mistake. When I push the button, the LED blinks but will not stop at 30 seconds.

Revamped:

const byte alarmOnButton          = 2;
const byte buzzPin                = 5;

byte lastState;

boolean alarmFlag                 = false;
boolean buzzerFlag                = false;

const unsigned long alarmDuration = 30000;
const unsigned long buzzInterval  = 500;

unsigned long buzzerMillis;
unsigned long alarmMillis;
unsigned long switchMillis;

//************************************************************************
void setup()
{
  pinMode(alarmOnButton, INPUT_PULLUP);
  lastState = digitalRead(alarmOnButton);

  pinMode(buzzPin, OUTPUT);
  digitalWrite(buzzPin, LOW);

} //END of setup()

//************************************************************************
void loop()
{
  //*************************************
  //time to check the switches ?
  if (millis() - switchMillis >= 50)
  {
    //restart the Timer
    switchMillis = millis();

    checkSwitches();
  }

  //*************************************
  //if enabled, time to toggle the buzzer ?
  if (buzzerFlag == true && millis() - buzzerMillis >= buzzInterval)
  {
    //restart the Timer
    buzzerMillis = millis();

    digitalWrite(buzzPin, !digitalRead(buzzPin));

  }

  //*************************************
  //if enabled, time to stop the alarm ?
  if (alarmFlag == true && millis() - alarmMillis >= alarmDuration)
  {
    //disable the buzzer
    alarmFlag = false;

    //disable the alarm
    buzzerFlag = false;
    
    digitalWrite(buzzPin, LOW);

  }
  
} //END of loop()

//************************************************************************
void checkSwitches()
{
  byte currentState = digitalRead(alarmOnButton);

  //has the switch change state ?
  if (lastState != currentState)
  {
    //update the status
    lastState = currentState;

    //was the switch closed ?
    if (alarmFlag == false && currentState == LOW)
    {
      //enable buzzer
      buzzerFlag = true;

      //reset the Timer
      buzzerMillis = millis();

      //enable alarm
      alarmFlag = true;

      //reset the Timer
      alarmMillis = millis();
    }
  }

} //END of checkSwitches()

BTW, stay away from ‘while()’ until you reach posting number 300 ;).

If you are ok with using libraries.. (Some people seem to feel like they are cheating when using libraries.. )

A different approach.

#include <mechButton.h>
#include <blinker.h>

#define BUTTON_PIN   2        // Pin we'll hook the button to. The other side hooks to ground.
#define LED_PIN      13       // Usual pin number for built in LED.
#define BLINK_MS     30000    // 30 second blink time.

mechButton  button(BUTTON_PIN);     // Create a debouncing button.
blinker     aBlinker;               // Default is for pin 13. Go with that.
timeObj     blinkTimer(BLINK_MS);   // Setup a timer for 30 seconds.
bool        blinking;               // A bool to save our blinking state.

// Your standard sketch setup()
void setup() {

   button.setCallback(myCallback);		// Set up our callback. (Also calls hookup() for idling.)
   aBlinker.setOnOff(false);           // Make sure we're not already doing the blink thing.
   blinking = false;                   // Note that we're not blinking.
}


// This is the guy that's called when the button changes state.
void myCallback(void) {

  
   if (!button.trueFalse()) {          // If the button was mashed to ground.. (Pressed)
      if (blinking) {                  // If we ARE already blinking..
         aBlinker.setOnOff(false);     // Stop this silly blinking.
         blinking = false;             // Save our state. 
      } else {                         // Else, we are NOT blinking..
         blinkTimer.start();           // Fire up the timer.
         aBlinker.setOnOff(true);      // Start blinking.
         blinking = true;              // Save our state.
      }
   }
}


// Your standard sketch loop()
void loop() {
      
   idle();											// Let all the idlers have time to do their thing.
   if (blinking && blinkTimer.ding()) {   // If we are blinking and our time has expired..
      aBlinker.setOnOff(false);           // Stop blinking.
      blinking = false;                   // Save our state. 
   }
}

If you'd like to try this, install LC_baseTools from the IDE's library manager.

-jim lee

Again another library approach, my millisDelay class available in the SafeString library (available from the library manager)
see the tutorial How to write Timers and Delays in Arduino for the details

One of the advantages of the millisDelay class is that you can test if your delay timer is running and stop it early if you need to.

it is good to do both:
using libraries and doing things "by hand". libraries are good for things with a lot of details like the bitbanging
to send data to a display ore onewrire-bus etc.

Though a library can not offer all logic variations all the different applications of users need.
That's where "knowing how to code" comes into play.
the functionality you describe can be called a "oneshot-timer"

This goes beyond standard blinking.
more functionality means needing more code
blink only after button-press and stop after 30 seconds can be done with a boolean flag-variable

you can imagine boolean variables as internal switches ON/OFF but the states where true / false

press a button enables that something starts to happend for a period of time. When period has elapsed then stop.

you could define a function "blink_LED" which does the blinking

here is a "sketchy" sketch that shows the principle.
This code needs additional the function "blink_LED()" and closing curly brackets etc. to compile

unsigned long currentTime;
unsigned long previousTime;
unsigned long Blinking_Time = 30000;

boolean runBlinking; 

void setup() {

  runBlinking = false;
  ....
}

void loop() {

  currentTime = millis();
  
  if digitalRead(myButton == HIGH) {  // if button is pressed
    runBlinking = true;               // set flag to true
    previousTime = currentTime;       // make snapshot of time when button is pressed
  }

  if (runBlinking) { // only if flag is true 
    blink_LED(); 
    
    if (currentTime - previousTime >= Blinking_Time) {
      runBlinking = false;
    }
  }    
  ....

best regards Stefan

I took a look inside the milliDelay-tutorial of user drmpf

this is a very practical approach. Supported by a library with short demo-codes that are quite easy to understand.
This tutorial will reach all people that don't want to read pages long explanations that go deepest into programming-techniques. So IMHO just a different approach from the "standard" millis()-tutorial.

It is very clear: the short explanantions in this tutorial means a smaller understanding that the reader develops.
Anyway as all this is for free and done in free-time everybody is free to choose.

keep your code responsive with non-blocking-timing using the millisDelay-library

best regards Stefan

Thanks, all. I've got some information to digest.

@johnwasser Thank you.

@larryd Thanks Larryd. Everything is running perfectly. One day I will see the solutions easier. And I promise to avoid the temptation to use while() for a while - post 300!

@jimlee Jim, thanks again. You've helped me out in the past. In the near future, I'm going to try your approach too.

@drmpf Thank you for taking the time to post.

@SteFanL38 Thank you. Everyone has given me a lot to review. Thank you for your time.

@jimlee - I do use libraries. Forgot to mention that.