Button press actions and timings issues

Hi to all, thanks to this community i have learned alot, thanks for your patience and support to total newbies like me.

I have been working on a piece of code for a couple of days now, and im having a difficult time grasping the basic ideas such as timing using millis(), and also loops and conditions. So far i believe i get a basic idea but alot of it is going over my head.

Hardware :

  • Arduino board
  • 2 LEDs
  • Buzzer
  • Relay
  • Push button switch

What i try to accomplish is the following:

-Upon powering up the board, turn on LED1 (relay, LED2 and buzzer stay off)

  • When button is pushed, the following happens:

  • LED 1 turns OFF and LED2 turns ON

  • Audible sound from buzzer is heard

  • Relay TURNS ON, after 20 seconds it TURNS OFF

  • 5 minutes after button is pushed, LED 2 TURNS OFF, LED1 TURNS ON and audible sound from buzzer is heard. After this it goes back at the beggining (the button can be pushed again)

My problem right now is exiting the loop related to if ((timing) && (millis() >= startTime)) condition on the code below. The second audible buzzer sounds doesnt stop.

Here is the code i have so far. In the code the timing have been shortened for practical purposes. The correct times are the ones listed above not the one in the code.

#include <ezButton.h> // import button denoise library

boolean timing = false;
unsigned long startTime;

// constants:
const int BUTTON_PIN = 7; 
const int RELAY_PIN = 9; 
const int LED_PIN_1 = 3; 
const int LED_PIN_2 = 4;
const int BUZZER_PIN = 8; 

ezButton button(BUTTON_PIN);  // create ezButton object that attach to pin 7;

// variables:
int relayState = LOW;   
int ledState = LOW;   


// setup:
void setup() {
  Serial.begin(9600);         // initialize serial
  pinMode(RELAY_PIN, OUTPUT); 
  pinMode(LED_PIN_1, OUTPUT);   
  pinMode(LED_PIN_2, OUTPUT);   
  pinMode(BUZZER_PIN, OUTPUT);       
  digitalWrite(LED_PIN_2, HIGH); //turn on this led by default
  button.setDebounceTime(50); // set debounce time to 50 milliseconds , related to button librry
}

void loop() {
  
  button.loop(); // MUST call the loop() function first, related to button librry

  //actions to do when button is pressed
  if(button.isPressed()) {
  
    Serial.println("The button is pressed");
    digitalWrite(RELAY_PIN, HIGH); 
    digitalWrite(LED_PIN_1, HIGH);  
    digitalWrite(LED_PIN_2, LOW);  
    tone(BUZZER_PIN, 100, 100); 
    timing = true; // set this to know the button was pressed and that we have to check millis()
    startTime = millis() + 3000L;
     
     }

     
   //3 seconds after button press do this
  if ((timing) && (millis() >= startTime)) { //3 seconds after button press do the following
    
    Serial.println("Time has passed"); // floods the output, intended to only run it once
    digitalWrite(RELAY_PIN, LOW); 
    digitalWrite(LED_PIN_1, LOW);  
    digitalWrite(LED_PIN_2, HIGH);  
    tone(BUZZER_PIN, 500, 1000); // buzzer doesnt stop
   
    // at this point i want to return to initial conditions but no avail
    }

}

Any help would be greatly appreciated.

Sorry, this is a classic mistake. Time stamps should never be in the future (reasons are complicated).

    startTime = millis() + 3000L;

You need to

    startTime = millis();

and then

  if ((timing) && (millis() - startTime) >= 3000L) { //3 seconds after button press do the following

instead of

  if ((timing) && (millis() >= startTime)) { //3 seconds after button press do the following
startTime = millis() + 3000L;

The name of that variable is misleading don't you think ?

   Serial.println("Time has passed"); // floods the output, intended to only run it once

What is the point of having a variable named timing, testing its value but never setting it to false once the timed action has occurred ?

Take a look at Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

Thank you aarg and UKHeliBob for the feedback, to be honest, I took the timing code from another post and adapted it to mine without understanding it fully. Will check out the given links and report my findings.

A follow up to my first comment, i believe i got the initial goals working. I am still trying to understand the intricacies of using millis(), but the following code appears to work:

//Global Variables
const int BUTTON_PIN = 7; 
const int RELAY_PIN = 9; 
const int LED_PIN_1 = 3; 
const int LED_PIN_2 = 4;
const int BUZZER_PIN = 8; 

unsigned long buttonPushedMillis; // when button was released
unsigned long devicesTurnedOnAt; // when devices were triggered
unsigned long turnOnDelay = 0; // wait to trigger devices to turn on
unsigned long turnOffDelay = 5000; // turn off devices after this time
bool devicesReady = false; // flag for when button is let go
bool devicesState = false; // for devices are on or off


void setup() {
  Serial.begin(9600);         // initialize serial
  pinMode(RELAY_PIN, OUTPUT); 
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN_1, OUTPUT);   
  pinMode(LED_PIN_2, OUTPUT);   
  pinMode(BUZZER_PIN, OUTPUT);       
  digitalWrite(LED_PIN_2, HIGH); //turn on this led by default
}

void loop() {
  
 unsigned long currentMillis = millis(); // get the time at the start of this loop()

 // check the button
 if (digitalRead(BUTTON_PIN) == LOW) {
  // update the time when button was pushed
  buttonPushedMillis = currentMillis;
  devicesReady = true;
 }

// make sure this code isn't checked until after button has been let go
 if (devicesReady) {
   //this is typical millis code here:
   if ((unsigned long)(currentMillis - buttonPushedMillis) >= turnOnDelay) { // okay, enough time has passed since the button was let go.
     Serial.println("The button is pressed");
    digitalWrite(RELAY_PIN, HIGH); 
    digitalWrite(LED_PIN_1, HIGH);  
    digitalWrite(LED_PIN_2, LOW);  
    tone(BUZZER_PIN, 100, 100); 
    delay(3000); //wait this time
    digitalWrite(RELAY_PIN, LOW); // turn off relay after 3 seconds
     devicesState = true; // setup our next "state"
     devicesTurnedOnAt = currentMillis;  // save when this was triggered
     devicesReady = false;      // wait for next button press
   }
 }

// see if we are watching for the time to turn off devices
 if (devicesState) {
   if ((unsigned long)(currentMillis - devicesTurnedOnAt) >= turnOffDelay) {  // okay, devices are on, check for now long
    Serial.println("ok time has passed");
    digitalWrite(RELAY_PIN, LOW); 
    digitalWrite(LED_PIN_1, LOW);  
    digitalWrite(LED_PIN_2, HIGH);  
    tone(BUZZER_PIN, 1000, 100); 
     devicesState = false;
   }
 }
}

What the code does is:

  • LED 1 is turned on upon powering up device. Upon button press, LED 1 turns OFF, LED 2 turns ON
  • Sound is emmitted from buzzer
  • Relay activates for 3 seconds then turns off (using delay(),)
  • After 10 seconds, another sound is emmited and the LEDs are reversed, returning to initial situation.

I would greatly appreciate any feedback on how to make it better.. still trying to get around this coding business.

Hi esquirish,

your description

Upon powering up the board, turn on LED1 (relay, LED2 and buzzer stay off)

  • When button is pushed, the following happens:

is a typical case for a software-tecnique called "statemachine"
You have a always the same running through sequence of actions
as there are

  • turn on LED1
  • wait for button to be pressed
  • if button is pressed switch LEDs, buzz, make timestamp, and turn on relay
  • wait for 20 seconds to pass by then turn relay off
  • wait until 5 minutes have passed by since timestamp (made at button-press) then switch leds, buzz and go back to initial state

I recommend reading this tutorial about statemachines

best regards Stefan

Thanks alot Stefan for the feedback and the link. Very informative, this is the first time i read about "Finite State Machines".
Best regards,
esquirish