DS3231 with 2 alarms used as interrupts

Hello,

I m trying to found my way with the DS3231 RTC module: have read a lot of stuff on it and got a problem: I cannot manage to get both alarms work as interrupt to wake up my UNO twice a day (that is the goal)
if alarm1 and alarm2 are both activated, the interrupt occurs only when alarm2 trigger . if only one alarm is active it is working (even if it is alarm1 or alarm2). Both alarms are working fine and got the trigger flag when not in “interrupt mode”.
My DS3231 module is a usual one ds3231 module with led and the 4 resistor block removed as exposed here: DS3231 modifications .

I m using the library from Rodan : DS3231 library by Rodan and this is my code (interrupt on pin 3 of my UNO)

It drives me crazy… where I m wrong?
thx for your help!

#include "ds3231.h"
#include <Wire.h>
#include <LowPower.h>

#define BUFF_MAX 128
#define WAKEUPALARM   3 //2d wake up pin!!!!!!

struct ts t;

unsigned long SleepTime, previousSleepTime;

byte y, mth, d, h, m, s; 


byte  i, alarm1_sec,
      alarm1_minute, alarm1_hour,
      control_reg, status_reg,
      alarm2_minute, alarm2_hour;

volatile bool flagAlarm = false;

void setup() {

  pinMode (WAKEUPALARM, INPUT_PULLUP); //wake up pin 1 -> alarms
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600);
  Wire.begin();
  // DS3231_init(DS3231_INTCN);
  /*----------------------------------------------------------------------------
    In order to synchronise your clock module, insert timetable values below !
    ----------------------------------------------------------------------------*/
//    t.hour = 15;
//    t.min = 39;
//    t.sec = 0;
//    t.mday = 19;
//    t.mon = 03;
//    t.year = 2021;
//  
//     DS3231_set(t);

  //********************************Alarm *********************

  // flags define what calendar component to be checked against the current time in order
  // to trigger the alarm - see datasheet
  // A1M1 (seconds) (0 to enable, 1 to disable)
  // A1M2 (minutes) (0 to enable, 1 to disable)
  // A1M3 (hour)    (0 to enable, 1 to disable)
  // A1M4 (day)     (0 to enable, 1 to disable)
  // DY/DT          (dayofweek == 1/dayofmonth == 0)
  uint8_t flags[5] = { 0, 0, 0, 1, 1 }; 
   uint8_t flagsA2[4] = { 0, 0, 1, 1 }; 


  // set Alarm1&2
  DS3231_set_a1(1, 15, 16, 0, flags); //(s, m,h,d,flag)
  DS3231_set_a2(13, 16, 0, flagsA2); //( m,h,d,flag)

  // activate Alarm1
  DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A1IE);

  // activate Alarm2
  DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A2IE);


  previousSleepTime = millis();
  SleepTime = millis();
  digitalWrite(LED_BUILTIN, HIGH);
  Serial.println("start");
  delay(10);

DS3231_clear_a1f();
DS3231_clear_a2f();


}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  if ((SleepTime - previousSleepTime) > 20000) { 
      // Allow wake up pin to trigger interrupt on low.
          digitalWrite(LED_BUILTIN, LOW);
      Serial.println("sleeping test .....");
      delay(10);
      attachInterrupt(1, wakeUp, LOW); // alarm on pin3 , FALLING or LOW ?
  
  
  
      // Enter power down state with ADC and BOD module disabled.
      // Wake up when wake up pin is low.
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  
      // Disable external pin interrupt on wake up pin.
  //HERE AFTER WAKING UP
      detachInterrupt(1);
      Serial.print("reveil!!!");
      previousSleepTime = millis();
  
    }

  digitalWrite(LED_BUILTIN, HIGH);

  DS3231_get(&t);

  char buff[BUFF_MAX];

  h = t.hour;
  m = t.min;
  s = t.sec;



  Serial.print("\t Hour : ");
  Serial.print(h);
  Serial.print(":");
  Serial.print(m);
  Serial.print(".");
  Serial.println(s);

  delay(2000);

  // display a1 debug info
  //  DS3231_get_a1(&buff[0], 59);
  //  Serial.println(buff);

  // alarms_read_display();


  if (DS3231_triggered_a1()) {
    // INT has been pulled low
    Serial.println(" -> alarm1 has been triggered");
    Serial.println(String(flagAlarm));
  
    DS3231_clear_a1f();
  }

  if (DS3231_triggered_a2()) {
    // INT has been pulled low
    Serial.println(" -> alarm2 has been triggered and I do the stuff!!");
    DS3231_clear_a2f();
  }


  delay(1000);
  SleepTime = millis();
}

void alarms_read_display() {                    // Function to read and display alarm1, alarm2 and temperature data

  Wire.beginTransmission(0x68);                 // Start I2C protocol with DS3231 address
  Wire.write(0x07);                             // Send register address (0x07) for second (0x08) for starting at minute
  Wire.endTransmission(false);                  // I2C restart
  Wire.requestFrom(0x68, 4);                   // Request 4 bytes (corresponding to Alarm1) from DS3231 and release I2C bus at end of reading
  alarm1_sec = Wire.read();
  alarm1_minute = Wire.read();                  // Read alarm1 minutes
  alarm1_hour   = Wire.read();                  // Read alarm1 hours
  Wire.read();                                  // Skip alarm1 day/date register

  Wire.beginTransmission(0x68);                 // Start I2C protocol with DS3231 address
  Wire.write(0x0B);                             // Send register address (0x07) for second (0x08) for starting at minute
  Wire.endTransmission(false);                  // I2C restart
  Wire.requestFrom(0x68, 3);

  alarm2_minute = Wire.read();                  // Read alarm2 minutes
  alarm2_hour   = Wire.read();                  // Read alarm2 hours
  Wire.read();                                  // Skip alarm2 day/date register
  //  control_reg = Wire.read();                    // Read the DS3231 control register
  //  status_reg  = Wire.read();                    // Read the DS3231 status register
  //  Wire.read();                                  // Skip aging offset register
  //  temperature_msb = Wire.read();                // Read temperature MSB
  //  temperature_lsb = Wire.read();                // Read temperature LSB

  // Convert BCD to decimal
  alarm1_sec = ((alarm1_sec / 16 * 10) + (alarm1_sec % 16));
  alarm1_minute = ((alarm1_minute / 16 * 10) + (alarm1_minute % 16));
  alarm1_hour = ((alarm1_hour / 16 * 10) + (alarm1_hour % 16));

  alarm2_minute = ((alarm2_minute / 16 * 10) + (alarm2_minute % 16));
  alarm2_hour = ((alarm2_hour / 16 * 10) + (alarm2_hour % 16));


  Serial.print("alarm1 sec: ");

  Serial.println(alarm1_sec, DEC);

  Serial.print("alarm minute ");
  Serial.println(alarm2_minute);

  Serial.print("alarm2 hours ");
  Serial.println(alarm2_hour);



}

void wakeUp()
{
  // Just a handler for the pin interrupt.

  flagAlarm = true; 

}
    attachInterrupt(1, wakeUp, LOW); // alarm on pin3 , FALLING or LOW ?

Assuming that the DS3231 takes the pin LOW when an alarm occurs this would be better as FALLING otherwise the interrupt may trigger more than once. How long does the DS3131 pin stay LOW when an alarm is triggered ?

Hi,
I did try with LOW rather than FALLING but didn't help much.
I had try to capture the LOW status from the SQW output using a multimeter, but wasn't succesfull was at 5V all the time (maybe it is so quick that it cannot notice it?)

I did try with LOW rather than FALLING but didn't help much.

Do you mean that yo tried it with FALLING ?

Write some small test sketches

Try incrementing a counter every time the interrupt is triggered so you can tell how many times it occurs. Using FALLING as a trigger should only be counted once, whilst using LOW will almost certainly cause the interrupt to trigger multiple times

If using the "interrupt" pin of the DS3231 is causing so much grief you might just as well not use it and compare the current time with your alarm times yourself

Hi, thx for your answer.
Yes I did try FALLING but also LOW a lot of time and I got the same result: if alarm1 and alarm2 are activated, only alarm2 will wake up the arduino (independently if the alarm1 "drings" before or after the alarm2). If only one alarm is activated it wake up the arduino no problem (even if it is alarm1 that is activated).

Write some small test sketches

Try incrementing a counter every time the interrupt is triggered so you can tell how many times it occurs.

it is already a small sketch, no?? :wink: I m doing nothing more than setup 2 alarms, putting the arduino on sleep and display in the serial when they are awake ... + some time retrieved from the DS3231 to make sure it is operational...

Not using the interrupt is not really an option in a way that my goal is to put arduino on sleep for long time (->power consumption issues .... :slight_smile:

Not using the interrupt is not really an option in a way that my goal is to put arduino on sleep for long time (->power consumption issues ....

Understood.

If I get the chance I will hook up a DS3231 module and try it out for myself, but no promises

I did narrow the problem I believe: it is localized on the alarm activation control order:

so far I had :

// activate Alarm1
DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A1IE);
// activate Alarm2
DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A2IE);

and alarm2 was the one that was waking up the arduino from sleep (independently if alarm2 time was before or after alarm1):

now if I do :

// activate Alarm2
DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A2IE);
// activate Alarm1
DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A1IE);

Alarm1 is the one that wake up the arduino from sleep!

So it looks like the latest set_creg override the previous one .. and that both together are not working. but in the DS3231 datasheet they are on 2 different bits so normally they should independents, no? I believe there is something to dig there ....

If I get the chance I will hook up a DS3231 module and try it out for myself, but no promises

crossing fingers that you (or even some other) will have some time to try it :slight_smile: ... at least just to understand this activation order problem!

What you have discovered certainly does not seem to match the datasheet

I cannot track down my DS3231 module at the moment, but a quick thought

Do you actually need 2 alarms ?
When alarm 1 triggers it could set alarm 2 and vice versa

Do you actually need 2 alarms ?
When alarm 1 triggers it could set alarm 2 and vice versa

indeed you right, that would be the work around I guess.... or alternate/override the alarms activations (more easy I guess?)
But I still would like to understand what I did miss in my code .... maybe somebodies has already encountered the problem and solved it, or is everybody able to get both alarm activated ready to fire interrupt without any problem?

I still would like to understand what I did miss in my code

Maybe nothing

One complication is the number of libraries available for the DS3231. I don't have the one that you are using, but I do have one with the same name, which means that installing both is more complicated and I still can't find my DS3231 in any case

If I were you I would go with the pragmatic approach and use the workaround

I GOT IT!! So for the ds3231.h Rodan's library as mentioned in my first post

The trick is in this part: instead of doing that:

  // activate Alarm2
  DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A2IE);

  // activate Alarm1
  DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A1IE );

just use this:

DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A1IE | DS3231_CONTROL_A2IE);

using this will trigger the alarm accordingly to the setup date (also just to note for people to be careful with the flags: (it found me ages to found out!
) if you do not setup a day, you do not need to say day of the month or day of the week, so only 4 entries for the A1 flag and 3 for the A2 flag!)

My setup will look like this

void setup() {

  pinMode (WAKEUPALARM, INPUT_PULLUP); //wake up pin 1 -> alarms
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600);
  Wire.begin();
  // DS3231_init(DS3231_INTCN);
  DS3231_init(DS3231_CONTROL_INTCN);
  /*----------------------------------------------------------------------------
    In order to synchronise your clock module, insert timetable values below !
    ----------------------------------------------------------------------------*/
  //    t.hour = 15;
  //    t.min = 39;
  //    t.sec = 0;
  //    t.mday = 19;
  //    t.mon = 03;
  //    t.year = 2021;
  //
  //     DS3231_set(t);

  //********************************Alarm *********************

  // flags define what calendar component to be checked against the current time in order
  // to trigger the alarm - see datasheet
  // A1M1 (seconds) (0 to enable, 1 to disable)
  // A1M2 (minutes) (0 to enable, 1 to disable)
  // A1M3 (hour)    (0 to enable, 1 to disable)
  // A1M4 (day)     (0 to enable, 1 to disable)
  // DY/DT          (dayofweek == 1/dayofmonth == 0) only if day is activated else remove from the flag!!
  uint8_t flags[4] = { 0, 0, 1, 1 }; //as day is not activated
  uint8_t flagsA2[3] = { 0, 1, 1 }; //as day is not activated


  // set Alarm1&2
  DS3231_set_a1(25, 25, 17, 0, flags); //(s, m,h,d,flag)
  DS3231_set_a2(22, 17, 0, flagsA2); //( m,h,d,flag)


//allow Alarm1 and 2 to do an interrupt on the same pin
DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A1IE | DS3231_CONTROL_A2IE);

 
 
  Serial.println("start");
  delay(10);

  DS3231_clear_a1f();
  DS3231_clear_a2f();

Hope that will help someone else!
lenoyl

Well done and I am sure that it will be helpful