From f5adeb7526500f47440cc73663733210f1ba3160 Mon Sep 17 00:00:00 2001 From: Justin Newitter Date: Tue, 8 Feb 2022 01:31:37 -0500 Subject: [PATCH 1/2] DS3231: Add support for fetching alarm values This commit introduces 4 new methods for DS3231 devices: - DateTime getAlarm1(); - DateTime getAlarm2(); - Ds3231Alarm1Mode getAlarm1Mode(); - Ds3231Alarm2Mode getAlarm2Mode(); The getAlarm methods assume that the alarms are stored using 24 hour format since this library doesn't support storing alarms in 12 hour format (DS3231 hardware supports this). Updated the DS3231_alarm example to fetch alarm1. Also improved the formatting of the serial logging. --- examples/DS3231_alarm/DS3231_alarm.ino | 31 +++++- src/RTC_DS3231.cpp | 129 +++++++++++++++++++++++++ src/RTClib.h | 4 + 3 files changed, 159 insertions(+), 5 deletions(-) diff --git a/examples/DS3231_alarm/DS3231_alarm.ino b/examples/DS3231_alarm/DS3231_alarm.ino index 5b77192a..903da7d1 100644 --- a/examples/DS3231_alarm/DS3231_alarm.ino +++ b/examples/DS3231_alarm/DS3231_alarm.ino @@ -65,12 +65,32 @@ void loop() { char date[10] = "hh:mm:ss"; rtc.now().toString(date); Serial.print(date); + + // the stored alarm value + mode + DateTime alarm1 = rtc.getAlarm1(); + Ds3231Alarm1Mode alarm1mode = rtc.getAlarm1Mode(); + char alarm1Date[12] = "DD hh:mm:ss"; + rtc.getAlarm1().toString(alarm1Date); + Serial.print(" [Alarm1: "); + Serial.print(alarm1Date); + Serial.print(", Mode: "); + switch (rtc.getAlarm1Mode()) { + case DS3231_A1_PerSecond: Serial.print("PerSecond"); break; + case DS3231_A1_Second: Serial.print("Second"); break; + case DS3231_A1_Minute: Serial.print("Minute"); break; + case DS3231_A1_Hour: Serial.print("Hour"); break; + case DS3231_A1_Date: Serial.print("Date"); break; + case DS3231_A1_Day: Serial.print("Day"); break; + } + // the value at SQW-Pin (because of pullup 1 means no alarm) - Serial.print(" SQW: "); + Serial.print("] SQW: "); Serial.print(digitalRead(CLOCK_INTERRUPT_PIN)); - // whether a alarm happened happened - Serial.print(" Alarm1: "); + + // whether a alarm fired + Serial.print(" Fired: "); Serial.print(rtc.alarmFired(1)); + // Serial.print(" Alarm2: "); // Serial.println(rtc.alarmFired(2)); // control register values (see https://datasheets.maximintegrated.com/en/ds/DS3231.pdf page 13) @@ -79,10 +99,11 @@ void loop() { // resetting SQW and alarm 1 flag // using setAlarm1, the next alarm could now be configurated - if(rtc.alarmFired(1)) { + if (rtc.alarmFired(1)) { rtc.clearAlarm(1); - Serial.println("Alarm cleared"); + Serial.print(" - Alarm cleared"); } + Serial.println(); delay(2000); } diff --git a/src/RTC_DS3231.cpp b/src/RTC_DS3231.cpp index 06f46dcc..f831b190 100644 --- a/src/RTC_DS3231.cpp +++ b/src/RTC_DS3231.cpp @@ -181,6 +181,135 @@ bool RTC_DS3231::setAlarm2(const DateTime &dt, Ds3231Alarm2Mode alarm_mode) { return true; } +/**************************************************************************/ +/*! + @brief Get the date/time value of Alarm1 + @return DateTime object with the Alarm1 data set in the + day, hour, minutes, and seconds fields +*/ +/**************************************************************************/ +DateTime RTC_DS3231::getAlarm1() { + uint8_t buffer[5] = {DS3231_ALARM1, 0, 0, 0, 0}; + i2c_dev->write_then_read(buffer, 1, buffer, 5); + + uint8_t seconds = bcd2bin(buffer[0] & 0x7F); + uint8_t minutes = bcd2bin(buffer[1] & 0x7F); + // Fetching the hour assumes 24 hour time (never 12) + // because this library exclusively stores the time + // in 24 hour format. Note that the DS3231 supports + // 12 hour storage, and sets bits to indicate the type + // that is stored. + uint8_t hour = bcd2bin(buffer[2] & 0x3F); + + // Determine if the alarm is set to fire based on the + // day of the week, or an explicit date match. + bool isDayOfWeek = (buffer[3] & 0x40) >> 6; + uint8_t day; + if (isDayOfWeek) { + // Alarm set to match on day of the week + day = bcd2bin(buffer[3] & 0x0F); + } else { + // Alarm set to match on day of the month + day = bcd2bin(buffer[3] & 0x3F); + } + + return DateTime(2000U, 1, day, hour, minutes, seconds); +} + +/**************************************************************************/ +/*! + @brief Get the date/time value of Alarm2 + @return DateTime object with the Alarm2 data set in the + day, hour, and minutes fields +*/ +/**************************************************************************/ +DateTime RTC_DS3231::getAlarm2() { + uint8_t buffer[4] = {DS3231_ALARM2, 0, 0, 0}; + i2c_dev->write_then_read(buffer, 1, buffer, 4); + + uint8_t minutes = bcd2bin(buffer[0] & 0x7F); + // Fetching the hour assumes 24 hour time (never 12) + // because this library exclusively stores the time + // in 24 hour format. Note that the DS3231 supports + // 12 hour storage, and sets bits to indicate the type + // that is stored. + uint8_t hour = bcd2bin(buffer[1] & 0x3F); + + // Determine if the alarm is set to fire based on the + // day of the week, or an explicit date match. + bool isDayOfWeek = (buffer[2] & 0x40) >> 6; + uint8_t day; + if (isDayOfWeek) { + // Alarm set to match on day of the week + day = bcd2bin(buffer[2] & 0x0F); + } else { + // Alarm set to match on day of the month + day = bcd2bin(buffer[2] & 0x3F); + } + + return DateTime(2000U, 1, day, hour, minutes, 0); +} + +/**************************************************************************/ +/*! + @brief Get the mode for Alarm1 + @return Ds3231Alarm1Mode enum value for the current Alarm1 mode +*/ +/**************************************************************************/ +Ds3231Alarm1Mode RTC_DS3231::getAlarm1Mode() { + uint8_t buffer[5] = {DS3231_ALARM1, 0, 0, 0, 0}; + i2c_dev->write_then_read(buffer, 1, buffer, 5); + + uint8_t alarm_mode = (buffer[0] & 0x80) >> 7 // A1M1 - Seconds bit + | (buffer[1] & 0x80) >> 6 // A1M2 - Minutes bit + | (buffer[2] & 0x80) >> 5 // A1M3 - Hour bit + | (buffer[3] & 0x80) >> 4 // A1M4 - Day/Date bit + | (buffer[3] & 0x40) >> 2; // DY_DT + + // Determine which mode the fetched alarm bits map to + switch (alarm_mode) { + case DS3231_A1_PerSecond: + case DS3231_A1_Second: + case DS3231_A1_Minute: + case DS3231_A1_Hour: + case DS3231_A1_Date: + case DS3231_A1_Day: + return (Ds3231Alarm1Mode)alarm_mode; + default: + // Default if the alarm mode cannot be read + return DS3231_A1_Date; + } +} + +/**************************************************************************/ +/*! + @brief Get the mode for Alarm2 + @return Ds3231Alarm2Mode enum value for the current Alarm2 mode +*/ +/**************************************************************************/ +Ds3231Alarm2Mode RTC_DS3231::getAlarm2Mode() { + uint8_t buffer[4] = {DS3231_ALARM2, 0, 0, 0}; + i2c_dev->write_then_read(buffer, 1, buffer, 4); + + uint8_t alarm_mode = (buffer[0] & 0x80) >> 7 // A2M2 - Minutes bit + | (buffer[1] & 0x80) >> 6 // A2M3 - Hour bit + | (buffer[2] & 0x80) >> 5 // A2M4 - Day/Date bit + | (buffer[2] & 0x40) >> 3; // DY_DT + + // Determine which mode the fetched alarm bits map to + switch (alarm_mode) { + case DS3231_A2_PerMinute: + case DS3231_A2_Minute: + case DS3231_A2_Hour: + case DS3231_A2_Date: + case DS3231_A2_Day: + return (Ds3231Alarm2Mode)alarm_mode; + default: + // Default if the alarm mode cannot be read + return DS3231_A2_Date; + } +} + /**************************************************************************/ /*! @brief Disable alarm diff --git a/src/RTClib.h b/src/RTClib.h index 380ada80..ad9bf8fb 100644 --- a/src/RTClib.h +++ b/src/RTClib.h @@ -377,6 +377,10 @@ class RTC_DS3231 : RTC_I2C { void writeSqwPinMode(Ds3231SqwPinMode mode); bool setAlarm1(const DateTime &dt, Ds3231Alarm1Mode alarm_mode); bool setAlarm2(const DateTime &dt, Ds3231Alarm2Mode alarm_mode); + DateTime getAlarm1(); + DateTime getAlarm2(); + Ds3231Alarm1Mode getAlarm1Mode(); + Ds3231Alarm2Mode getAlarm2Mode(); void disableAlarm(uint8_t alarm_num); void clearAlarm(uint8_t alarm_num); bool alarmFired(uint8_t alarm_num); From 25964685ca3b3ee16380adfc2b8d9758565cb14d Mon Sep 17 00:00:00 2001 From: Justin Newitter Date: Wed, 9 Feb 2022 21:25:28 -0500 Subject: [PATCH 2/2] Apply suggestions from code review Handle edge case where the result of calling getAlarm is used to set the alarm and the mode is set to "Day" Co-authored-by: Edgar Bonet --- src/RTC_DS3231.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/RTC_DS3231.cpp b/src/RTC_DS3231.cpp index f831b190..9a74166a 100644 --- a/src/RTC_DS3231.cpp +++ b/src/RTC_DS3231.cpp @@ -213,7 +213,9 @@ DateTime RTC_DS3231::getAlarm1() { day = bcd2bin(buffer[3] & 0x3F); } - return DateTime(2000U, 1, day, hour, minutes, seconds); + // On the first week of May 2000, the day-of-the-week number + // matches the date number. + return DateTime(2000, 5, day, hour, minutes, seconds); } /**************************************************************************/ @@ -247,7 +249,9 @@ DateTime RTC_DS3231::getAlarm2() { day = bcd2bin(buffer[2] & 0x3F); } - return DateTime(2000U, 1, day, hour, minutes, 0); + // On the first week of May 2000, the day-of-the-week number + // matches the date number. + return DateTime(2000, 5, day, hour, minutes, 0); } /**************************************************************************/