2

How do I prevent an alarm, which checks if the current time matches an alarm time in hours and minutes, from going more than once within one minute?

I am using the DS3231 RTC module and RTClib from adafruit. I want to check if the current time equals to the alarm time, then set the alarmFlag variable. I want to clear alarmFlag when a button on pin D5 is pressed.

The code below works fine as long as I don't press the button within about a minute from the start of the alarm. If I press the button within about a minute from the start of the alarm, the alarmFlag will be 0 for one loop() cycle, but then (almost immediately, I don't have much in loop()) will be back to 1. I want for the button to stop the alarm completely, so it fires again only on the next day.

uint8_t alarm[2] = {19, 30}; // hour (24), minute

// other code ...

void loop() {

  currTime = rtc.now();

  // check if current time matches alarm time
  // if it matches, it matches for a whole minute, which is the problem here
  if (!alarmFlag && alarm[0] == currTime.hour() && alarm[1] == currTime.minute()) {
    alarmFlag = true;    
  }

  // clear alarmFlag on button press (this is a shortened version, excuse the lack of debouncing/other stuff)
  if (digitalRead(5) == HIGH) {
    alarmFlag = false;
  }

} 

Notes:

  1. I can not use the RTC's built-in alarms because I need them elsewhere.
  2. I don't want to just add currTime.seconds() == 0 to the first if statement, because that would skip the alarm if the loop() cycle takes more than a second.
  3. I know that the way I detect a button press in the example code is suboptimal, I wrote a better way to deal with this in my main code, but I don't want to clutter up this question.
6
  • Have you tried adding the current seconds to your first if statement? Something like this: alarm[2] == currTime.seconds(). Oh, and maybe this too: uint8_t alarm[3] = {19, 30, 0};.
    – VE7JRO
    Jan 10 at 3:28
  • 1
    store the minute when the alarm triggered ... do not trigger alarm if stored minute equals current minute ... or use another flag to signify that an alarm already raised
    – jsotola
    Jan 10 at 4:08
  • 1
    You might want to read about "finite state machines" because this is what you have here. Granted, a really simple one, but this knowledge will be valuable in your future life as a developer. You will need at least 3 states: armed, triggered, muted. The transition conditions and how to implement it is left as an exercise for you. Jan 10 at 7:51
  • Make your alarmFlag have three states, 0 for false, 1 for alarming, 2 for muted.
    – Dave X
    Jan 10 at 14:24
  • 1
    I'd prefer to make a comment rather than an answer; however, I'm not allowed to do so. I use a single RTC interrupt to service any number of actual alarms. Each time I activate a new alarm it is sorted into a linked list with the earliest scheduled alarm at the head. Then I setup the RTC hardware to interrupt when that time is reached. The listhead is discarded once its interrupt occurs and the next one on the list is setup in the RTC.
    – user81882
    Jan 12 at 0:59

2 Answers 2

1

As suggested by the busybee, this is a textbook application of the concept of finite state machine. Below is a state diagram that hopefully captures the desired behavior. Its meaning is hopefully obvious at first sight.

State diagram

Here your alarmFlag is replaced by the TRIGGERED state. Please note that, with this state machine, the only way to turn the alarm off is to press the button. If nobody is there to press it, is goes on forever. This kind of issue is quite obvious when looking at the state diagram, less so when looking at the code. This is why it is advisable to draw a state diagram before you start coding. If this behavior is a not desirable, you may add an extra transition out of the TRIGGERED state, which would happen on a timeout.

It is also worth noting that, with this state machine, pressing the button multiple times in quick succession has the same effect as pressing it once. In other words, there is no point in debouncing the button.

For implementing this in C++, I suggest you follow the steps of this tutorial on finite state machines.

-1

If you add a delay(60*1000) to your button press function, then you will essentially have a cool off period before the alarm is checked again. The alarm cannot go off again during that delay;

For non-blocking code, you could store the time you pushed the button, and then check that time before checking it's the alarm time.

    //code to add to your button press
    lastbuttonpress = millis();  

    //code to add before your alarm check
    if (lastbuttonpress - millis() > 60*1000) {
        //do your alarm check
    }
3
  • 1
    does it mean you can't stop the alarm until it runs one minute?
    – Juraj
    Feb 6 at 7:02
  • no, the alarm would be stopped immediately on button press. These recommendations only prevent the alarm from being triggered again within one minute of the alarm being silenced
    – rfii
    Feb 6 at 20:14
  • but the time for the end of the problem is known and why have a 4 byte variable instead of a 1 byte bool and wait more than necessary
    – Juraj
    Feb 7 at 6:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.