A timer for a button

Hello everyone

I've been trying without any success to add a timer to this code.

What I'm wanting to do is turn the relay on and off with the button (this part works), but when the relay is turned on start a delay off timer to turn off the relay after X seconds

int button1 = 0;

int rl1 = 5;

//States for Relay and Button (1)

int state1 = HIGH;      // the current state of the output pin
int reading1;           // the current reading from the input pin
int previous1 = LOW;    // the previous reading from the input pin

long time1 = 0;          // the last time the output pin was toggled

long debounce1 = 200;   // the debounce time, increase if the output flickers

unsigned long turnOnDelay = 3000; 



void setup() {
  // put your setup code here, to run once:
  
pinMode(button1, INPUT);
pinMode(button1, INPUT_PULLUP);
pinMode(rl1, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
  
reading1 = digitalRead(button1);
unsigned long currentMillis = millis();

//Condition Relay 1
  
  if (reading1 == HIGH && previous1 == LOW && millis() - time1 > debounce1) {
    if (state1 == HIGH)
      state1 = LOW;
      
    else
      state1 = HIGH;

    time1 = millis();    

    
    
  }


   digitalWrite(rl1, state1);   
      
   previous1 = reading1;
   
}

Hello mayfield016,
nobody is responding you ? a few remarks ! (I am not so fluent in english...)

Syntax : it is better to separate with ( ) the different terms, otherwise it is necessary to know the priority of treatment of the conditions. I forgot it for a long time!

if ( (reading1 == HIGH) && (previous1 == LOW) && (millis()-time1 > debounce1) ) {

But even when you are used to programming, condensing multiple conditions in a single line of instructions is difficult to understand, and often leads to logic errors (beyond 2 conditions, at least for me...)
Separate them :

unsigned long turnOnStart;                  // place it at the top of code        


if (reading1 == HIGH) {                     // button is pressed
  turnOnStart = millis();                   // start the timer
  digitalWrite(rl1, 1);                     // relay ON  
}

else {                                      // button is released
 if (millis()-turnOnStart > turnOnDelay) {  // decrease the timer, and at the end,  
   digitalWrite(rl1, 0);                    // turn relay OFF
 }
}

Thankyou for your reply

This is confusing to me. If I take your code and place it instead of my current void loop it doesn't work.

Ah ! maybe i make a mistake ?
One moment, I look at...

Thankyou I really appreciate it. It's been confusing me all day.

I sometimes spend several hours looking for a silly mistake.

I forgot, I always work in negative logic on my buttons: they are active when set to zero; I forgot the inversion (!) in my button test.

if (!digitalRead(button1))

voilà la correction


int button1 = 0;                                          // pin Button (but pin 0 & 1 are for serial, prefer 2 or else  
int rl1     = 5;                                          // pin Relay
int turnOnDelay;                                          // tempo (3000 * 1 millisecond = 3 seconds)
unsigned long previousMillis;                             // for count the millisecond


void setup() {                                            // put your setup code here, to run once:
pinMode(button1, INPUT);
pinMode(button1, INPUT_PULLUP);
pinMode(rl1, OUTPUT);

}

void loop() {                                             // put your main code here, to run repeatedly:
  if (!digitalRead(button1)) {                            // in this case, no need for debounce
    turnOnDelay = 3000;                                   // tempo 3 seconds begin when button is released
    digitalWrite(rl1, 1);                                 // turn ON the relay
    PORTB |= B00100000;                                   // PB5 = 1 (internal led ON) 
  }  
  
  
  if (previousMillis != millis()) {                       // wait for 1 millisecond delay
    previousMillis = millis();                            // reload for next millisecond
  
    if (turnOnDelay) {                                    // if turnOnDelay > 0  (button was activated)
      turnOnDelay--;                                      // decrease 3 seconds tempo (3000 * 1 millisecond)
      if (turnOnDelay == 0) {
        digitalWrite(rl1, 0);     	                      // when it reach 0, turn OFF the relay 
        PORTB &= B11011111;                               // PB5 = 0 (internal led OFF) 
      }      
    }
  }  
}

You use pin 0 as button input, but pins 0 and 1 are also used for the serial link; later on, you could choose another pin, we prefer to keep those to use with the serial monitor, for example.

sorry, I could have put a more understandable instruction :

PORTB |= Bxxxxxxxx

is the same as :

digitalWrite(13, 1); // turn ON the internal LED
digitalWrite(13, 0); // turn OFF the internal LED

Thanks alot for your time PatMax

I have been playing around with your code and it's getting me closer. What would I need to add to the code to manually turn off the relay before the timer does?

for example push button to turn on - timer starts then before timer times out a button press turns off relay/cancels timer

int button1 = 0;                                          // pin Button (but pin 0 & 1 are for serial, prefer 2 or else  
int rl1     = 5;                                          // pin Relay
int turnOnDelay;                                          // tempo (3000 * 1 millisecond = 3 seconds)
unsigned long previousMillis;                             // for count the millisecond


void setup() {                                            // put your setup code here, to run once:
pinMode(button1, INPUT);
pinMode(button1, INPUT_PULLUP);
pinMode(rl1, OUTPUT);
digitalWrite(rl1, 1);
digitalWrite(13, 0);
}

void loop() {                                             // put your main code here, to run repeatedly:
  if (!digitalRead(button1)) {                            // in this case, no need for debounce
    turnOnDelay = 3000;                                   // tempo 3 seconds begin when button is released
    digitalWrite(rl1, 0);                                 // turn ON the relay
    //PORTB |= B00100000;                                   // PB5 = 1 (internal led ON) 
    digitalWrite(13, 1); // turn ON the internal LED
  }  
  
  
  if (previousMillis != millis()) {                       // wait for 1 millisecond delay
    previousMillis = millis();                            // reload for next millisecond
  
    if (turnOnDelay) {                                    // if turnOnDelay > 0  (button was activated)
      turnOnDelay--;                                      // decrease 3 seconds tempo (3000 * 1 millisecond)
      if (turnOnDelay == 0) {
        digitalWrite(rl1, 1);                             // when it reach 0, turn OFF the relay 
        //PORTB &= B11011111;                               // PB5 = 0 (internal led OFF) 
        digitalWrite(13, 0); // turn OFF the internal LED
      }      
    }
  }  
}

:grinning_face_with_smiling_eyes:

I simplified a part at the bottom of the code that was unnecessarily complicated.
With a second button, it would be very easy, I let you do it!
With the same button, it's feasible, BUT : we start to multiply the conditions, as soon as other conditions will be necessary, it will be too complicated to use the same approach : we'll have to use the state machine ! on the other hand, you'll see that now, the debounce becomes necessary on the button1...

int button1 = 0;                                          // pin Button1 (but pin 0 & 1 are for serial, prefer 2 or else  
int rl1     = 5;                                          // pin Relay
unsigned long previousMillis;                             // for count the millisecond
#define turnOnDelay 3000                                  // 3000 * 1 millisecond = 3 seconds 


void setup() {                                            // put your setup code here, to run once:
  pinMode(button1, INPUT);
  pinMode(button1, INPUT_PULLUP);
  pinMode(rl1, OUTPUT);
  digitalWrite(rl1, 1);                                   // relay
  digitalWrite(13, 0);                                    // internal LED
}


void loop() {                                             // put your main code here, to run repeatedly:
  if (!digitalRead(button1)) {                            // in this case, no need for debounce

    if (millis()-previousMillis <= turnOnDelay)           // button1 was recently activated, it is a second hit
      previousMillis = 0;                                 // force previousMillis to activate "line 30"
   
    else {
      previousMillis = millis();                          // delay will start at the release of button1
      digitalWrite(rl1, 0);                               // turn xx the relay
      digitalWrite(13, 1);                                // turn ON the internal LED
    }  
  }  
    

  if (millis()-previousMillis > turnOnDelay) {            // 3000 milliseconds after button1 is released, 
    digitalWrite(rl1, 1);                                 // turn xx the relay     
    digitalWrite(13,  0);                                 // turn OFF the internal LED
  }
}

Ooops the code is not perfect, it makes the relay beat as long as the key is pressed...
OK we are learning, uuuuh

Show us a good schematic of your circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.

Try this:

// Version    YY/MM/DD  Description
// 1.00       21/07/24

#define switchIsClosed      LOW
#define switchIsOpen        HIGH

#define enabled             true
#define disabled            false

#define relayON             HIGH
#define relayOFF            LOW

//***********************************
const byte button1        = 2;  //+5V----[50k INPUT_PULLUP]----[pin]----[switch]----GND
const byte relay          = 5;  //[pin]----[220R]----[NPN transistor base]
const byte heartbeatLED   = 13; //[pin]----[220R]----[>|]----GND

bool relayTimerFlag       = disabled;

byte lastButton1State     = switchIsOpen;

//timing stuff
unsigned long turnOnDelay = 3000;

unsigned long heartbeatMillis;
unsigned long switchMillis;
unsigned long relayMillis;


//************************************************************************
void setup()
{
  pinMode(button1, INPUT_PULLUP);

  digitalWrite(relay, relayOFF);
  pinMode(relay, OUTPUT);
  pinMode(heartbeatLED, OUTPUT);

} //END of setup()


//************************************************************************
void loop()
{
  //***********************
  //time to toggle the heartbeatLED (every 1/2 second) ?
  if (millis() - heartbeatMillis >= 500)
  {
    //restart the TIMER
    heartbeatMillis = millis();

    //toggle the LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //***********************
  //time to check the switches (every 50ms) ?
  if (millis() - switchMillis >= 50)
  {
    //restart the TIMER
    switchMillis = millis();

    checkSwitches();
  }

  //***********************
  //if enabled, has the relay TIMER expired ?
  if (relayTimerFlag == enabled && millis() - relayMillis >= turnOnDelay)
  {
    //disable the TIMER
    relayTimerFlag = disabled;

    digitalWrite(relay, relayOFF);
  }

  //***********************
  //other non-blocking code goes here
  //***********************

} //END of loop()


//************************************************************************
void checkSwitches()
{
  byte state;

  //*****************************************       button1
  state = digitalRead(button1);

  //***********************
  //has the switch changed state ?
  if (lastButton1State != state)
  {
    //update to the new state
    lastButton1State = state;

    //************
    //if we are not timing, was the switch closed ?
    if (relayTimerFlag == disabled && state == switchIsClosed)
    {
      //enable the relay TIMER
      relayTimerFlag = enabled;

      digitalWrite(relay, relayON);

      //restart the TIMER
      relayMillis = millis();
    }

  } //END of  if (lastSwitchState != state)

} //END of  checkSwitches()

//************************************************************************

If there is something you do not understand, it is up to you to ask for an explanation.

For post #11.

Thanks for the help LarryD What software are you using to draw your diagrams?

My schematic program is not available any more. :frowning:


Many here use Eagle.


EasyEDA is an online option.

You might find these tutorials useful
Debouncing Switches in Arduino handles button inputs and lets you check for change of state
How to write Timers and Delays in Arduino covers timers and as noted above don't combine timer tests with other logical tests.
Multi-tasking in Arduino covers structuring your code as a series of tasks like {check button, start time} {check timer finished do something}
multitaskingDiagramSmall