rtc alarm interrupt notworking

hello
I’m using the RTC DS3231, and i need to setup two alarms, alarm1 will turn on some devices and alarm2 will turn them off.
I did set the alarms, created the interrupt, and the interrupt happens at alarm1, but alarm2 is never triggered.
The problem is that on the validation it says alarm triggered for both the alarms, and don’t execute the other instructions.
Here is some code:

#include "WiFi.h"
#include <WebServer.h>
#include "time.h"
#include "RTClib.h"
#include <Wire.h>
#include "microSD.h"

#define CLOCK_INTERRUPT_PIN 13
RTC_DS3231 rtc;

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

  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
  }

  rtc.disable32K();
  // digitalWrite(relayFilter, HIGH);
  pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, FALLING);
  
  rtc.writeSqwPinMode(DS3231_OFF);
  rtc.clearAlarm(1);
  rtc.clearAlarm(2);

  
  rtc.setAlarm1(DateTime("", aquariumTimeOn.configValue), DS3231_A1_Hour);
  rtc.setAlarm2(DateTime("", aquariumTimeOff.configValue), DS3231_A2_Hour);
}

the code above works fine, but when the interrupt happens and it call the function onAlarm, it is acting as both alarm1 and alarm2 have been triggered, is printing on the Serial Monitor:
“Alarm 1 occurred!”
“Alarm 2 occurred!”
however the function operateDispositive is not being called

void onAlarm() {
    if(rtc.alarmFired(1)){
      rtc.clearAlarm(1);
      operateDispositive("all", true);
      Serial.println("Alarm 1 occured!");
    }
    if(rtc.alarmFired(2)){
      rtc.clearAlarm(2);
      operateDispositive("all", false);
      Serial.println("Alarm 2 occured!"); 
    }
}

note: when the interrupt happens the CLOCK_INTERRUPT_PIN is going from HIGH to LOW

hope someone can point me in the right direction

Why are you using interrupts…??

I never used interrupts before. But considering that i need to test the time to turn devices on and off, it felt like the moment to learn about interrupts

the function operateDispositive is not being called

Or it is being called but is doing nothing. That seems a more likely explanation. Please post complete code which is long enough to demonstrate your problem but short enough to post in code tags.

I agree with bluejet's question. This does not seem like an appropriate situation to use interrupts. You could simply read the interrupt pin each time in loop(). But if you do use interrupts, you should keep them as short as possible and not use long complex functions like Serial.print(). Just set a flag, increment a variable, that kind of thing.

have look at this example of using alarm interrupts with a DS3231 RTC

// Date and time functions using a DS3231 RTC connected via I2C and Wire lib
//
// setting alarm 1 to go off 1 minute and alarm 2 3 mins after startup 

// uses https://github.com/adafruit/RTClib

// define interrupt pin (UNO only allows pins 2 and 3)
#define CLOCK_INTERRUPT_PIN 2   // pins 2 and 3 on UNO

#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

volatile bool alarm1GoneOFF=false;    // set when alarm1 goes off by interrupt routine
// interrupt routine called when external interrupt occurs
void onAlarm() {
    // if(rtc.alarmFired(1))    // >>>> calls to rtc in interrupt routine hang the processor !!!!
         alarm1GoneOFF=true;    // indicate alarm has gone off
}

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

// print RTC time
void printRTC(const DateTime t) {
        Serial.print(t.year(), DEC);
        Serial.print('/');
        Serial.print(t.month(), DEC);
        Serial.print('/');
        Serial.print(t.day(), DEC);
        Serial.print(" (");
        Serial.print(daysOfTheWeek[t.dayOfTheWeek()]);
        Serial.print(") ");
        Serial.print(t.hour(), DEC);
        Serial.print(':');
        Serial.print(t.minute(), DEC);
        Serial.print(':');
        Serial.print(t.second(), DEC);
        Serial.println();
}

void setup () {
#ifndef ESP8266
  while (!Serial); // for Leonardo/Micro/Zero
#endif
  Serial.begin(115200);
  delay(3000); // wait for console opening
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
    DateTime now = rtc.now();
    rtc.clearAlarm(1);   // clean any alarm condition
    // set the alarm to go off in 1 minute time (to minute and seconds)
    DateTime alarm1(now.year(), now.month(), now.day(), now.hour(),  now.minute()+1, now.second());
    rtc.setAlarm1(alarm1,  DS3231_A1_Minute);   // when minutes and seconds match!!
    Serial.print("alarm 1 will go off at ");
    printRTC(alarm1);
    rtc.clearAlarm(1);   // clean any alarm condition
    // alarm 2 fires in 3 mins time (to minute)
    DateTime alarm2(now.year(), now.month(), now.day(), now.hour(),  now.minute()+3, now.second());
    Serial.print("alarm 2 will go off at ");
    printRTC(alarm2);
    rtc.setAlarm2(alarm2,  DS3231_A2_Minute);   // when minutes match!!
    Ds3231SqwPinMode(DS1307_OFF);               // switch off square wave to get INT signal        
    // setup CLOCK_INTERRUPT_PIN and enable the interrupt
    pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, FALLING);
}

unsigned long int startTime=millis();
void loop () {
    // if 10 second delay elapsed or an alarm has gone off print information
    if((millis()-startTime>10000) || alarm1GoneOFF)   {
        startTime=millis();
        // debugging - display interrupt pin state
        //Serial.print("pin 2 "); Serial.println(digitalRead(CLOCK_INTERRUPT_PIN)); 
        // if alarm interrpt occured clear A1 and display message
        if(alarm1GoneOFF)
          if(rtc.alarmFired(1))
             { rtc.clearAlarm(1); alarm1GoneOFF=false;  Serial.print("\n!!! alarm 1 fired !!!!  "); }
          else
              if(rtc.alarmFired(2))
                 { rtc.clearAlarm(2); alarm1GoneOFF=false;  Serial.print("\n!!! alarm 2 fired !!!! "); }
        DateTime now = rtc.now();   
        printRTC(now);
        }
}

a run gives

alarm 1 will go off at 2020/9/3 (Thursday) 13:29:0
alarm 2 will go off at 2020/9/3 (Thursday) 13:31:0
2020/9/3 (Thursday) 13:28:7
2020/9/3 (Thursday) 13:28:17
2020/9/3 (Thursday) 13:28:27
2020/9/3 (Thursday) 13:28:37
2020/9/3 (Thursday) 13:28:47
2020/9/3 (Thursday) 13:28:57

!!! alarm 1 fired !!!!  2020/9/3 (Thursday) 13:29:0
2020/9/3 (Thursday) 13:29:9
2020/9/3 (Thursday) 13:29:19
2020/9/3 (Thursday) 13:29:30
2020/9/3 (Thursday) 13:29:39
2020/9/3 (Thursday) 13:29:49
2020/9/3 (Thursday) 13:29:59
2020/9/3 (Thursday) 13:30:9
2020/9/3 (Thursday) 13:30:19
2020/9/3 (Thursday) 13:30:29
2020/9/3 (Thursday) 13:30:39
2020/9/3 (Thursday) 13:30:49
2020/9/3 (Thursday) 13:30:59

!!! alarm 2 fired !!!! 2020/9/3 (Thursday) 13:31:0
2020/9/3 (Thursday) 13:31:9
2020/9/3 (Thursday) 13:31:19
2020/9/3 (Thursday) 13:31:29
2020/9/3 (Thursday) 13:31:39

note that when setting an alarm DS3231_A1_Minute and DS3231_A2_Minute match different things

Yes, i did come to realize that the function operateDispositive is being called, but it seems the only thing happening is the Serial.println.
I did remove the Serial.println from the function onAlarm.
Why you say that interrupt is not appropriate for this situation?

Note: i'm not at home, i'll post the code later.

Thanks @horace for your code explanation. This seems to be what i'm looking for.
I'll test it later and let you now how it goes.

I also noted that calling rtc methods in the interrupt routine could crash the system, e.g. see commented out call in

void onAlarm() {
    // if(rtc.alarmFired(1))    // >>>> calls to rtc in interrupt routine hang the processor !!!!
         alarm1GoneOFF=true;    // indicate alarm has gone off
}

I was calling rtc methods in in loop() and if in the middle of such a call an interrupt occured program corruption was likly unless the methods were reentrant

My understanding is that interrupts add unnecessary complexity to a code if there is any way to avoid them. They upset timing and can create spiders webs of code. Your situation does not require them so implementing an unnecessary complication which could have unpredictable (to you) results is probably a bad idea.

horace:
I also noted that calling rtc methods in the interrupt routine could crash the system, e.g. see commented out call in

void onAlarm() {

// if(rtc.alarmFired(1))    // >>>> calls to rtc in interrupt routine hang the processor !!!!
        alarm1GoneOFF=true;    // indicate alarm has gone off
}



I was calling rtc methods in in loop() and if in the middle of such a call an interrupt occured program corruption was likly unless the methods were reentrant

i did notice that it rebooted the system when trying to execute other actions after the interrupt happen. i'll test it with your suggestions

pmagowan:
My understanding is that interrupts add unnecessary complexity to a code if there is any way to avoid them. They upset timing and can create spiders webs of code. Your situation does not require them so implementing an unnecessary complication which could have unpredictable (to you) results is probably a bad idea.

this might be, but as i stated before i never used an interrupt before, and (with all respect, please don't get me wrong) if i don't use it i can't learn about it.
only by mistake, by attempt, by research, by gathering opinions, by talking about it ... i can learn about it, and many other things in the process
so i need to make it works with interrupt, and then i will tested without the interrupt and verify which is better

what Arduino are you using?

I use interrupts all the time in real time control systems, e.g.

  1. when timer events happen at set intervals, says evey 20mSecs acquiring data from sensors
  2. responding to events such as levels changing, switches operating, etc

the main program control loop could be busy talking to a mobile phone via Bluetooth or to a remote database using Lora or a GSM TCP/IP stack

horace:
what Arduino are you using?

I use interrupts all the time in real time control systems, e.g.

  1. when timer events happen at set intervals, says evey 20mSecs acquiring data from sensors
  2. responding to events such as levels changing, switches operating, etc

the main program control loop could be busy talking to a mobile phone via Bluetooth or to a remote database using Lora or a GSM TCP/IP stack

i just tried the code with your modifications, and it works beautifully
both the alarms are triggered, no crash, just awesome
thanks for the alert on the constants, just now i realize that DS3231_A1_Hour and DS3231_A2_Hour (and the others) are slightly different form each other

thanks to all of you folks

I guess I would answer that if you can do something reasonably easily without using interrupts, you should not use interrupts. The same is true for many hardware features of chips, such as timers, direct port manipulation and so on. In a situation where you think you have to use them, you could spend your time learning how to use them or you could spend that time learning how to write your code in a more efficient way so you don't need to use them. The latter is a skill you can use to your benefit on the smallest MCU to massive spark-clusters.

I think interrupts are the next best thing since sliced bread.

If you want to learn about interrupts, it's much more illustrative to use them for something that actually requires them.

They are limited in number on the AVR platform. So it is possible to "use them up" as your code undergoes revisions and additions. That is a good reason to leave them alone unless you need them.

There is memory and execution time overhead associated with an interrupt. Both of those are limited on the AVR platform. Another reason.

Interrupts are a "mine field" of problems with opaque behaviour that can easily baffle a beginner. There are a lot of fine details that are mostly new to someone who hasn't written for it before, any one of which will cause a failure. Each of them requires the acquisition of new concepts with subtle ramifications, like the 'volatile' keyword and atomic accesses. Most users do not have any interactive debug capability, and have to rely on being sure of the code, and being able to interpret the problem symptoms. That is a reason why beginners should only use them if necessary.

There are some things interrupts don't do well, like debouncing switches (because an event lockout is impractical). That is a reason to not use them just to debounce some user switch.

Interrupts "interrupt". Well, that's obvious. But some code can not be interrupted without failing (like NeoPixel drive for example). Now you have to disable and enable interrupts a lot in your main sketch. Another reason.

Hammers are amazing, but you should use a hammer to pound nails, a wrench to tighten bolts. Every tool has a general use that it was designed for. Using it for other things, is asking for trouble.

nunocv:
i just tried the code with your modifications, and it works beautifully
both the alarms are triggered, no crash, just awesome

Indeed it does, but the interrupt is entirely unnecessary. :roll_eyes:

The reason your original code failed was that you attempted to do completely inappropriate things (writing to the terminal) in the interrupt routine.

Since the interrupt does not go away until you clear it, instead of using the flag “alarm1GoneOFF” and testing it in the main loop, all you need to do is to test the interrupt pin itself - in the main loop.


As a beginner, it is incredibly unlikely that interrupts will be useful to you.

A common “newbie” misunderstanding is that an interrupt is a mechanism for altering the flow of a program - to execute an alternate function. Nothing could be further from the truth! :astonished:

An interrupt is a mechanism for performing an action which can be executed in “no time at all” with an urgency that it must be performed immediately or else data - information - will be lost or some harm will occur. It then returns to the main task without disturbing that task in any way though the main task may well check at the appropriate point for a “flag” set by the interrupt.

Now these criteria are in a microprocessor time scale - microseconds. This must not be confused with a human time scale of tens or hundreds of milliseconds or indeed, a couple of seconds. A switch operation is in this latter category and even a mechanical operation perhaps several milliseconds; the period of a 6000 RPM shaft rotation is ten milliseconds. Sending messages to a video terminal is clearly in no way urgent,

Unless it is a very complex procedure, you would expect the loop() to cycle many times per millisecond. If it does not, there is most likely an error in code planning; while the delay() function is provided for testing purposes, its action goes strictly against effective programming methods. The loop() will be successively testing a number of contingencies as to whether each requires action, only one of which may be whether a particular timing criteria has expired. Unless an action must be executed in the order of microseconds, it will be handled in the loop().

So what sort of actions do require such immediate attention? Well, generally those which result from the computer hardware itself, such as high speed transfer of data in UARTs(, USARTs) or disk controllers.

An alternate use of interrupts, for context switching in RTOSs, is rarely relevant to this category of microprocessors as it is more efficient to write cooperative code as described above.

as previous posts have indicated you only use interrupts when required by the application
in many none time critical applications polling in the main program loop() for events is applicable
for example, the program of post #4 only sets the volatile variable alarm1GoneOFF in the interrupt service routine
it is much simpler to call the alarmFired() method in loop() to see if an alarm has fired
e.g. this program gives similar results to post #4 but without using interrupts

// Date and time functions using a DS3231 RTC connected via I2C and Wire lib
//
// setting alarm 1 to go off 1 minute and alarm 2 3 mins after startup 

// uses https://github.com/adafruit/RTClib

#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;


char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

// print RTC time
void printRTC(const DateTime t) {
        Serial.print(t.year(), DEC);
        Serial.print('/');
        Serial.print(t.month(), DEC);
        Serial.print('/');
        Serial.print(t.day(), DEC);
        Serial.print(" (");
        Serial.print(daysOfTheWeek[t.dayOfTheWeek()]);
        Serial.print(") ");
        Serial.print(t.hour(), DEC);
        Serial.print(':');
        Serial.print(t.minute(), DEC);
        Serial.print(':');
        Serial.print(t.second(), DEC);
        Serial.println();
}

void setup () {
#ifndef ESP8266
  while (!Serial); // for Leonardo/Micro/Zero
#endif
  Serial.begin(115200);
  delay(3000); // wait for console opening
  Serial.println("DS3231 RTC time and alarm test");
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
    DateTime now = rtc.now();
    rtc.clearAlarm(1);   // clean any alarm condition
    // set the alarm to go off in 1 minute time (to minute and seconds)
    DateTime alarm1(now.year(), now.month(), now.day(), now.hour(),  now.minute()+1, now.second());
    rtc.setAlarm1(alarm1,  DS3231_A1_Minute);   // when minutes and seconds match!!
    Serial.print("alarm 1 will go off at ");
    printRTC(alarm1);
    rtc.clearAlarm(1);   // clean any alarm condition
    // alarm 2 fires in 3 mins time (to minute)
    DateTime alarm2(now.year(), now.month(), now.day(), now.hour(),  now.minute()+3, 0);
    Serial.print("alarm 2 will go off at ");
    printRTC(alarm2);
    rtc.setAlarm2(alarm2,  DS3231_A2_Minute);   // when minutes match!!
}

unsigned long int startTime=millis();
void loop () {
    // if 10 second delay elapsed or an alarm has gone off print information
    if((millis()-startTime>10000) ||rtc.alarmFired(1) || rtc.alarmFired(2))   {
        startTime=millis();
        // check for alarms and display message
        if(rtc.alarmFired(1))
             { rtc.clearAlarm(1);Serial.print("\n!!! alarm 1 fired !!!!  "); }
          else
              if(rtc.alarmFired(2))
                 { rtc.clearAlarm(2); Serial.print("\n!!! alarm 2 fired !!!! "); }
        DateTime now = rtc.now();   
        printRTC(now);
        }
}

a run gives

DS3231 RTC time and alarm test
alarm 1 will go off at 2020/9/4 (Friday) 5:51:14
alarm 2 will go off at 2020/9/4 (Friday) 5:53:0
2020/9/4 (Friday) 5:50:21
2020/9/4 (Friday) 5:50:31
2020/9/4 (Friday) 5:50:41
2020/9/4 (Friday) 5:50:51
2020/9/4 (Friday) 5:51:1
2020/9/4 (Friday) 5:51:11

!!! alarm 1 fired !!!!  2020/9/4 (Friday) 5:51:14
2020/9/4 (Friday) 5:51:24
2020/9/4 (Friday) 5:51:34
2020/9/4 (Friday) 5:51:44
2020/9/4 (Friday) 5:51:54
2020/9/4 (Friday) 5:52:4
2020/9/4 (Friday) 5:52:14
2020/9/4 (Friday) 5:52:24
2020/9/4 (Friday) 5:52:34
2020/9/4 (Friday) 5:52:44
2020/9/4 (Friday) 5:52:54

!!! alarm 2 fired !!!! 2020/9/4 (Friday) 5:53:0
2020/9/4 (Friday) 5:53:10
2020/9/4 (Friday) 5:53:20

however, it depends what you are doing in the main program loop()
loop() in the above program and that of post #4 does nothing except poll for timeouts and alarmFired() indicating an alarm has gone off
if in addition to polling for events the mian loop is doing complex possibly time consuming tasks, e.g. communicating over TCP/IP to a remote server, it is possible that a time critical event may be missed. In such an application the event would be serviced using an interrupt.

The point here is that the alarm interrupt is neither time critical nor evanescent.

Thanks @horace, i will try it with only the alarmFired function (without the interrupt).
i forgot to answer you before, i'm working on the ESP32

Paul__B:
evanescent

Good word use!