src/DS3231_Logger.cpp
Source code
/******************************************************************************
DS3231_Logger.cpp
A simplified library for the DS3231, focused on data logger applications
Bobby Schulz @ Northern Widget LLC
4/4/2018
The DS3231 is a high accuracy tempurature compensated RTC. This chip allows for time to be accurately kept
over long periods of time, and waking up a logger device when required to take measurments or tend to sensors
"That's not fair. That's not fair at all. There was time now. There was, was all the time I needed..."
-Henery Bemis
Distributed as-is; no warranty is given.
******************************************************************************/
#include "Arduino.h"
#include <Wire.h>
#include <DS3231_Logger.h>
DS3231_Logger::DS3231_Logger() {
}
int DS3231_Logger::begin(void) {
Wire.begin();
Wire.beginTransmission(ADR);
Wire.write(0x0E); //Write values to Control reg
Wire.write(0x24); //Start oscilator, turn off BBSQW, Turn off alarms, turn on convert
return Wire.endTransmission(); //return result of begin, reading is optional
}
int DS3231_Logger::setTime(int Year, int Month, int Day, int Hour, int Min, int Sec) {
if (Year > 999) {
Year = Year - 2000; //FIX! Add compnesation for centry
}
int TimeDate[7] = { Sec, Min, Hour, 0, Day, Month, Year };
for (int i = 0; i <= 6; i++) {
if (i == 3)
i++;
int b = TimeDate[i] / 10;
int a = TimeDate[i] - b * 10;
if (i == 2) {
if (b == 2)
b = B00000010;
else if (b == 1)
b = B00000001;
}
TimeDate[i] = a + (b << 4);
Wire.beginTransmission(ADR);
Wire.write(i); //Write values starting at reg 0x00
Wire.write(TimeDate[i]); //Write time date values into regs
Wire.endTransmission(); //return result of begin, reading is optional
}
//Read back time to test result of write??
}
void DS3231_Logger::readTime() {
int TimeDate[7]; //second,minute,hour,null,day,month,year
Wire.beginTransmission(ADR); //Ask 1 byte of data
Wire.write(0x00); //Read values starting at reg 0x00
Wire.endTransmission();
Wire.requestFrom(ADR, 7);
for (int i = 0; i <= 6; i++) {
if (i == 3) {
i++;
Wire.read();
}
unsigned int n = Wire.read(); //Read value of reg
//Process results
int a = n & B00001111;
if (i == 2) {
int b = (n & B00110000) >> 4; //24 hour mode
if (b == B00000010)
b = 20;
else if (b == B00000001)
b = 10;
TimeDate[i] = a + b;
} else if (i == 4) {
int b = (n & B00110000) >> 4;
TimeDate[i] = a + b * 10;
} else if (i == 5) {
int b = (n & B00010000) >> 4;
TimeDate[i] = a + b * 10;
} else if (i == 6) {
int b = (n & B11110000) >> 4;
TimeDate[i] = a + b * 10;
} else {
int b = (n & B01110000) >> 4;
TimeDate[i] = a + b * 10;
}
}
Time_Date[0] = TimeDate[6];
Time_Date[1] = TimeDate[5];
Time_Date[2] = TimeDate[4];
Time_Date[3] = TimeDate[2];
Time_Date[4] = TimeDate[1];
Time_Date[5] = TimeDate[0];
}
size_t DS3231_Logger::formatTime(char* buf, size_t n, int mode) {
//The stored fields, in order: 2-digit year, month, day, hour, minute, second.
//Every field is zero-padded to two digits and the year carries its century,
//which is what the String form did by padding each one and prefixing "20".
int year = 2000 + Time_Date[YEAR];
int written = 0;
if (mode == 0) //Year, Month, Day, Hour, Minute, Second (Scientific Style)
written = snprintf_P(buf, n, PSTR("%04d/%02d/%02d %02d:%02d:%02d"), year,
Time_Date[MONTH], Time_Date[DAY], Time_Date[3], Time_Date[4], Time_Date[5]);
else if (mode == 1) //Month, Day, Year, Hour, Minute, Second (US Civilian Style)
written = snprintf_P(buf, n, PSTR("%02d/%02d/%04d %02d:%02d:%02d"),
Time_Date[MONTH], Time_Date[DAY], year, Time_Date[3], Time_Date[4], Time_Date[5]);
else if (mode == 2) //As mode 1, on a 12 hour clock. The hour is not padded
{
int hour12 = Time_Date[3] % 12;
if (hour12 == 0) hour12 = 12; //Midnight and noon are both twelve
written = snprintf_P(buf, n, PSTR("%02d/%02d/%04d %d:%02d:%02d %s"),
Time_Date[MONTH], Time_Date[DAY], year, hour12,
Time_Date[4], Time_Date[5], Time_Date[3] >= 12 ? "PM" : "AM");
}
else if (mode == 1701) //Year, Day (of year), Hour, Minute, Second (Stardate)
{
int DayOfYear = 0;
int MonthDay[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (Time_Date[YEAR] % 4 == 0) MonthDay[2] = 29;
for (int m = 1; m < Time_Date[MONTH]; m++) {
DayOfYear = DayOfYear + MonthDay[m];
}
DayOfYear = DayOfYear + Time_Date[DAY];
written = snprintf_P(buf, n, PSTR("%04d.%d %02d.%02d.%04d"), year, DayOfYear,
Time_Date[DAY], Time_Date[MONTH], year);
}
else written = snprintf_P(buf, n, PSTR("Invalid Input"));
if (written < 0) { //The platform refused to format at all
if (n > 0) buf[0] = '\0';
return 0;
}
if ((size_t)written >= n) return n > 0 ? n - 1 : 0; //Truncated: say what fits
return (size_t)written;
}
size_t DS3231_Logger::printTime(Print& out, int mode) {
readTime();
char buf[32];
formatTime(buf, sizeof(buf), mode);
return out.print(buf);
}
String DS3231_Logger::getTime(int mode) {
readTime();
char buf[32];
formatTime(buf, sizeof(buf), mode);
return String(buf);
}
float DS3231_Logger::getTemp() {
float Temp = 0;
Wire.beginTransmission(ADR);
Wire.write(0x11); //Read from reg 0x11
Wire.endTransmission();
Wire.requestFrom(ADR, 2);
uint8_t TempHigh = Wire.read(); //Get high reg of temp data
uint8_t TempLow = Wire.read(); //Get low reg of temp data
if (bitRead(TempHigh, 7) == 1) {
TempHigh = (~TempHigh) + 1; //Take 2s complement of whole temp value
Temp = -1.0 * float(TempHigh + 0.25 * float(TempLow >> 6)); //Temp = -(Whole + 2^-2 x Frac)
} else Temp = float(TempHigh) + 0.25 * float(TempLow >> 6); //Temp = (Whole + 2^-2 x Frac)
return Temp;
}
int DS3231_Logger::getValue(int n) // n = 0:Year, 1:Month, 2:Day, 3:Hour, 4:Minute, 5:Second
{
readTime(); //Update time
return Time_Date[n]; //Return desired value
}
int DS3231_Logger::setAlarm(unsigned int Seconds) { //Set alarm from current time to x seconds from current time
//DEFINE LIMITS FOR FUNCTION!!
if (Seconds == 60) {
uint8_t AlarmMask = 0x07; //nibble for A1Mx values
// Wire.beginTransmission(ADR);
// Wire.write(0x0E); //Write values to control reg
// Wire.write(0x40); //Turn on 1 Hz square wave
// Wire.endTransmission();
Wire.beginTransmission(ADR);
Wire.write(0x0E); //Write values to control reg
Wire.write(0x06); //Turn on INTCN and Alarm 2
Wire.endTransmission();
//DEBUG!
Wire.beginTransmission(ADR);
Wire.write(0x0F); //Write values to control reg
Wire.write(0x00); //Clear any alarm flags, set oscilator to run
Wire.endTransmission();
for (int i = 0; i < 3; i++) {
Wire.beginTransmission(ADR);
Wire.write(0x0B + i); //Write values starting at reg 0x0B
// Wire.write(((AlarmMask & (1 << i)) << 8)); //Write time date values into regs
Wire.write(0x80);
Wire.endTransmission(); //return result of begin, reading is optional
}
}
else {
//Currently can not set timer for more than 24 hours
uint8_t AlarmMask = 0x08; //nibble for A1Mx values
uint8_t DY = 0; //DY/DT value
readTime();
int AlarmTime[7] = { Time_Date[5], Time_Date[4], Time_Date[3], 0, Time_Date[2], Time_Date[1], Time_Date[0] };
int AlarmVal[7] = { Seconds % 60, ((Seconds - (Seconds % 60)) / 60) % 60, ((Seconds - (Seconds % 3600)) / 3600) % 24, 0, ((Seconds - (Seconds % 86400)) / 86400), 0, 0 }; //Remove unused elements?? FIX!
int CarryIn = 0; //Carry value
int CarryOut = 0;
int MonthDay[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; //Use??
if (AlarmTime[6] % 400 == 0) MonthDay[2] = 29; //If year is divisable by 400, is leap year, correct days in February
else if ((AlarmTime[6] % 4 == 0) && (AlarmTime[6] % 100 != 0)) MonthDay[2] = 29; //Otherwise, if IS dividsable by 4, but NOT multiple of 100, is leap year, correct days in February
Wire.beginTransmission(ADR);
Wire.write(0x0E); //Write values to control reg
Wire.write(0x05); //Turn on INTCN and Alarm 1
Wire.endTransmission();
//Calc seconds
if (AlarmTime[0] + AlarmVal[0] >= 60) CarryOut = 1;
AlarmTime[0] = (AlarmTime[0] + AlarmVal[0]) % 60;
CarryIn = CarryOut; //Copy over prevous carry
//Calc minutes
if (AlarmTime[1] + AlarmVal[1] + CarryIn >= 60) CarryOut = 1;
else CarryOut = 0;
AlarmTime[1] = (AlarmTime[1] + AlarmVal[1] + CarryIn) % 60;
CarryIn = CarryOut; //Copy over prevous carry
//Calc hours
if (AlarmTime[2] + AlarmVal[2] + CarryIn >= 24) CarryOut = 1; //OUT OF RANGE??
else CarryOut = 0;
AlarmTime[2] = (AlarmTime[2] + AlarmVal[2] + CarryIn) % 24;
CarryIn = CarryOut; //Copy over prevous carry
//Calc days
if (AlarmTime[4] + AlarmVal[4] + CarryIn > MonthDay[AlarmTime[5]]) CarryOut = 1; //Carry out if result pushes you beyond current month
else CarryOut = 0;
AlarmTime[4] = (AlarmTime[4] + AlarmVal[4] + CarryIn) % (MonthDay[AlarmTime[5]] + 1);
if (AlarmTime[4] == 0) AlarmTime[4] = 1; //FIX! Find more elegant way to do this
//ADD FAILURE NOTIFICATION FOR OUT OF RANGE??
int Offset = 0;
for (int i = 0; i <= 6; i++) {
if (i == 3) i++;
int b = AlarmTime[i] / 10;
int a = AlarmTime[i] - b * 10;
if (i == 2) {
if (b == 2)
b = B00000010;
else if (b == 1)
b = B00000001;
}
AlarmTime[i] = a + (b << 4);
Wire.beginTransmission(ADR);
Wire.write(0x07 + Offset); //Write values starting at reg 0x07
Wire.write(AlarmTime[i] | ((AlarmMask & (1 << Offset)) << 8)); //Write time date values into regs
Wire.endTransmission(); //return result of begin, reading is optional
Offset++;
}
}
clearAlarm();
}
int DS3231_Logger::clearAlarm() { //Clear registers to stop alarm, must call setAlarm again to get it to turn on again
Wire.beginTransmission(ADR);
Wire.write(0x0F); //Write values to status reg
Wire.write(0x00); //Clear all flags
Wire.endTransmission(); //return result of begin, reading is optional
}
Updated on 2026-10-04 at 04:52:16 +0000