Push Button Timer Help

Hi all. I'm stuck. I've got an arduino that I want to do several things. It momentarily turns on an LED on startup and starts another LED for 1.5 seconds when a button is pushed. I plan on adding an LCD display and a thermocouple. Right now I'm having issues with figuring out how to start a timer that displays 1 second intervals when the button is pressed and stops/resets when the button is pressed again.

Here is what I'm working with so far:

#define POWER_LED 9
#define START_LED 8
#define BUTTON_PIN 7

unsigned long previousTime = 0;
const long waitTime = 1500;
const long count = 1000;
int seconds = 0;

void setup(){
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(START_LED, OUTPUT);
  pinMode(POWER_LED, OUTPUT);
  digitalWrite(POWER_LED, HIGH);
  delay(500);
  digitalWrite(POWER_LED, LOW);
  Serial.begin(9600); 
}

void loop ()
{
    unsigned long msec = millis ();

    if (digitalRead (BUTTON_PIN) == LOW) {
        previousTime = msec;
        digitalWrite (START_LED, HIGH);
    }

    if (msec - previousTime >= waitTime) {
        digitalWrite (START_LED, LOW);
    }

}

This is what I've got so far for the timer...

if (msec - previousTime >= count) {
    Serial.print (msec / 1000);
  }

Any help would be great.

You need to hold the time that the last second occurred.

I suggest creating another variable for this.

unsigned long previousSecond = 0;

Then in loop...

if (msec - previousSecond >= count) 
{
    previousSecond = msec;
    Serial.print (msec / 1000);
}

Here is a demo-code that does:

  • convert a momentary push-button into a toggle-switch
    first push activated , second push de-activated, ....

  • has a skeleton of code that shows how to put a part of the code into a function that is only executed if push-button-toggle-switch is in mode activated

blinks an LED in a non-blocking way if push-button-toggle-switch is in mode activated

There are a lot of comments in this code that shall explain how it works.
Though it will take some time to understand all details

/* explanation of the most important details:

  realising a functionality where using a momentary push-button
  acts as a toogle-switch
  push       => activated  push again => DE-activated
  push again => activated  push again => DE-activated
  etc. etc. ...
  This needs quite some code. This code is well organised in MULTIPLE functions
  where each function is a senseful SUB-program

  This is the reason why function loop looks super-short:

  void loop () {
  activationMode = GetToggleSwitchState(); // must be executed all the time
  execute_if_Active(activationMode);       // function that does what its name says
  }

  Huh that's all? No. Of course there is more (below function loop)

  If you just want to APPLY the functionality to your own code
  replace the code inside function    void my_Action()
  with your own code that shall only be  executed if you activated execution
  with the momentary push-button

  I recommend to RENAME the function my_Action() to a SELF-explaining name
  This will cause a compiler-error which shows you where you have to rename
  the function-call of this function too (this is inside function execute_if_Active
  If you want to understand how it works read and analyse the rest of the functions.

*/


#define ProjectName "Toggle Button demonstration"

// define IO-states for inputs with pull-up-resistors
// pull-up-resistors invert the logig
#define unPressed HIGH
#define pressed   LOW

const byte ToggleButtonPin = A0;

bool activationMode = false;
unsigned long myCounter = 0;
unsigned long MyBlinkTimer = 0;


void setup() {
  Serial.begin(115200); // adjust baudrate in the serial monitor to match the number
  Serial.println( F("Setup-Start") );
  printFileNameDateTime();

  pinMode (LED_BUILTIN, OUTPUT);  // used for indicating logging active or not
  digitalWrite(LED_BUILTIN, LOW);
  // wire button between IO-pin and GND
  // Pull-up-resistor inverts the logic
  // unpressed: IO-pin detects HIGH
  // pressed:   IO-Pin detects LOW
  pinMode(ToggleButtonPin, INPUT_PULLUP);
  MyBlinkTimer = millis();
}



// this is the function that is called
// inside function execute_if_Active()
void my_Action() {

  if ( TimePeriodIsOver(MyBlinkTimer, 500) ) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN) ); // read state of LED-output and set to invertetd state
  }
}


void loop () {
  activationMode = GetToggleSwitchState(); // must be executed all the time
  execute_if_Active(activationMode);       // function that does what its name says
}


bool GetToggleSwitchState() {
  // "static" makes variables persistant over function calls
  static bool toggleState     = false;
  static bool lastToggleState = false;

  static byte buttonStateOld = unPressed;
  static unsigned long buttonScanStarted  =  0;
  unsigned long buttonDebounceTime = 50;
  static unsigned long buttonDebounceTimer;

  byte buttonStateNew;

  if ( TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
    // if more time than buttonDebounceTime has passed by
    // this means let pass by some time until
    // bouncing of the button is over
    buttonStateNew = digitalRead(ToggleButtonPin);

    if (buttonStateNew != buttonStateOld) {
      // if button-state has changed
      buttonStateOld = buttonStateNew;
      if (buttonStateNew == unPressed) {
        // if button is released
        toggleState = !toggleState; // toggle state-variable
      } // the attention-mark is the NOT operator
    }   // which simply inverts the boolean state
  }     // !true  = false   NOT true  is false
  //       !false = true    NOT false is true
  return toggleState;
}


void execute_if_Active(bool p_IsActivated) {
  printActionState(p_IsActivated);   // for demonstration purposes only

  if (p_IsActivated) {
    my_Action();
  }
}


// helper-function ignore at first
void printFileNameDateTime() {
  Serial.print( F("File   : ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("Date   : ") );
  Serial.println( F(__DATE__) );
  Serial.print( F("Project: ") );
  Serial.println( F(ProjectName) );
}

// ignore at first
// helper-function for easy to use non-blocking timing
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

void printActionState(boolean p_IsActivated) {
  static boolean lastIsActivated;
  
  if ( p_IsActivated != lastIsActivated) {
    // only if state of parameter p_logIsActivated has changed
    if (p_IsActivated) {
      Serial.println();
      Serial.println("start executing");
      Serial.println();
      digitalWrite(LED_BUILTIN, HIGH);
    }
    else { // not activated
      Serial.println();
      Serial.println("stopp executing");
      Serial.println();
      digitalWrite(LED_BUILTIN, LOW);
    }
    lastIsActivated = p_IsActivated; // update variable lastSDlogActive
  }  
}

EDIT: @zdalton13 you should not start multiple threads with overlapping content.
This is seen as cross-posting which is against forum rules.
You have another thread titled Trouble with LED trigger whre you are asking a very similar question

If you are a beginner you will accelerate proceeding by learning the important basics
Take a look into this tutorial:

Arduino Programming Course

It is easy to understand and has a good mixture between explaining important concepts and example-codes to get you going. So give it a try and report your opinion about this tutorial.

For understanding more about non-blocking timing I recommend reading this tutorial

best regards Stefan

Thanks for letting me know about the different posts. I didn't know. As for the code you posted... I'm having a really hard time understanding it or even understanding how to implement it. Could you explain a little about what is going on so I can wrap my head around it?

You do realize as long as you push the switch, the TIMER is extended ?


Always look at when the switch changes state, not the switches state.

This is a very general question. I will give some more overview. After that you have to ask specific questions about certain lines of code

The code does a lot of serial output so you should activate the serial monitor in the arduino-IDE and then watch what happends if you run the code.
You have to connect a push-button between ground and A0 to make the code react on the push-button-pressing

GetToggleSwitchState()

does what its name says
this function has a return-value.
You can assign a variable a value

myVar = 10;

a function that has a return-value can be used to assign this value to a variable
This is what is happening here

  activationMode = GetToggleSwitchState(); // must be executed all the time

assign the variable with name "activationMode" the return-value of function GetToggleSwitchState()

Which is either value "true" or value "false"

function

execute_if_Active(activationMode);

is handed over this value in the parenthesis
so the function call is either

execute_if_Active(true);

or

execute_if_Active(false);

function execute_if_Active() does what its name says

void execute_if_Active(bool p_IsActivated) {
  printActionState(p_IsActivated);   // for demonstration purposes only

  if (p_IsActivated) {
    my_Action();
  }
}

function printActionState() does what its name says

the if-condition

  if (p_IsActivated) {
    my_Action();
  }

gets handed over the value "true" or "false" inside parameter p_isActivated
this means

  if (true) {
    my_Action();
  }

then function my_Action() is executed
or

  if (false) {
    my_Action();
  }

function my_Action() will NOT be executed

you put the code that you want to be executed only if mode is "active" inside function
my_Action() ;

you can jump over the
the lines inside functions
GetToggleSwitchState()
and
TimePeriodIsOver()
as the only thing you use directly is the function-call

activationMode = GetToggleSwitchState(); 

If you find this still very hard to understand your knowledge-level is very low and I highly recommend that you work through this tutorial
You will have a lot of AHA!-Moments
Take a look into this tutorial:

Arduino Programming Course

It is easy to understand and has a good mixture between explaining important concepts and example-codes to get you going. So give it a try and report your opinion about this tutorial.

You can avoid learning the basics but then you will go on having a hard time to write code

So look into the toggleswitch-example again and post a suggestion what you think

where

inside this code you would have to put the blinking of YOUR led

best regards Stefan

I'm confused about what is going on with this function:

if ( TimePeriodIsOver(MyBlinkTimer, 500) ) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN) ); // read state of LED-output and set to invertetd state
  }

Specifically what's going on with the argument? Also, I'm also confused about why the baud rate is changed.

What's a 'scelet' ?

@zdalton13
Are you trying to make a stopwatch ?

Yes, that is one of the functions I am trying to accomplish.

// read state of LED-output and set to invertetd state

digitalRead(LED_BUILTIN) read the IO-state of the IO-pin which is represented by the name "LED_BUILTIN"
On an Arduino Uno-Board this is IO-pin 13. On an ESP32-board this is IO-pin 2 etc.

The Attentionmark "!" is the not-operator
!false meaning "not false" which is the inversion
!false meaning "not false" = true
!true meaning "not true" = false

This means the "!" inverts false to true and inverts true to false
digitalWrite(LED_BUILTIN,XXX);
set IO-pin represented by name "LED_BUILTIN" to state "XXX"
where state XXX can be "true" which is decimal a "1" and as voltage-level HIGH
where state XXX can be "false" which is decimal a "0" and as voltage-level LOW

if IO-pin is HIGH set IO-pin LOW
if IO-pin is LOW set IO-pin HIGH

which means invert the state

do you mean this line of code?

  Serial.begin(115200); // adjust baudrate in the serial monitor to match the number

9600 baud is really slow. Which effects Code-execution. So things that shouldrun fast are significantly slowed down.
Even the small (and slow 0,016 GHz) Arduino-Uno can do 1.000.000 baud on hardware-serial
The normal "Serial"

O wrong name in english skeleton

Sorry, I'm confused about the MyBlinkTimer, 500 part of the code. What does that mean?

Yes, that is what I mean about the baud rate.

BlinkTimer is the variable that is used for the nonblocking timing
and 500 is the milliseconds

The function with name TimePeriodIsOver

takes two arguments
variable "MyBlinkTimer"
and
fixed value "500"

These are the lines of code that get executed

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

Do you in principle understand what a function-definition is and what a function-call is?

If not it is totally natural that you don't yet understand the code.
Then I really HIGHLY recommend working through this tutorial
because this tutorial explains the difference
Take a look into this tutorial:

Arduino Programming Course

It is easy to understand and has a good mixture between explaining important concepts and example-codes to get you going. So give it a try and report your opinion about this tutorial.

You could have a ripped apart version of this tutorial if I write all the explainings down on the fly.
But the quality will be less than the quality of the tutorial.

Taking the time to work through this tutorial is rewarded with five times faster coding after having understood the basics

best regards Stefan

1 Like

Yes, I understand what a function is. I took a coding course a few tears ago, so all if this is a bit rusty to me.

With this sketch, clicking the pushbutton turns on an LED (good ol' #13) for 5 seconds ("testTime"). Clicking the pushbutton during that period, inside the 5 seconds, turns the LED off. It also addresses the necessity of Debouncing.

(Using INPUT_PULLUP, so the pb goes between the input pin (2) and GND.)

unsigned long markTime;
unsigned long elapsedTime;
unsigned long lockoutmark;  // pb-related
const unsigned long debounceTime = 250; // debounceTime, used w/ lockout
const byte T1 = 13;  // onboard
const unsigned long testTime = 5000;  // time on
boolean lockout;  // if true then PB doesn't get checked
boolean tripped;  // pb hit, prep Update for new pointers etc.
const byte pb1pin = 2;
byte pb1state;
boolean engaged;

void setup ()
{
  //Serial.begin(9600);
  pinMode(pb1pin,INPUT_PULLUP);
  pinMode(T1, OUTPUT);
  
  lockout = false;
  tripped = true;
  engaged = false;
  markTime = millis();
}

void loop ()
{
  elapsedTime = millis();  
  if(!lockout)  // ok to read
  {
    pb1state = digitalRead(pb1pin);
    if (!pb1state)  // pb made active
    {
      lockoutmark = millis();  // record millis when pb went LOW
      lockout = true;
      tripped = true;
      if (engaged)
      {
        digitalWrite(T1, LOW);
        engaged = false;
      }
      else
      {
        digitalWrite(T1, HIGH);
        markTime = millis();
        engaged = true;
      }
    }
  }
  
  elapsedTime = millis();
  if ((lockout) && ((elapsedTime - lockoutmark) >= debounceTime))
  {
    lockout = false; // lockout done, threshold met
    tripped = false;
  }

  if (engaged)
  {
    elapsedTime = millis();
    if((elapsedTime - markTime) >= testTime) // determine whether an Update() is due
    {
      digitalWrite(T1,LOW);
      engaged = false;
    }
  }
}

This makes so much more sense to me than the other one! Thank you. I'm trying to incorporate a stopwatch into this now, but I'm having trouble because engaged becomes false after my light goes out.

is where it checks to see if it's time to turn off the LED (or, practically, whether it's been turned off / cancelled).

Right, so if I wanted to throw in a stopwatch at this point, would I just do it before engaged is switched to false?

To indicate how long the LED has been on?