Using the DS3231 Real Time Clock alarm with the Adafruit RTClib library

I love the DS3231 RTC module and have used it in a couple of projects without a library using code based on Ralph Bacon’s example. It worked well, but I wanted to use the alarm and working out how to do that was a step too far for me. There doesn’t seem to be a shortage of libraries and I found it challenging to choose one.

I tried the Adafruit RTClib library which I liked but the documentation I looked at didn’t mention support for the alarm, but I found this closed issue in the libraries Github repository Add support for Alarm on DS3231 #94, so it seems it does have support for it. After many hours of twiddling and tweaking here are some notes and example from my experiments of what I believe is correct info. I’m not an expert so let me know if you believe any of it is incorrect or can be improved.

Setting the alarm

This is what I had most trouble with. There are two alarms. The commands to set the alarms time are:
setAlarm1(DateTime, alarm_mode)
setAlarm2(DateTime, alarm_mode)

The alarms can be set to a specific time using DateTime or to a set amount from the current time using TimeSpan. Some formats I used are:

For a specific time use DateTime:
DateTime(Year, Month, Day, Hour, Minute, Second)

For a number of seconds, minutes, hours, etc from the current time using TimeSpan:
now + TimeSpan(days, hours, minutes, seconds)

Using unixtime:
now.unixtime() + seconds

The second part of setAlarm is alarm_mode. This sets what needs to match for the alarm to trigger. The modes are:

Alarm modes for alarm 1
DS3231_A1_PerSecond     Once per second
DS3231_A1_Second        When seconds match
DS3231_A1_Minute        When minutes and seconds match
DS3231_A1_Hour          When hours, minutes, and seconds match
DS3231_A1_Date          When date, hours, minutes, and seconds match (day of month)
DS3231_A1_Day           When day, hours, minutes, and seconds match (day of week)

Alarm modes for alarm 2
DS3231_A2_PerMinute     Once per minute (00 seconds of every minute)
DS3231_A2_Minute        When minutes and seconds match
DS3231_A2_Hour          When hours, minutes, and seconds match
DS3231_A2_Date          When date, hours, minutes, and seconds
match
DS3231_A2_Day           When day, hours, minutes, and seconds match

Alarm 2 is slightly different in that it can’t be set to trigger on anything smaller other than 00 seconds, that is triggering on the minute is the smallest amount.

The trick for me was working out what how the relationship between time and mode. If a suitable mode is not chosen it can (and did) result in unexpected results. For example setting the time for 7:00am the next day, but choosing DS3231_A1_Minute will result in the alarm triggering on the next hour after setting as only minutes and seconds are being used in the mode. Hours and days are ignored.

Examples

Set to an explicit time

The alarm will trigger when seconds, minutes and hours match, resulting in triggering once per day, every day at (15:34:00). The date, month and year are all ignored.
rtc.setAlarm1(DateTime(2020, 6, 25, 15, 34, 0), DS3231_A1_Hour)

When seconds at zero, that is every minute
rtc.setAlarm1(DateTime(0, 0, 0, 0, 0, 0), DS3231_A1_Second)

When the minutes and seconds are at zero, that is, every hour
rtc.setAlarm1(DateTime(0, 0, 0, 0, 0, 0), DS3231_A1_Minute)

When the minutes are at 1 and seconds are at 10, that is 1 minute and 10 seconds past the hour
rtc.setAlarm1(DateTime(0, 0, 0, 0, 1, 10), DS3231_A1_Minute)

At 10:00 o’clock every day
rtc.setAlarm1(DateTime(0, 0, 0, 10, 0, 0), DS3231_A1_Hour)

At 10:18 every day
rtc.setAlarm1(DateTime(0, 0, 0, 10, 18, 0), DS3231_A1_Hour)

Using TimeSpan or unixtime to set to an amount from the current time

The current time needs to be called before setting the remaining examples as it is needed to calculate the end time. These examples set the alarm to 10 seconds from the current time.
rtc.setAlarm1(setAlarm1(rtc.now + TimeSpan(0, 0, 0, 10), DS3231_A1_Second)
rtc.setAlarm1(rtc.now() + TimeSpan(10), DS3231_A1_Second)
rtc.setAlarm1(rtc.now.unixtime() + 10, DS3231_A1_Second)

In 1 minutes time from now
rtc.setAlarm1(rtc.now() + TimeSpan(0, 0, 1, 0), DS3231_A1_Minute)

In 2 minutes and 10 seconds time from now
rtc.setAlarm1(rtc.now() + TimeSpan(0, 0, 2, 10), DS3231_A1_Minute)

In 1 hour and 5 minutes time from now
rtc.setAlarm1(rtc.now() + TimeSpan(0, 1, 5, 0), DS3231_A1_Hour)

Monitoring the alarm

So now it can be set, how do we know it has activated? There seems to be two ways to do this.

Using alarmFired

alarmFired() will return true if the alarm is firing and will continue to do so for at least some minutes(?) unless it is cleared. However, while the alarm can be cleared, it doesn’t appear that it can be disabled, so it will fire again when the alarm setting criteria is met again. So that needs to be considered in code. Also, I could only see how to do this by polling alarmFired, which if true looks like this method rules it out for use with sleep modes that I want to use.

Using the SQW pin

This SQW pin can be used to output square waves. It can also be used by the alarm. Its output goes low when the alarm is triggered. This opens up the possibility of using it for use with an interrupt so can be used to wake an Arduino from sleep.

Update 9 July 2022

Since I wrote this post later versions of RTClib have included an example sketch for the alarm. That sketch is more sophisticated than my examples so you should check it out.

I have added some more examples above for formatting setting the alarm time.

I have also added another sketch to my Github repository DS3231-_RTClib-Adafruit-Alarm-SetTest. It is based on the example that now comes with the library. I made it just so that I could test setAlarm times. It monitors the SQW pin and simply displays the current time and then when the alarm is triggered it displays the time it was triggered.

Examples

My Github repository has a few examples:

Setting an alarm and polling the SQW pin – DS3231-RTClib-Adafruit-Alarm-Poll-SQW.ino https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/blob/master/DS3231-RTClib-Adafruit-Alarm-Poll-SQW/DS3231-RTClib-Adafruit-Alarm-Poll-SQW.ino

Setting an alarm and polling using alarmFired – DS3231-RTClib-Adafruit-Alarm-Poll-alarmFired.ino https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/blob/master/DS3231-RTClib-Adafruit-Alarm-Poll-alarmFired/DS3231-RTClib-Adafruit-Alarm-Poll-alarmFired.ino

Sending the Arduino to sleep and waking it using the alarm and SQW pin – DS3231-_RTClib-Adafruit-Alarm-Sleep.ino https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/blob/master/DS3231-_RTClib-Adafruit-Alarm-Sleep/DS3231-_RTClib-Adafruit-Alarm-Sleep.ino

I hope this is useful and doesn’t have too much incorrect info. Let me know your feedback.

15 thoughts on “Using the DS3231 Real Time Clock alarm with the Adafruit RTClib library

Add yours

  1. Thank you! I was in a similar place (trying to trigger events on a schedule) and wasn’t having much luck with other documentation I found. This worked perfectly. I appreciate you posting this.

    Like

  2. Garry, I really appreciate your detailed blog post with such useful examples. I was having a tough time figuring out how to use DS3231 interrupts/alarms and the RTClib with an ATMega MCU. The sleep mode info was a bonus! Thank you.

    Like

    1. Hi Clem, That’s a good question. It’s been a couple of years since this post and I have not used an alarm with the DS3231 in that time. I confess to having a bit of trouble working that out from my own post. I also found there is now an example sketch in the library. So, I’ve updated this blog post above. In short for minutes here are a couple of examples:

      rtc.setAlarm1(rtc.now() + TimeSpan(0, 0, 1, 0), DS3231_A1_Minute) // In 1 minute from now
      rtc.setAlarm1(rtc.now() + TimeSpan(0, 0, 2, 10), DS3231_A1_Minute) // In 2 minutes and 10 seconds time from now

      I’ve added another test sketch here https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/tree/master/DS3231-_RTClib-Adafruit-Alarm-SetTest that may help with testing setting alarms

      Like

  3. Hi Garry, thanks for putting this post together — it’s great!! A question: have you found that when the alarm time you’re setting with now() + TimeSpan is an integer multiple of 60 seconds (whether it’s formatted as TimeSpan(60) or TimeSpan(0,0,1,0)) that the alarm starts firing immediately without waiting the appropriate TimeSpan value? I’ve encountered this, and the work-around I’ve used has been to set a TimeSpan(59) or TimeSpan(61) instead. It’s like there’s a timewheel which only has 60 slots on it, and if the TimeSpan figure you’re using lines up exactly on the :00 timeslot, then the alarm will fire immediately rather than waiting the desired period.

    Liked by 1 person

    1. Hi Mike, I haven’t had the it fire immediately. Are you checking the alarm by checking the state of the SQW pin? After reading your comment I tried doing a few more tests. I’m testing using a recent test sketch I did just for setting alarm tests here https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/blob/master/DS3231-_RTClib-Adafruit-Alarm-SetTest/DS3231-_RTClib-Adafruit-Alarm-SetTest.ino, but I couldn’t reproduce that.

      One thing I just learned is that the alarm will continue to fire at regular intervals but not necessarily when you expect. For example using rtc.now() + TimeSpan(10), DS3231_A1_Second will result in the alarm triggering in 10 seconds from the current time. It will then trigger every minute after that when the seconds match the 10 seconds from now. So in 10 seconds, then every 60 seconds.

      A similar thing happens with the other timespan method. I’m just a hobbyist and made this post because I couldn’t find other info, but there is still more to learn.

      I’ll do some more tests and go through the post again soon and make some more updates. One thing not mention is rtc.disableAlarm(1) that can be used to disable the alarm.

      Like

    1. Hi Philip

      I hope someone else can help here because as there is DS3231_A1_Day it seems it should be possible, but I have not been able to any info or get it to work. The only workaround I have is to set it to for a set time of the day, check the day of week when it fires and only do your processing if it matches the day you want.

      For example set it to off at 8:00am using
      DateTime(0, 0, 0, 8, 0, 0), DS3231_A1_Hour

      And then in the code that runs when the alarm is triggered have something like this:

      
      DateTime now = rtc.now();
      // Check day of the week - Sunday = 0, Saturday = 6
      if (now.dayOfTheWeek() == 6) {
        // The intended day of the week
        // Do things...
        
        //Clear alarm
        rtc.clearAlarm(1);
        // Disable alarm if it is not recurring
        rtc.disableAlarm(1);
      } else {
        // Other weekdays
        // Clear the alarm but don't disable it
        rtc.clearAlarm(1);
      }
      

      Liked by 1 person

      1. Hi, If you want to set up a weekly alarm I found help on the Arduino forms. If you wanted it to go off on a Saturday use.

        rtc.setAlarm1(DateTime(2022, 7, 23, 10, 0, 0), DS3231_A1_Day);

        That makes it go off every week at 10.00 on a Saturday.

        The 23rd was a Saturday but you can put any Saturdays date in there and it fires every week without having to change the date.

        Like

  4. wonderful work ! Im an IT Engineer , and struggeled for hours to find a good documented and working example, this worked like charm, you should be proud of your self!

    Like

  5. Question – Since both alarms use the SQW pin, how does the Arduino distinguish between them if you want to set different digital inputs for each alarm? Is there a code we can use to make that distinction? I’ve read about monitoring how many times the Interrupt is used, but confusing. Can you give an example?

    Like

    1. That’s a good question and I don’t really have an answer for that. I had a quick look at the FTClib and I couldn’t see anything that may help, but I may have missed something. If I was going to do that with the code I use I would try storing the values for the alarm in variables when the alarms are set and when the alarm fires compare the current time with the values of the alarm time. A little bit clunky bout may work.

      Like

      1. Thank you for your response. I will continue working on this.
        Too many sites either abandon the webpage or ignore the comments. It’s refreshing to see someone with the integrity and devotion to their posts.

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Website Powered by WordPress.com.

Up ↑

%d bloggers like this: