Adafruit 3028 DS3231 RTC Problem

I am trying to get a Adafruit 3028 feather working - this is the DS3231 RTC. The attraction of the DS3231 is that it is temperature compensated and super accurate +/-2ppm. I have tried the Adafruit RTC library which includes a driver for the DS3231 and also written a short library myself to try and get the RTC to work properly. After 8 hours trying everything I can think of I am still getting the same issue. I have tried 2 3028 feathers and a separate DS3231 module I have mounted on a proto board.

I can initialise the RTC and can read the registers, get the temperature (seems accurate) and can seemingly set the time/date but when I read the time/date back from the RTC it always starts incrementing the time from 2005/5/5 05:05:00 when read back and then updates only every 10 seconds even if read every second. Serial output is below:

DS3231RTC test
RTC begin
RTC setTime
setTime yyyy: 2019 mm: 5 dd: 3 dow: 5 hh: 0 mm: 0 ss: 0
RTC getTime
getTime yyyy: 2005 mm: 5 dd: 5 dow: 25 hh: 5 mm: 5 ss: 0
1115269500
Temperature: 22.00 C

Any ideas of what is going wrong will be gratefully received!

@armor, how is your featherwing connected? Can you post your code?

The featherwing is mounted in a tripler (standard Adafruit variety). I decided to try a PCF8523 RTC, the one on the Adafruit DataLogger feather and I get the same results. The code I am using is the Adafruit_RTCLib_RK in both cases. I’m going to investigate exactly what gets read back - wondering if the translation to unixtime is causing the issue as this is then formatted into a string.

Finally got to the bottom of the issue. Below is the standard function to read the RTC registers and pack in a DateTime struct/object:

DateTime DS3231::getTime()
{
    Wire.beginTransmission(DS3231_ADDRESS);
    Wire.write((byte)0);	
    Wire.endTransmission();
    Wire.requestFrom(DS3231_ADDRESS, 7);
    uint8_t ss = bcd2bin(Wire.read() & 0x7F);
    uint8_t mm = bcd2bin(Wire.read());
    uint8_t hh = bcd2bin(Wire.read());
    uint8_t dow= Wire.read();
    uint8_t d =  bcd2bin(Wire.read());
    uint8_t m =  bcd2bin(Wire.read());
    uint16_t y = bcd2bin(Wire.read()) + 2000;
    return DateTime (y, m, d, dow, hh, mm, ss);
}

This does not work - we’ll come to why later.

I had added a function to read the temperature on the DS3231 which worked fine.

float DS3231::getTemp()
{
	uint8_t _msb = read_i2c_register(DS3231_ADDRESS, DS3231_REG_TEMPM);
	uint8_t _lsb = read_i2c_register(DS3231_ADDRESS, DS3231_REG_TEMPL);
	return (float)_msb + ((_lsb >> 6) * 0.25f);
}

The obvious difference was the use of the read_i2c_register() function and the reading of one register at a time. I modified the getTime() function to use this function and read register by register - and now it works. Thus, setting the pointer to the first RTC register and requesting 7 bytes (as had been coded) does not work. I would be interested to understand why but for now I need to move on as I have wasted enough time on trying to solve this.

DateTime DS3231::getTime()
{
    uint8_t temp;
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_SEC);
    uint8_t ss = bcd2bin(temp & 0x7F);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_MIN);
    uint8_t mm = bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_HOUR);
    uint8_t hh = bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_DOW);
    uint8_t dow = temp & 0x07;
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_DATE);
    uint8_t d =  bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_MON);
    uint8_t m =  bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_YEAR);
    uint16_t y = bcd2bin(temp) + 2000;
    return DateTime (y, m, d, dow, hh, mm, ss);
}
1 Like

Hi,

I’m thinking of using this RTC for my new Boron project and I was curious if you ported your modified DS3231 library to Particle. I’m not seeing it in the Web IDE. Any chance you’d share your library? :kissing_heart:

Thanks!

RTCLib.cpp was all that I changed from the community library and as explained above just the getTime() methods - included below:

//
#include "RTCLib.h"

#define 	bcd2bin(n)   ( ((n) >> 4) * 10 + ((n) & 0x0F) )     //Macro to convert from BCD to binary format
#define 	bin2bcd(n)   ( (((n) / 10) << 4) + ((n) % 10) )     //Macro to convert from binary to BCD format 
//read/return register reg value
static uint8_t read_i2c_register(uint8_t addr, uint8_t reg)
{
    Wire.beginTransmission(addr);
    Wire.write((byte)reg);
    Wire.endTransmission();
    Wire.requestFrom(addr, (byte)1);
    return Wire.read();
}
//write register reg value val
static void write_i2c_register(uint8_t addr, uint8_t reg, uint8_t val)
{
    Wire.beginTransmission(addr);
    Wire.write((byte)reg);
    Wire.write((byte)val);
    Wire.endTransmission();
}
//Days in Month lookup
const uint8_t daysInMonth [] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
// number of days since 1/1/2000, valid for 2001..2099
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d)
{
    if (y >= 2000) y -= 2000;
    uint16_t days = d;
    for (uint8_t i = 1; i < m; ++i) days += daysInMonth[i - 1];
    if (m > 2 && y % 4 == 0) ++days;
    return days + 365 * y + (y + 3) / 4 - 1;
}
//return seconds for days and h:m:s
static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s)
{
    return ((days * 24L + h) * 60 + m) * 60 + s;
}
// DateTime implementation - ignores time zones, DST changes and leap seconds
// Input unixtime t populates the DateTime object
DateTime::DateTime (uint32_t t)
{
    t -= SECONDS_FROM_1970_TO_2000;    // bring to 2000 timestamp from 1970
    ss = t % 60;
    t /= 60;
    mm = t % 60;
    t /= 60;
    hh = t % 24;
    uint16_t days = t / 24;
    uint8_t leap;
    for (yOff = 0; ; ++yOff)
    {
        leap = yOff % 4 == 0;
        if (days < 365 + leap) break;
        days -= 365 + leap;
    }
    for (m = 1; ; ++m)
    {
        uint8_t daysPerMonth = daysInMonth[m - 1];
        if (leap && m == 2) ++daysPerMonth;
        if (days < daysPerMonth) break;
        days -= daysPerMonth;
    }
    d = days + 1;
}
// Input yyyy, m, d, dow, h, m, s populates DateTime object
DateTime::DateTime (uint16_t year, uint8_t month, uint8_t date, uint8_t hour, uint8_t min, uint8_t sec)
{
    if (year >= 2000)
        year -= 2000;
    yOff = year;
    m = month;
    d = date;
    hh = hour;
    mm = min;
    ss = sec;
}
//return t unixtime for object DateTime
uint32_t DateTime::unixtime(void) const
{
    uint32_t t;
    uint16_t days = date2days(yOff, m, d);
    t = time2long(days, hh, mm, ss);
    t += SECONDS_FROM_1970_TO_2000;  // seconds from 1970 to 2000
    return t;
}
//return t seconds since 1/1/2000 00:00:00
long DateTime::secondstime(void) const
{
    long t;
    uint16_t days = date2days(yOff, m, d);
    t = time2long(days, hh, mm, ss);
    return t;
}
boolean DS3231::begin(void)
{
    Wire.begin();
    uint8_t statreg = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
    statreg &= 0x7F; // reset OSF bit
    write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, statreg);
    return true;
}
//returns true if Oscillator Stop Flag (OSF) is set meaning the power has been lost 
bool DS3231::lostPower(void)
{
    return (read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG) >> 7);
}
//set the RTC time yyyy, m, d, h, m, s
void DS3231::setTime(uint16_t year, uint8_t month, uint8_t date, uint8_t hour, uint8_t minute, uint8_t second)
{
    if (year>=2000) year-=2000;     //00-99
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_SEC, bin2bcd(second));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MIN, bin2bcd(minute));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_HOUR, bin2bcd(hour));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DOW, 0);
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DATE, bin2bcd(date));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MON, bin2bcd(month));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_YEAR, bin2bcd(year));
    uint8_t statreg = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
    statreg &= 0x7F; // reset OSF bit
    write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, statreg);
}
//set the RTC time yyyy, m, d, h, m, s
void DS3231::setTime(uint32_t t)
{
    uint32_t ut = t;
    t -= SECONDS_FROM_1970_TO_2000;
    uint8_t second = t % 60;
    t /= 60;
    uint8_t minute = t % 60;
    t /= 60;
    uint8_t hour = t % 24;
    uint16_t days = t / 24;
    uint8_t leap;
    uint8_t date;
    uint8_t yOff, year;
    uint8_t month, m;
    for (yOff = 0; ; ++yOff)
    {
        leap = (yOff % 4 == 0);
        if (days < 365 + leap) break;
        days -= 365 + leap;
    }
    year = yOff;
    for (m = 1; ; ++m)
    {
        uint8_t daysPerMonth = daysInMonth[m - 1];
        if (leap && m == 2) ++daysPerMonth;
        if (days < daysPerMonth) break;
        days -= daysPerMonth;
    }
    month = m;
    date = days + 1;
    
	int dow;
	uint8_t mArr[12] = {6,2,2,5,0,3,5,1,4,6,2,4};
	dow = (year+2000) % 100;
	dow = dow*5/4;
	dow += date;
	dow += mArr[month-1];
	if ((((year+2000) % 4)==0) && (month<3)) dow -= 1;
	while (dow>7) dow -= 7;
    //Serial.printlnf("setTime %lu yyyy: %i mm: %i dd: %i dow: %i hh: %i mm: %i ss: %i",ut,year+2000,month,date,dow,hour,minute,second);
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_SEC, bin2bcd(second));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MIN, bin2bcd(minute));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_HOUR, bin2bcd(hour));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DOW, dow);
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DATE, bin2bcd(date));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MON, bin2bcd(month));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_YEAR, bin2bcd(year));
    uint8_t statreg = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
    statreg &= 0x7F; // reset OSF bit
    write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, statreg);
}
//
void DS3231::getTime(uint16_t *y, uint8_t *m, uint8_t *d, uint8_t *hh, uint8_t *mm, uint8_t *ss)
{
    uint8_t temp;
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_SEC);
    *ss = bcd2bin(temp & 0x7F);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_MIN);
    *mm = bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_HOUR);
    *hh = bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_DOW);
    uint8_t dow = temp & 0x07;
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_DATE);
    *d =  bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_MON);
    *m =  bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_YEAR);
    *y = (uint16_t) bcd2bin(temp) + 2000;
    //Serial.printlnf("getTime bcdy: %0X yyyy: %d mm: %i dd: %i dow: %i hh: %i mm: %i ss: %i",temp,*y,*m,*d,dow,*hh,*mm,*ss);
}
//
DateTime DS3231::getTime()
{
    uint8_t temp;
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_SEC);
    uint8_t ss = bcd2bin(temp & 0x7F);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_MIN);
    uint8_t mm = bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_HOUR);
    uint8_t hh = bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_DOW);
    uint8_t dow = temp & 0x07;
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_DATE);
    uint8_t d =  bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_MON);
    uint8_t m =  bcd2bin(temp);
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_REG_YEAR);
    uint16_t y = bcd2bin(temp) + 2000;
    return DateTime (y, m, d, hh, mm, ss);
}
//return float temperature measurement in C
float DS3231::getTemp()
{
	uint8_t _msb = read_i2c_register(DS3231_ADDRESS, DS3231_REG_TEMPM);
	uint8_t _lsb = read_i2c_register(DS3231_ADDRESS, DS3231_REG_TEMPL);
	return (float)_msb + ((_lsb >> 6) * 0.25f);
}
//
void DS3231::setAlarm(uint8_t num, uint8_t date, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint8_t temp;
    if (num > 2) return; //out of range, num = 0 will clear the alarm 1 & 2 enabled bits
    temp = read_i2c_register(DS3231_ADDRESS, DS3231_CONTROL);
    num > 0 ? temp &= ~(1<<(num-1)): temp &= ~3; //clear alarm enable bit to disable
    write_i2c_register(DS3231_ADDRESS, DS3231_CONTROL, temp);
    uint8_t stat = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
    num > 0 ? stat &= ~(1<<(num-1)): stat &= ~3; //clear alarm flag bit to disable
    write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, stat);   //reset alarm flag
    if (num == 1)
    {
        write_i2c_register(DS3231_ADDRESS, DS3231_REG_A1SEC, bin2bcd(sec));
        write_i2c_register(DS3231_ADDRESS, DS3231_REG_A1MIN, bin2bcd(min));
        write_i2c_register(DS3231_ADDRESS, DS3231_REG_A1HR,  bin2bcd(hour));
        if (date == 0)  //set A1M4 bit to alarm on match of hour, min and sec
        {
            temp = 0x80; 
            write_i2c_register(DS3231_ADDRESS, DS3231_REG_A1DAT, temp);
        }
        else            //set A1M4 bit to alarm on match of date, hour, min and sec 
        {
            write_i2c_register(DS3231_ADDRESS, DS3231_REG_A1DAT, bin2bcd(date));
        }
        temp = read_i2c_register(DS3231_ADDRESS, DS3231_CONTROL);
        temp |= 0x01;   //set bit 0
        write_i2c_register(DS3231_ADDRESS, DS3231_CONTROL, temp);   //set alarm 1 enabled
        //uint8_t stat = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
        //stat &= ~1;
        //write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, stat);   //reset alarm flag
    }
    else if (num == 2)
    {
        write_i2c_register(DS3231_ADDRESS, DS3231_REG_A2MIN, bin2bcd(min));
        write_i2c_register(DS3231_ADDRESS, DS3231_REG_A2HR, bin2bcd(hour));
        if (date == 0)  //set A2M4 bit to alarm on match of hour, min and sec=0
        {
            temp = 0x80; 
            write_i2c_register(DS3231_ADDRESS, DS3231_REG_A2DAT, temp);
        }
        else            //set A2M4 bit to alarm on match of date, hour, min and sec=0
        {
            write_i2c_register(DS3231_ADDRESS, DS3231_REG_A2DAT, bin2bcd(date));
        }
        temp = read_i2c_register(DS3231_ADDRESS, DS3231_CONTROL);
        temp |= 0x02;   //set bit 1
        write_i2c_register(DS3231_ADDRESS, DS3231_CONTROL, temp);   //set alarm 2 enabled
        //uint8_t stat = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
        //stat &= ~2;
        //write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, stat);   //reset alarm flag
    }
}
//returns true if alarm num [1-2] has alarmed (and clears the alarm flag) and false if not or not set
bool DS3231::alarmed(uint8_t num)
{
    bool result = false;
    if (num == 1 || num == 2)
    {
        uint8_t stat = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
        uint8_t temp = stat;
        uint8_t cont = read_i2c_register(DS3231_ADDRESS, DS3231_CONTROL);
        cont &= 1<<(num-1);
        if (cont > 0)               //alarm enabled
        {
            temp &= 1<<(num-1);     //mask off all but alarm flags
            if (temp > 0)           //alarm flagged
            {
                result = true;
                stat &= ~(1<<(num-1));
                write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, stat);   //reset alarm flag
            }
        }
    }
    return result;
}
//switch on the interrupt output to the /INT \SQW pin (active low)
void DS3231::setOutput(bool _set)
{
    uint8_t _reg = read_i2c_register(DS3231_ADDRESS, DS3231_CONTROL);   //read interrupt control register
    _reg &= ~0x04;                                                      //clear bit 2 INTCN / turn off
    _reg &= ~0x18;                                                      //set freq bits to 0
    if (_set) _reg |= 0x04;                                             //if set requested then set bit 2 INTCN / turn on
    write_i2c_register(DS3231_ADDRESS, DS3231_CONTROL, _reg);           //write interrupt control register
}
//
DateTime DateTime::operator+(const TimeSpan& span) {
    return DateTime(unixtime()+span.totalseconds());
}

DateTime DateTime::operator-(const TimeSpan& span) {
    return DateTime(unixtime()-span.totalseconds());
}

TimeSpan DateTime::operator-(const DateTime& right) {
    return TimeSpan(unixtime()-right.unixtime());
}
// TimeSpan implementation
TimeSpan::TimeSpan (int32_t seconds):
  _seconds(seconds)
{}
//
TimeSpan::TimeSpan (int16_t days, int8_t hours, int8_t minutes, int8_t seconds):
  _seconds((int32_t)days*86400L + (int32_t)hours*3600 + (int32_t)minutes*60 + seconds)
{}
//
TimeSpan::TimeSpan (const TimeSpan& copy):
  _seconds(copy._seconds)
{}
//
TimeSpan TimeSpan::operator+(const TimeSpan& right) {
  return TimeSpan(_seconds+right._seconds);
}
//
TimeSpan TimeSpan::operator-(const TimeSpan& right) {
  return TimeSpan(_seconds-right._seconds);
}
//
void DS3231::adjust(const DateTime& dt)
{
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_SEC, bin2bcd(dt.second()));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MIN, bin2bcd(dt.minute()));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_HOUR, bin2bcd(dt.hour()));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DOW, 0);
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DATE, bin2bcd(dt.day()));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MON, bin2bcd(dt.month()));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_YEAR, bin2bcd(dt.year() - 2000));
    uint8_t statreg = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
    statreg &= 0x7F; // reset OSF bit
    write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, statreg);
}
//set the RTC time using unixtime
void DS3231::adjust(uint32_t t)
{
    uint32_t ut = t;
    t -= SECONDS_FROM_1970_TO_2000;
    uint8_t second = t % 60;
    t /= 60;
    uint8_t minute = t % 60;
    t /= 60;
    uint8_t hour = t % 24;
    uint16_t days = t / 24;
    uint8_t leap;
    uint8_t date;
    uint8_t yOff, year;
    uint8_t month, m;
    for (yOff = 0; ; ++yOff)
    {
        leap = (yOff % 4 == 0);
        if (days < 365 + leap) break;
        days -= 365 + leap;
    }
    year = yOff;
    for (m = 1; ; ++m)
    {
        uint8_t daysPerMonth = daysInMonth[m - 1];
        if (leap && m == 2) ++daysPerMonth;
        if (days < daysPerMonth) break;
        days -= daysPerMonth;
    }
    month = m;
    date = days + 1;
    
	int dow;
	uint8_t mArr[12] = {6,2,2,5,0,3,5,1,4,6,2,4};
	dow = (year+2000) % 100;
	dow = dow*5/4;
	dow += date;
	dow += mArr[month-1];
	if ((((year+2000) % 4)==0) && (month<3)) dow -= 1;
	while (dow>7) dow -= 7;
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_SEC, bin2bcd(second));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MIN, bin2bcd(minute));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_HOUR, bin2bcd(hour));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DOW, dow);
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_DATE, bin2bcd(date));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_MON, bin2bcd(month));
    write_i2c_register(DS3231_ADDRESS, DS3231_REG_YEAR, bin2bcd(year));
    uint8_t statreg = read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG);
    statreg &= 0x7F; // reset OSF bit
    write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, statreg);
}

Understood. Thanks for the awesome work figuring this out. It will save me from nightmares.

From the Web IDE, do i just manually edit the code in the .CPP file and save it to my application. I remember reading another feed from @ScruffR that there may be other requirements to modify/edit a community library and incorporate those changes?

Thanks

Sorry for the delay in reply - my laptop stopped working.

What I would do is create your own local .cpp and .h files (using the + button in the top right of the window. Easiest to just copy exactly what is in the library except for RTCLib.cpp, plus in RTCSync.h I remember that you have to define the RTC device as DS3231.

Thanks for the help!

Are you using the RTC to wake up from deep sleep ?

No I have not used the DS3231 to wake from deep sleep. There is an interrupt pin but it is not programmable in operation. There is a topic about using a microchip device that can correctly wake from deep sleep - I think a Rick K tutorial.

The DS3231 I use is there to provide a reliable local time source (through sleeping and waking) and I also use the alarms feature for a calendar entry wakeup but from stop sleep not deep/standby sleep. The sleepy end node sleep time isn’t critical in its timing hence I just use the seconds parameter in the System.sleep() call and that works for me.