Programming alarm after some time and resetting the alarm after button is pressed for certain period

Hello,
I am trying to program a couple of features for a project.
First feature would be triggering the alarm after X time has passed
(for now just to test the concept I have put every 10 seconds led
that represents alarm turns on, later it will be buzzer that turns on every x hours).
Second feature would be resetting the alarm by holding a button pressed for 3 seconds (in this case resetting means turning off LED and starting the alarm count again, later it will mean turning off buzzer and continuing the rest of code until alarm turns back on).
Since my goal is to make when alarm is on no other code should be executed, only thing that should happen is for LED (buzzer) until the button is pressed for 3 seconds then it should go back to regular loop.
I am having some trouble in thinking of proper way to write the code, should I trigger some kind of interrupt for alarm or avoid that?
So far this is what I have got but I cant reset the alarm:

const int LED_PIN = 13;    
const int rfa = 7;  

const long BLINK_INTERVAL = 10000;  

bool alarmState = false;
int startPressed = 0;   
int holdTime = 0;       
int prevState = LOW;
bool buttonReleased = false;


unsigned long previousMillis = 0;
unsigned long currentMillis;


void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(rfa, INPUT);
}

void loop() {
   
  if(alarmState == false) {
    currentMillis = millis();
    digitalWrite(LED_PIN, LOW);

    if (currentMillis - previousMillis >= BLINK_INTERVAL) {
    alarmState = true;

  }
  
  }

  else if (alarmState == true) {
    digitalWrite(LED_PIN, HIGH);
    
    int state = digitalRead(rfa);
    
    if(state != prevState) 
    { 
      if (state == HIGH) {
      buttonReleased = false;
      startPressed = millis();
      }
      else {
        buttonReleased = true;
      }
      
      prevState = state;
    }
    
    if (buttonReleased == true) {
        holdTime = millis() - startPressed;
        if (holdTime >= 3000)
        {
          digitalWrite(LED_PIN, LOW);
          alarmState = false;
          previousMillis = currentMillis;
        }
        buttonReleased = false;
        startPressed=0;
      }
    
}
   
    
}

I can't look closely right now, but I note you have not made any use of the serial monitor.

Put

  Serial.begin(115200);

in your setup() function.

Then sprinkle Serial.print() liberally about your code, to check the values of key variables and to confirm that they inform the flow of your code properly.

a7

shouldn't the Button be checked when alarmState is both set and cleared?

since alarmState is false by default, the BLINK_INTERVAL needs to timeout before the else if where the button is check is invoked

since there are 2 timers, it would be clearer if the flags were named for the timer: alarmTimer, buttonTimer, possibly set to the time period

buttons are typically connected between the pin and ground, the pin configured as INPUT_PULLUP to use the internal pullup resistor which pulls the pin HIGH and when pressed, the button pulls the pin LOW.

Yeah I have been putting Serial prints in different parts of code to sort of debug it but I removed them before sending the code here.

The button is actually rf receiver that can be programmed to work as toggle or momentary button output (meaning when I press a button on a remote, rf receiver gives 5v on its output either while button is pressed or until button is pressed second time).

Hello tuadru

Post a timing diagram showing all state transitions and dependies for the project.

Have a nice day and enjoy coding in C++.

So here is my code with just some Serial prints put in to see where it is stuck and how the flow goes:


const int LED_PIN = 13;
const int rfa = 7;

const long BLINK_INTERVAL = 10000;
bool alarmState = false;
int startPressed = 0;    
int holdTime = 0;        
int prevState = LOW;
bool buttonReleased = false;


unsigned long previousMillis = 0;  
unsigned long currentMillis;


void setup() {
  Serial.begin(9600);

  pinMode(LED_PIN, OUTPUT);
  pinMode(rfa, INPUT);
}

void loop() {
   
  if(alarmState == false) {
    currentMillis = millis();
    Serial.println("1");
    digitalWrite(LED_PIN, LOW);

    if (currentMillis - previousMillis >= BLINK_INTERVAL) {
    alarmState = true;
    Serial.println("2"); 
  }
  
  }

  else if (alarmState == true) {
    digitalWrite(LED_PIN, HIGH);
    Serial.println("3");
    
    int state = digitalRead(rfa);
    
    if(state != prevState) 
    { 
      if (state == HIGH) {
      buttonReleased = false;
      Serial.println("4");
      startPressed = millis();
      }
      else {
        buttonReleased = true;
        Serial.println("5");
      }
      
      prevState = state;
    }
    
    if (buttonReleased == true) {
      Serial.println("6");
        holdTime = millis() - startPressed;
        if (holdTime >= 3000)
        {
          Serial.println("7");
          digitalWrite(LED_PIN, LOW);
          alarmState = false;
          previousMillis = currentMillis;
        }
        buttonReleased = false;
        startPressed=0;
        Serial.println("8");
      }
    
}
   
    
}

What happens is this:
First it prints out all 1s until 10 seconds pass, then it prints 2 and then all 3s. When I press button for reset it prints out 4 then 3s again until I release the button 3-4 seconds after at which point it prints out 5 6 8 and back to 3s. So basically it skips over if loop where I check if holdTime is greater out than 3 seconds.

look this over

const int LED_PIN = 13;
const int PinBut  =  7;

const unsigned long ALARM_INTERVAL = 5000;
const unsigned long BLINK_INTERVAL =  500;

unsigned long alarmTime;
unsigned long alarmMillis;

const unsigned long ButtonTime = 3000;
unsigned long buttonMillis;

bool alarmSet;
bool buttonSet;

byte butPrev;

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    pinMode (LED_PIN, OUTPUT);
    pinMode (PinBut,  INPUT_PULLUP);
}


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

    // check for alarm timeout(s)
    if (alarmSet && currentMillis - alarmMillis > alarmTime)  {
        Serial.println ("alarm");

        alarmMillis = currentMillis;    // reset alarm value to blink
        alarmTime   = BLINK_INTERVAL;

        digitalWrite (LED_PIN, ! digitalRead (LED_PIN));    // toggle
    }

    // check for button timeout
    if (buttonSet && currentMillis - buttonMillis > ButtonTime)  {
        Serial.println ("alarm set");
        digitalWrite (LED_PIN, HIGH);

        alarmMillis = currentMillis;
        alarmTime  = ALARM_INTERVAL;
        alarmSet   = true;
        buttonSet  = false;
    }

    // check for button press/release
    byte but = digitalRead (PinBut);
    if (butPrev != but)  {
        butPrev = but;
        delay (20);                 // debounce

        if (LOW == but)  {          // pressed
            Serial.println ("button pressed");
            buttonMillis = currentMillis;
            buttonSet    = true;
        }
        else if (buttonSet)  {      // not pressed
            if (alarmSet)
                Serial.println ("alarm reset");
            else
                Serial.println ("button released");
            alarmSet  = false;
            buttonSet = false;
        }
    }
}

Replace the RF receiver with a button or switch.

Even if you are fully 100 percent confident, it always makes sense to leave as many things out as you can whilst struggling working on a part that can be separately perfected.

a7

Ok I have tested a bit more and alarm is actually being reseted now (led goes off) butfor very very short time (not even few seconds) before it turns on again.
Ill try gcjr code in a minute. As for rf receiver I currently dont have a press button at my place so I work with rf but I have tested it with multimeter and code with just it and led and works fine so far.

P.S.
The time between me resetting alarm and it turning on again seems to be different every time, probably depending on at what time I let the button go.

Hello tuadru

Your design will end up here?

Playing with @gcjr's code a bit.

One thing.

  if (alarmSet) {
      Serial.println ("alarm reset");
      digitalWrite (LED_PIN, LOW); // otherwise the LED gets randomly left on or off
  }

Second, there does not appear to be any delay in the reset. @tuadru told us

resetting the alarm by holding a button pressed for 3 seconds

and so far "a button" is the same button that raises the alarm.

Lastly, it seems that alarm condition reset is only accomplished with a brief stab of the button - dwelling on the button reinitiates the alarm sequence: a long period of the LED being on, followed by blinking.

Now my own version doesn't work at all and it is time to go to the beach. :frowning: , :slight_smile: .

L8R

a7

1 Like

OK, y'all know what I am risking by possibly making she who will not be kept waiting waiting.

This should turn on the alarm after a longish press, and turn it off after a shorter longish press.

I tested it, but the beach.

const int LED_PIN = 13;
const int rfa = 7;

const unsigned long WAIT_INTERVAL = 3000;
const unsigned long RESET_INTERVAL = 1500;

bool alarmState;

# define PRESST LOW
bool buttonState = !PRESST;

void setup() {
  Serial.begin(9600);

  pinMode(LED_PIN, OUTPUT);
  pinMode(rfa, INPUT_PULLUP);
}

void loop() {

  delay(50); // crude loop throttle - avoids many problems

  unsigned long now = millis();
  static unsigned long timer;

  bool buttonNow = digitalRead(rfa) == PRESST;
  static bool buttonFlag;

  if (buttonNow != buttonState) {
    if (buttonNow) {
      buttonFlag = true;
      timer = now;
    }
    else buttonFlag = false;

    buttonState = buttonNow;
  }

  if (!alarmState) {
    if (buttonFlag) {
      if (now - timer > WAIT_INTERVAL) {
        Serial.println("ALARM ON");
        alarmState = true;
        digitalWrite(LED_PIN, HIGH);

        buttonFlag = false;
      }
    }
  }
  else { // alarm state

// do any blinking or whatever here

    if (buttonFlag) {
      if (now - timer > RESET_INTERVAL) {
        Serial.println("ALARM OFF");
        alarmState = false;
        digitalWrite(LED_PIN, LOW);

        buttonFlag = false;
      }
    }
  }
}

@tuadru if you haven't, check this out:

a7

Maybe I didnt explain it properly @gcjr , I care about button state only when alarm is triggered and use it only to reset/disable the alarm. I dont want to check button state when alarmState is false. I have tried the simulator you have sent @alto777 , have put pushbutton that is pulled down so it simulates my rf receiver that gives HIGH when button is pressed. So far it is behaving same as irl, I manage to reset the alarm but it goes back to being triggered much faster than 10 seconds. Also hope your missus didn't have to wait too long :slight_smile:

Hopefully it doesen't end up being dead mans switch :slight_smile:

Eureka! Code works now, just had to move currentMillis= millis(); outside of if loop.


const int LED_PIN = 13;
const int rfa = 7;

const long ALARM_TIMER = 10000;
const long BUTTON_TIMER = 3000;
bool alarmState = false;
int startPressed = 0;    
int holdTime = 0;        
int prevState = LOW;
bool buttonReleased = false;


unsigned long previousMillis = 0;  
unsigned long currentMillis;


void setup() {
  Serial.begin(9600);

  pinMode(LED_PIN, OUTPUT);
  pinMode(rfa, INPUT);
}

void loop() {
   currentMillis = millis();
  if(alarmState == false) {
    
    Serial.println("1");
    digitalWrite(LED_PIN, LOW);

    if (currentMillis - previousMillis >= ALARM_TIMER) {
    alarmState = true;
    Serial.println("2"); 
  }
  
  }

  else if (alarmState == true) {
    digitalWrite(LED_PIN, HIGH);
    Serial.println("3");
    
    int state = digitalRead(rfa);
    
    if(state != prevState) 
    { 
      if (state == HIGH) {
      buttonReleased = false;
      Serial.println("4");
      startPressed = millis();
      }
      else {
        buttonReleased = true;
        Serial.println("5");
      }
      
      prevState = state;
    }
    
    if (buttonReleased == true) {
      Serial.println("6");
        holdTime = millis() - startPressed;
        if (holdTime >= BUTTON_TIMER)
        {
          Serial.println("7");
          digitalWrite(LED_PIN, LOW);
          alarmState = false;
          previousMillis = currentMillis;
        }
        buttonReleased = false;
        startPressed=0;
        Serial.println("8");
      }
    
}
   
    
}

Now only change I want to make is actually to reset alarm as soon as button is pressed for 3 seconds without needing to pull the finger off the button.

it's not clear what alarmState represents. looks like the alarm is active when alarmState is false. looks like alarmState is false by default.

and alarmState is set to true, the alarm is cleared, as soon as the alarm time has expired without a button press

what do you want to happen when the alarm time expires?

Actually when alarmState is false (which is by default in the begining), the alarm (which at this time is LED) is off. Then every 10 seconds alarm should be triggered (LED on) and it can only be turned off/resetted by pressing the button and holding it down for 3 seconds. At this point only thing I want to change from my last posted code is for alarm to be turned off after button was held down for 3 seconds (without the need for the button to be released, just as soon as it was held down for 3 seconds).

then why do you care that the button is released? (do you need buttonReleased)?

shouldn't the following set alarmState to true since is seems that alarmState== true means the alarm is not enabled?

and if you want the alarm to be turned off only when the button is pressed, what is alarmState set to true in following

the code is confusing

Because at first I wanted to register how long button was pressed and compare that to 3 seconds and if its greater than 3 then alarm should be turned off. But now thinking more about it I think its better if I just turn off alarm once button was pressed for 3 seconds and not register when it was released (there will be no need for that then.

I don't know which part of code is confusing so I'll try to explain again:
I track whether the alarm is on or off with alarmState variable.
When the variable is false it means it is off, when it's true then it is on.
Alarm in this case is represented with LED light which is off when alarm is off (hence digitalWrite(LED_PIN, LOW);). Alarm should be triggered every 10 seconds (hence the if loop where I check if 10 seconds have passed and then I put alarmState as true). Now when alarm is triggered, LED is turned ON. Only way to turn off the alarm is by pressing the button, holding it down for 3 seconds or more and then releasing it. Now I want to change the last part so that the button doesn't need to be released but as long as it was held down for 3 seconds it should turn off the alarm and LED light. I hope it is more clear now.