Erriez DS3231 high precision I2C RTC library for Arduino  2.0.0
This is a DS3231 high precision I2C Real Time Clock library for Arduino by Erriez.
ErriezDS3231.cpp
Go to the documentation of this file.
1 /*
2  * MIT License
3  *
4  * Copyright (c) 2020 Erriez
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 
33 #if (defined(__AVR__) || defined(ARDUINO_ARCH_SAM))
34 #include <avr/pgmspace.h>
35 #else
36 #include <pgmspace.h>
37 #endif
38 
39 #include <Wire.h>
40 
41 #include "ErriezDS3231.h"
42 
53 {
54  // Check zero bits in status register
55  if (readRegister(DS3231_REG_STATUS) & 0x70) {
56  return false;
57  }
58 
59  // DS3231 detected
60  return true;
61 }
62 
74 bool ErriezDS3231::clockEnable(bool enable)
75 {
76  uint8_t reg;
77 
78  // Read status register
80 
81  // Set or clear EOSC bit in control register
82  if (enable) {
83  reg &= ~(1 << DS3231_CTRL_EOSC);
84  } else {
85  reg |= (1 << DS3231_CTRL_EOSC);
86  }
87 
88  // Write control register
89  if (!writeRegister(DS3231_REG_CONTROL, reg)) {
90  return false;
91  }
92 
93  // Clear OSF bit in status register
95  reg &= ~(1 << DS3231_STAT_OSF);
96  return writeRegister(DS3231_REG_STATUS, reg);
97 }
98 
111 {
112  // Check OSF bit in status register
114  // RTC clock stopped
115  return false;
116  } else {
117  // RTC clock is running
118  return true;
119  }
120 }
121 
128 {
129  struct tm dt;
130  time_t t;
131 
132  // Read time structure
133  if (!read(&dt)) {
134  // RTC read failed
135  return 0;
136  }
137 
138  // Convert date/time struct tm to time_t
139  t = mktime(&dt);
140 
141  // An offset is needed for AVR target
142 #ifdef ARDUINO_ARCH_AVR
143  t += UNIX_OFFSET;
144 #endif
145 
146  // Return Unix epoch UTC
147  return t;
148 }
149 
160 {
161  struct tm *dt;
162 
163  // Subtract UNIX offset for AVR targets
164 #ifdef ARDUINO_ARCH_AVR
165  t -= UNIX_OFFSET;
166 #endif
167 
168  // Convert time_t to date/time struct tm
169  dt = gmtime(&t);
170 
171  // Write date/time to RTC
172  return write(dt);
173 }
174 
187 bool ErriezDS3231::read(struct tm *dt)
188 {
189  uint8_t buffer[7];
190 
191  // Read clock date and time registers
192  if (!readBuffer(0x00, buffer, sizeof(buffer))) {
193  memset(dt, 0, sizeof(struct tm));
194  return false;
195  }
196 
197  // Clear dt
198  memset(dt, 0, sizeof(struct tm));
199 
200  // Convert BCD buffer to Decimal
201  dt->tm_sec = bcdToDec(buffer[0] & 0x7F);
202  dt->tm_min = bcdToDec(buffer[1] & 0x7F);
203  dt->tm_hour = bcdToDec(buffer[2] & 0x3F);
204  dt->tm_wday = bcdToDec(buffer[3] & 0x07);
205  dt->tm_mday = bcdToDec(buffer[4] & 0x3F);
206  dt->tm_mon = bcdToDec(buffer[5] & 0x1F);
207  dt->tm_year = bcdToDec(buffer[6]) + 100; // 2000-1900
208 
209  // Month: 0..11
210  if (dt->tm_mon) {
211  dt->tm_mon--;
212  }
213 
214  // Day of the week: 0=Sunday
215  if (dt->tm_wday) {
216  dt->tm_wday--;
217  }
218 
219  // Check buffer for valid data
220  if ((dt->tm_sec > 59) || (dt->tm_min > 59) || (dt->tm_hour > 23) ||
221  (dt->tm_mday < 1) || (dt->tm_mday > 31) || (dt->tm_mon > 11) || (dt->tm_year > 199) ||
222  (dt->tm_wday > 6))
223  {
224  return false;
225  }
226 
227  return true;
228 }
229 
243 bool ErriezDS3231::write(const struct tm *dt)
244 {
245  uint8_t buffer[7];
246 
247  // Enable oscillator
248  if (!clockEnable(true)) {
249  return false;
250  }
251 
252  // Encode date time from decimal to BCD
253  buffer[0] = decToBcd(dt->tm_sec) & 0x7F;
254  buffer[1] = decToBcd(dt->tm_min) & 0x7F;
255  buffer[2] = decToBcd(dt->tm_hour) & 0x3F;
256  buffer[3] = decToBcd(dt->tm_wday + 1) & 0x07;
257  buffer[4] = decToBcd(dt->tm_mday) & 0x3F;
258  buffer[5] = decToBcd(dt->tm_mon + 1) & 0x1F;
259  buffer[6] = decToBcd(dt->tm_year % 100);
260 
261  // Write BCD encoded buffer to RTC registers
262  return writeBuffer(0x00, buffer, sizeof(buffer));
263 }
264 
280 bool ErriezDS3231::setTime(uint8_t hour, uint8_t min, uint8_t sec)
281 {
282  struct tm dt;
283 
284  read(&dt);
285  dt.tm_hour = hour;
286  dt.tm_min = min;
287  dt.tm_sec = sec;
288  return write(&dt);
289 }
290 
306 bool ErriezDS3231::getTime(uint8_t *hour, uint8_t *min, uint8_t *sec)
307 {
308  uint8_t buffer[3];
309 
310  // Read RTC time registers
311  if (!readBuffer(0x00, buffer, sizeof(buffer))) {
312  return false;
313  }
314 
315  // Convert BCD buffer to Decimal
316  *sec = bcdToDec(buffer[0] & 0x7F);
317  *min = bcdToDec(buffer[1] & 0x7F);
318  *hour = bcdToDec(buffer[2] & 0x3F);
319 
320  // Check buffer for valid data
321  if ((*sec > 59) || (*min > 59) || (*hour > 23)) {
322  // Invalid time
323  *sec = 0x00;
324  *min = 0x00;
325  *hour = 0x00;
326 
327  return false;
328  }
329 
330  return true;
331 }
332 
354 bool ErriezDS3231::setDateTime(uint8_t hour, uint8_t min, uint8_t sec,
355  uint8_t mday, uint8_t mon, uint16_t year,
356  uint8_t wday)
357 {
358  struct tm dt;
359 
360  // Prepare struct tm
361  dt.tm_hour = hour;
362  dt.tm_min = min;
363  dt.tm_sec = sec;
364  dt.tm_mday = mday;
365  dt.tm_mon = mon - 1;
366  dt.tm_year = year - 1900;
367  dt.tm_wday = wday;
368 
369  // Write date/time to RTC
370  return write(&dt);
371 }
372 
394 bool ErriezDS3231::getDateTime(uint8_t *hour, uint8_t *min, uint8_t *sec,
395  uint8_t *mday, uint8_t *mon, uint16_t *year,
396  uint8_t *wday)
397 {
398  struct tm dt;
399 
400  // Read date/time from RTC
401  if (!read(&dt)) {
402  return false;
403  }
404 
405  // Set return values
406  *hour = dt.tm_hour;
407  *min = dt.tm_min;
408  *sec = dt.tm_sec;
409  *mday = dt.tm_mday;
410  *mon = dt.tm_mon + 1;
411  *year = dt.tm_year + 1900;
412  *wday = dt.tm_wday;
413 
414  return true;
415 }
416 
445  uint8_t dayDate, uint8_t hours, uint8_t minutes, uint8_t seconds)
446 {
447  uint8_t buffer[4];
448 
449  // Store alarm 1 registers in buffer
450  buffer[0] = decToBcd(seconds);
451  buffer[1] = decToBcd(minutes);
452  buffer[2] = decToBcd(hours);
453  buffer[3] = decToBcd(dayDate);
454 
455  // Set alarm 1 bits
456  if (alarmType & 0x01) { buffer[0] |= (1 << DS3231_A1M1); }
457  if (alarmType & 0x02) { buffer[1] |= (1 << DS3231_A1M2); }
458  if (alarmType & 0x04) { buffer[2] |= (1 << DS3231_A1M3); }
459  if (alarmType & 0x08) { buffer[3] |= (1 << DS3231_A1M4); }
460  if (alarmType & 0x10) { buffer[3] |= (1 << DS3231_DYDT); }
461 
462  // Write alarm 1 registers
463  if (!writeBuffer(DS3231_REG_ALARM1_SEC, buffer, sizeof(buffer))) {
464  return false;
465  }
466 
467  // Clear alarm 1 flag
468  return clearAlarmFlag(Alarm1);
469 }
470 
495 bool ErriezDS3231::setAlarm2(Alarm2Type alarmType, uint8_t dayDate, uint8_t hours, uint8_t minutes)
496 {
497  uint8_t buffer[3];
498 
499  // Store alarm 2 registers in buffer
500  buffer[0] = decToBcd(minutes);
501  buffer[1] = decToBcd(hours);
502  buffer[2] = decToBcd(dayDate);
503 
504  // Set alarm 2 bits
505  if (alarmType & 0x02) { buffer[0] |= (1 << DS3231_A1M2); }
506  if (alarmType & 0x04) { buffer[1] |= (1 << DS3231_A1M3); }
507  if (alarmType & 0x08) { buffer[2] |= (1 << DS3231_A1M4); }
508  if (alarmType & 0x10) { buffer[2] |= (1 << DS3231_DYDT); }
509 
510  // Write alarm 2 registers
511  if (!writeBuffer(DS3231_REG_ALARM2_MIN, buffer, sizeof(buffer))) {
512  return false;
513  }
514 
515  // Clear alarm 2 flag
516  return clearAlarmFlag(Alarm2);
517 }
518 
534 bool ErriezDS3231::alarmInterruptEnable(AlarmId alarmId, bool enable)
535 {
536  uint8_t controlReg;
537 
538  // Clear alarm flag
539  clearAlarmFlag(alarmId);
540 
541  // Read control register
542  controlReg = readRegister(DS3231_REG_CONTROL);
543 
544  // Disable square wave out and enable INT
545  controlReg |= (1 << DS3231_CTRL_INTCN);
546 
547  // Set or clear alarm interrupt enable bit
548  if (enable) {
549  controlReg |= (1 << (alarmId - 1));
550  } else {
551  controlReg &= ~(1 << (alarmId - 1));
552  }
553 
554  // Write control register
555  return writeRegister(DS3231_REG_CONTROL, controlReg);
556 }
557 
574 {
575  // Mask alarm flags
576  if (readRegister(DS3231_REG_STATUS) & (1 << (alarmId - 1))) {
577  return true;
578  } else {
579  return false;
580  }
581 }
582 
597 {
598  uint8_t statusReg;
599 
600  // Read status register
601  statusReg = readRegister(DS3231_REG_STATUS);
602 
603  // Clear alarm interrupt flag
604  statusReg &= ~(1 << (alarmId - 1));
605 
606  // Write status register
607  return writeRegister(DS3231_REG_STATUS, statusReg);
608 }
609 
628 {
629  uint8_t controlReg;
630 
631  // Read control register
632  controlReg = readRegister(DS3231_REG_CONTROL);
633  controlReg &= ~((1 << DS3231_CTRL_BBSQW) |
634  (1 << DS3231_CTRL_INTCN) |
635  (1 << DS3231_CTRL_RS2) |
636  (1 << DS3231_CTRL_RS1));
637  controlReg |= squareWave;
638 
639  // Write control register
640  return writeRegister(DS3231_REG_CONTROL, controlReg);
641 }
642 
654 {
655  uint8_t statusReg;
656 
657  // Read status register
658  statusReg = readRegister(DS3231_REG_STATUS);
659 
660  // Set or clear EN32kHz flag in status register
661  if (enable) {
662  statusReg |= (1 << DS3231_STAT_EN32KHZ);
663  } else {
664  statusReg &= ~(1 << DS3231_STAT_EN32KHZ);
665  }
666 
667  // Write status register
668  return writeRegister(DS3231_REG_STATUS, statusReg);
669 }
670 
685 {
686  uint8_t regVal;
687 
688  // Convert 8-bit signed value to register value
689  if (val < 0) {
690  // Calculate two's complement for negative value
691  regVal = ~(-val) + 1;
692  } else {
693  // Positive register value
694  regVal = (uint8_t)val;
695  }
696 
697  // Write aging offset register
698  if (!writeRegister(DS3231_REG_AGING_OFFSET, regVal)) {
699  return false;
700  }
701 
702  // A temperature conversion is required to apply the aging offset change
704 }
705 
715 {
716  uint8_t regVal;
717 
718  // Read aging register
720 
721  // Convert to 8-bit signed value
722  if (regVal & 0x80) {
723  // Calculate two's complement for negative aging register value
724  return regVal | ~((1 << 8) - 1);
725  } else {
726  // Positive aging register value
727  return regVal;
728  }
729 }
730 
742 {
743  uint8_t controlReg;
744 
745  // Check if temperature busy flag is set
747  return false;
748  }
749 
750  // Start temperature conversion
751  controlReg = readRegister(DS3231_REG_CONTROL) | (1 << DS3231_CTRL_CONV);
752 
753  // Write control register
754  return writeRegister(DS3231_REG_CONTROL, controlReg);
755 }
756 
769 bool ErriezDS3231::getTemperature(int8_t *temperature, uint8_t *fraction)
770 {
771  uint8_t temp[2];
772 
773  // Read temperature MSB and LSB registers
774  if (!readBuffer(DS3231_REG_TEMP_MSB, &temp, sizeof(temp))) {
775  return false;
776  }
777 
778  // Set temperature argument
779  *temperature = temp[0];
780 
781  // Calculate two's complement when negative
782  if (*temperature & 0x80) {
783  *temperature |= ~((1 << 8) - 1);
784  }
785 
786  // Shift fraction bits 6 and 7 with 0.25 degree Celsius resolution
787  *fraction = (temp[1] >> 6) * 25;
788 
789  return true;
790 }
791 
799 uint8_t ErriezDS3231::bcdToDec(uint8_t bcd)
800 {
801  return (uint8_t)(10 * ((bcd & 0xF0) >> 4) + (bcd & 0x0F));
802 }
803 
811 uint8_t ErriezDS3231::decToBcd(uint8_t dec)
812 {
813  return (uint8_t)(((dec / 10) << 4) | (dec % 10));
814 }
815 
825 uint8_t ErriezDS3231::readRegister(uint8_t reg)
826 {
827  uint8_t value = 0;
828 
829  // Read buffer with one 8-bit unsigned value
830  readBuffer(reg, &value, 1);
831 
832  return value;
833 }
834 
848 bool ErriezDS3231::writeRegister(uint8_t reg, uint8_t value)
849 {
850  // Write buffer with one 8-bit unsigned value
851  return writeBuffer(reg, &value, 1);
852 }
853 
869 bool ErriezDS3231::writeBuffer(uint8_t reg, void *buffer, uint8_t writeLen)
870 {
871  // Start I2C transfer by writing the I2C address, register number and optional buffer
872  Wire.beginTransmission(DS3231_ADDR);
873  Wire.write(reg);
874  for (uint8_t i = 0; i < writeLen; i++) {
875  Wire.write(((uint8_t *)buffer)[i]);
876  }
877  if (Wire.endTransmission(true) != 0) {
878  return false;
879  }
880 
881  return true;
882 }
883 
897 bool ErriezDS3231::readBuffer(uint8_t reg, void *buffer, uint8_t readLen)
898 {
899  // Start I2C transfer by writing the I2C address and register number
900  Wire.beginTransmission(DS3231_ADDR);
901  Wire.write(reg);
902  // Generate a repeated start, followed by a read buffer
903  if (Wire.endTransmission(false) != 0) {
904  return false;
905  }
906  Wire.requestFrom((uint8_t)DS3231_ADDR, readLen);
907  for (uint8_t i = 0; i < readLen; i++) {
908  ((uint8_t *)buffer)[i] = (uint8_t)Wire.read();
909  }
910 
911  return true;
912 }
uint8_t readRegister(uint8_t reg)
Read register.
bool getDateTime(uint8_t *hour, uint8_t *min, uint8_t *sec, uint8_t *mday, uint8_t *mon, uint16_t *year, uint8_t *wday)
Get date time.
bool alarmInterruptEnable(AlarmId alarmId, bool enable)
Enable or disable Alarm 1 or 2 interrupt.
#define DS3231_REG_AGING_OFFSET
Aging offset register.
Definition: ErriezDS3231.h:58
#define DS3231_ADDR
DS3231 I2C 7-bit address.
Definition: ErriezDS3231.h:96
uint8_t bcdToDec(uint8_t bcd)
BCD to decimal conversion.
#define DS3231_CTRL_BBSQW
Battery-Backed Square-Wave Enable.
Definition: ErriezDS3231.h:72
#define DS3231_STAT_EN32KHZ
Enable 32kHz clock output.
Definition: ErriezDS3231.h:81
time_t getEpoch()
Read Unix UTC epoch time_t.
#define DS3231_A1M3
Alarm 1 bit 7 hours register.
Definition: ErriezDS3231.h:88
bool clearAlarmFlag(AlarmId alarmId)
Clear alarm flag.
#define DS3231_REG_ALARM2_MIN
Alarm 2 seconds register.
Definition: ErriezDS3231.h:52
bool setAlarm1(Alarm1Type alarmType, uint8_t dayDate, uint8_t hours, uint8_t minutes, uint8_t seconds)
Set Alarm 1.
bool startTemperatureConversion()
Start temperature conversion.
bool setAgingOffset(int8_t val)
Set aging offset register.
#define DS3231_CTRL_EOSC
Enable oscillator.
Definition: ErriezDS3231.h:71
Alarm2Type
Alarm 2 types enum.
Definition: ErriezDS3231.h:125
AlarmId
Alarm ID.
Definition: ErriezDS3231.h:104
bool readBuffer(uint8_t reg, void *buffer, uint8_t len)
Read buffer from RTC.
bool setDateTime(uint8_t hour, uint8_t min, uint8_t sec, uint8_t mday, uint8_t mon, uint16_t year, uint8_t wday)
Set date time.
bool write(const struct tm *dt)
Write date and time to RTC.
#define DS3231_CTRL_INTCN
Interrupt control.
Definition: ErriezDS3231.h:76
bool writeRegister(uint8_t reg, uint8_t value)
Write register.
#define DS3231_REG_CONTROL
Control register.
Definition: ErriezDS3231.h:56
#define DS3231_A1M4
Alarm 1 bit 7 day/date register.
Definition: ErriezDS3231.h:89
Alarm ID 2.
Definition: ErriezDS3231.h:106
bool read(struct tm *dt)
Read date and time from RTC.
uint8_t decToBcd(uint8_t dec)
Decimal to BCD conversion.
bool getTime(uint8_t *hour, uint8_t *min, uint8_t *sec)
Read time from RTC.
#define DS3231_CTRL_RS1
Square wave rate-select 1.
Definition: ErriezDS3231.h:75
#define DS3231_CTRL_RS2
Square wave rate-select 2.
Definition: ErriezDS3231.h:74
DS3231 high precision RTC library for Arduino.
bool clockEnable(bool enable=true)
Enable or disable oscillator when running on V-BAT.
bool setTime(uint8_t hour, uint8_t min, uint8_t sec)
Write time to RTC.
Alarm1Type
Alarm 1 types enum.
Definition: ErriezDS3231.h:112
#define DS3231_STAT_BSY
Temperature conversion busy flag.
Definition: ErriezDS3231.h:82
bool setEpoch(time_t t)
Write Unix epoch UTC time to RTC.
#define DS3231_REG_ALARM1_SEC
Alarm 1 seconds register.
Definition: ErriezDS3231.h:48
#define DS3231_CTRL_CONV
Start temperature conversion.
Definition: ErriezDS3231.h:73
bool getTemperature(int8_t *temperature, uint8_t *fraction)
Read temperature.
#define DS3231_STAT_OSF
Oscillator Stop Flag.
Definition: ErriezDS3231.h:80
bool getAlarmFlag(AlarmId alarmId)
Get Alarm 1 or 2 flag.
bool begin()
Initialize and detect DS3231 RTC.
#define DS3231_REG_STATUS
Status register.
Definition: ErriezDS3231.h:57
bool isRunning()
Read RTC OSF (Oscillator Stop Flag) from status register.
#define DS3231_A1M2
Alarm 1 bit 7 minutes register.
Definition: ErriezDS3231.h:87
bool setAlarm2(Alarm2Type alarmType, uint8_t dayDate, uint8_t hours, uint8_t minutes)
Set Alarm 2.
bool writeBuffer(uint8_t reg, void *buffer, uint8_t len)
Write buffer to RTC.
bool setSquareWave(SquareWave squareWave)
Configure SQW (Square Wave) output pin.
SquareWave
Squarewave enum.
Definition: ErriezDS3231.h:137
Alarm ID 1.
Definition: ErriezDS3231.h:105
#define DS3231_A1M1
Alarm 1 bit 7 seconds register.
Definition: ErriezDS3231.h:86
int8_t getAgingOffset()
Get aging offset register.
#define DS3231_REG_TEMP_MSB
Temperature MSB register.
Definition: ErriezDS3231.h:59
#define DS3231_DYDT
Alarm 2 bit 6.
Definition: ErriezDS3231.h:93
bool outputClockPinEnable(bool enable)
Enable or disable 32kHz output clock pin.