First Commit

This commit is contained in:
MindCreeper03
2025-02-27 19:31:50 +01:00
parent bcbb6aff9a
commit e490df1715
2470 changed files with 1479965 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
# Changelog
When adding new features to the repository (i.e. by merging your PR), please add your changes to the section with the upcoming version. With every build, this section becomes the next version, and a new upcoming section is created.
## -- Upcoming --
- Overloaded `getAXTime()` methods with `bool clearAlarmBits` flag.
- Added `tests` directory for rudimentary unit tests
- Added Arduino test script for `getAXTime()` methods
- Introduced Changelog
- PR #66 fixes a bug in setEpoch() and adds an example
## v1.1.1
- Overloaded `checkIfAlarm()` function with optinal parameter to suppress clearing of the alarm flags
- Introduced new alarm examples working with polling and interrupting code
- Addressed an issue with naming in the declaration: Standard naming now uses `myRTC` instead of `Clock`.
## v1.1.0
This is the last code version, whos changes are not covered in the changelog.

View File

@@ -0,0 +1,16 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Ayars"
given-names: "Eric"
orcid: "https://orcid.org/0000-0003-2150-6935"
- family-names: "Wickert"
given-names: "Andrew D."
orcid: "https://orcid.org/0000-0002-9545-3365"
- family-names: "Open-Source Hardware Community"
given-names: "Global"
title: "DS3231"
version: 1.1.1
doi: 10.5281/zenodo.2008621
date-released: 2022-08-08
url: "https://github.com/NorthernWidget/DS3231"

View File

@@ -0,0 +1,13 @@
# Contribution Guidelines
This library is the culmination of the expertise of many members of the open source community who have dedicated their time and hard work. The best way to ask for help or propose a new idea is to [create a new issue](https://github.com/NorthernWidget/DS3231/issues/new) while creating a Pull Request with your code changes allows you to share your own innovations with the rest of the community.
The following are some guidelines to observe when creating issues or PRs:
- Be friendly; it is important that we can all enjoy a safe space as we are all working on the same project and it is okay for people to have different ideas
- [Use code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code); it helps us help you when we can read your code! On that note also refrain from pasting more than 30 lines of code in a post, instead [create a gist](https://gist.github.com/) if you need to share large snippets
- Use reasonable titles; refrain from using overly long or capitalized titles as they are usually annoying and do little to encourage others to help :smile:
- Be detailed; refrain from mentioning code problems without sharing your source code and always give information regarding your board and version of the library.

809
libraries/DS3231/DS3231.cpp Normal file
View File

@@ -0,0 +1,809 @@
/*
DS3231.cpp: DS3231 Real-Time Clock library
Eric Ayars
4/1/11
Spliced in DateTime all-at-once reading (to avoid rollover) and unix time
from Jean-Claude Wippler and Limor Fried
Andy Wickert
5/15/11
Fixed problem with SD processors(no function call) by replacing all occurences of the term PM, which
is defined as a macro on SAMD controllers by PM_time.
Simon Gassner
11/28/2017
Fixed setting 12-hour clock in setHour function so that 12:xx AM is not stored as 00:xx and corrected
the setting of the PM flag for 12:xx PM. These address certain DS3231 errors in properly setting the
AM/PM (bit 5) flag in the 02h register when passing from AM to PM and PM to AM.
David Merrifield
04/14/2020
Changed parameter to uint16_t in isleapYear() because the function performs 16-bit arithmetic
at (y % 400) and because date2days() calls it with a uint16_t parameter. Grouped and typecast certain parameters and intermediate results in the constructor DateTime::DateTime (uint32_t t) to resolve a couple of non-fatal compiler warnings.
David Sparks
08 Sept 2022
Released into the public domain.
*/
#include "DS3231.h"
// These included for the DateTime class inclusion; will try to find a way to
// not need them in the future...
#if defined(__AVR__)
#include <avr/pgmspace.h>
#elif defined(ESP8266)
#include <pgmspace.h>
#endif
// Changed the following to work on 1.0
//#include "WProgram.h"
#include <Arduino.h>
#define CLOCK_ADDRESS 0x68
#define SECONDS_FROM_1970_TO_2000 946684800
// Constructor
DS3231::DS3231() : _Wire(Wire) {
// nothing to do for this constructor.
}
DS3231::DS3231(TwoWire & w) : _Wire(w) {
}
// Utilities from JeeLabs/Ladyada
////////////////////////////////////////////////////////////////////////////////
// utility code, some of this could be exposed in the DateTime API if needed
// DS3231 is smart enough to know this, but keeping it for now so I don't have
// to rewrite their code. -ADW
static const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
// number of days since 2000/01/01, 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 += pgm_read_byte(daysInMonth + i - 1);
if (m > 2 && isleapYear(y))
++days;
return days + 365 * y + (y + 3) / 4 - 1;
}
static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
return ((days * 24L + h) * 60 + m) * 60 + s;
}
/*****************************************
Public Functions
*****************************************/
/*******************************************************************************
* TO GET ALL DATE/TIME INFORMATION AT ONCE AND AVOID THE CHANCE OF ROLLOVER
* DateTime implementation spliced in here from Jean-Claude Wippler's (JeeLabs)
* RTClib, as modified by Limor Fried (Ladyada); source code at:
* https://github.com/adafruit/RTClib
******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
// DateTime implementation - ignores time zones and DST changes
// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second
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 = isleapYear((uint16_t) yOff);
if (days < (uint16_t)(365 + leap))
break;
days -= (365 + leap);
}
for (m = 1; ; ++m) {
uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1);
if (leap && m == 2)
++daysPerMonth;
if (days < daysPerMonth)
break;
days -= daysPerMonth;
}
d = days + 1;
}
DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) {
if (year >= 2000)
year -= 2000;
yOff = year;
m = month;
d = day;
hh = hour;
mm = min;
ss = sec;
}
// supported formats are date "Mmm dd yyyy" and time "hh:mm:ss" (same as __DATE__ and __TIME__)
DateTime::DateTime(const char* date, const char* time) {
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
static char buff[4] = {'0','0','0','0'};
int y;
sscanf(date, "%s %hhu %d", buff, &d, &y);
yOff = y >= 2000 ? y - 2000 : y;
m = (strstr(month_names, buff) - month_names) / 3 + 1;
sscanf(time, "%hhu:%hhu:%hhu", &hh, &mm, &ss);
}
// UNIX time: IS CORRECT ONLY WHEN SET TO UTC!!!
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;
}
// Slightly modified from JeeLabs / Ladyada
// Get all date/time at once to avoid rollover (e.g., minute/second don't match)
static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
// Commented to avoid compiler warnings, but keeping in case we want this
// eventually
//static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }
// Sept 2022 changed parameter to uint16_t from uint8_t
bool isleapYear(const uint16_t y) {
if(y&3)//check if divisible by 4
return false;
//only check other, when first failed
return (y % 100 || y % 400 == 0);
}
DateTime RTClib::now(TwoWire & _Wire) {
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0); // This is the first register address (Seconds)
// We'll read from here on for 7 bytes: secs reg, minutes reg, hours, days, months and years.
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 7);
uint16_t ss = bcd2bin(_Wire.read() & 0x7F);
uint16_t mm = bcd2bin(_Wire.read());
uint16_t hh = bcd2bin(_Wire.read());
_Wire.read();
uint16_t d = bcd2bin(_Wire.read());
uint16_t m = bcd2bin(_Wire.read());
uint16_t y = bcd2bin(_Wire.read()) + 2000;
return DateTime (y, m, d, hh, mm, ss);
}
///// ERIC'S ORIGINAL CODE FOLLOWS /////
byte DS3231::getSecond() {
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x00);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
return bcdToDec(_Wire.read());
}
byte DS3231::getMinute() {
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x01);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
return bcdToDec(_Wire.read());
}
byte DS3231::getHour(bool& h12, bool& PM_time) {
byte temp_buffer;
byte hour;
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x02);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
temp_buffer = _Wire.read();
h12 = temp_buffer & 0b01000000;
if (h12) {
PM_time = temp_buffer & 0b00100000;
hour = bcdToDec(temp_buffer & 0b00011111);
} else {
hour = bcdToDec(temp_buffer & 0b00111111);
}
return hour;
}
byte DS3231::getDoW() {
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x03);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
return bcdToDec(_Wire.read());
}
byte DS3231::getDate() {
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x04);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
return bcdToDec(_Wire.read());
}
byte DS3231::getMonth(bool& Century) {
byte temp_buffer;
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x05);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
temp_buffer = _Wire.read();
Century = temp_buffer & 0b10000000;
return (bcdToDec(temp_buffer & 0b01111111)) ;
}
byte DS3231::getYear() {
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x06);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
return bcdToDec(_Wire.read());
}
// setEpoch function gives the epoch as parameter and feeds the RTC
// epoch = UnixTime and starts at 01.01.1970 00:00:00
// HINT: => the AVR time.h Lib is based on the year 2000
void DS3231::setEpoch(time_t epoch, bool flag_localtime) {
#if defined (__AVR__)
epoch -= SECONDS_FROM_1970_TO_2000;
#endif
struct tm tmnow;
if (flag_localtime) {
localtime_r(&epoch, &tmnow);
}
else {
gmtime_r(&epoch, &tmnow);
}
setSecond(tmnow.tm_sec);
setMinute(tmnow.tm_min);
setHour(tmnow.tm_hour);
setDoW(tmnow.tm_wday + 1U);
setDate(tmnow.tm_mday);
setMonth(tmnow.tm_mon + 1U);
setYear(tmnow.tm_year - 100U);
}
void DS3231::setSecond(byte Second) {
// Sets the seconds
// This function also resets the Oscillator Stop Flag, which is set
// whenever power is interrupted.
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x00);
_Wire.write(decToBcd(Second));
_Wire.endTransmission();
// Clear OSF flag
byte temp_buffer = readControlByte(1);
writeControlByte((temp_buffer & 0b01111111), 1);
}
void DS3231::setMinute(byte Minute) {
// Sets the minutes
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x01);
_Wire.write(decToBcd(Minute));
_Wire.endTransmission();
}
// Following setHour revision by David Merrifield 4/14/2020 correcting handling of 12-hour clock
void DS3231::setHour(byte Hour) {
// Sets the hour, without changing 12/24h mode.
// The hour must be in 24h format.
bool h12;
byte temp_hour;
// Start by figuring out what the 12/24 mode is
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x02);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
h12 = (_Wire.read() & 0b01000000);
// if h12 is true, it's 12h mode; false is 24h.
if (h12) {
// 12 hour
bool am_pm = (Hour > 11);
temp_hour = Hour;
if (temp_hour > 11) {
temp_hour = temp_hour - 12;
}
if (temp_hour == 0) {
temp_hour = 12;
}
temp_hour = decToBcd(temp_hour) | (am_pm << 5) | 0b01000000;
} else {
// 24 hour
temp_hour = decToBcd(Hour) & 0b10111111;
}
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x02);
_Wire.write(temp_hour);
_Wire.endTransmission();
}
void DS3231::setDoW(byte DoW) {
// Sets the Day of Week
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x03);
_Wire.write(decToBcd(DoW));
_Wire.endTransmission();
}
void DS3231::setDate(byte Date) {
// Sets the Date
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x04);
_Wire.write(decToBcd(Date));
_Wire.endTransmission();
}
void DS3231::setMonth(byte Month) {
// Sets the month
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x05);
_Wire.write(decToBcd(Month));
_Wire.endTransmission();
}
void DS3231::setYear(byte Year) {
// Sets the year
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x06);
_Wire.write(decToBcd(Year));
_Wire.endTransmission();
}
void DS3231::setClockMode(bool h12) {
// sets the mode to 12-hour (true) or 24-hour (false).
// One thing that bothers me about how I've written this is that
// if the read and right happen at the right hourly millisecnd,
// the clock will be set back an hour. Not sure how to do it better,
// though, and as long as one doesn't set the mode frequently it's
// a very minimal risk.
// It's zero risk if you call this BEFORE setting the hour, since
// the setHour() function doesn't change this mode.
byte temp_buffer;
// Start by reading byte 0x02.
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x02);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
temp_buffer = _Wire.read();
// Set the flag to the requested value:
if (h12) {
temp_buffer = temp_buffer | 0b01000000;
} else {
temp_buffer = temp_buffer & 0b10111111;
}
// Write the byte
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x02);
_Wire.write(temp_buffer);
_Wire.endTransmission();
}
float DS3231::getTemperature() {
// Checks the internal thermometer on the DS3231 and returns the
// temperature as a floating-point value.
// Updated / modified a tiny bit from "Coding Badly" and "Tri-Again"
// http://forum.arduino.cc/index.php/topic,22301.0.html
byte tMSB, tLSB;
float temp3231;
// temp registers (11h-12h) get updated automatically every 64s
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x11);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 2);
// Should I do more "if available" checks here?
if(_Wire.available()) {
tMSB = _Wire.read(); //2's complement int portion
tLSB = _Wire.read(); //fraction portion
int16_t itemp = ( tMSB << 8 | (tLSB & 0xC0) ); // Shift upper byte, add lower
temp3231 = ( (float)itemp / 256.0 ); // Scale and return
}
else {
temp3231 = -9999; // Impossible temperature; error value
}
return temp3231;
}
void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM) {
byte temp_buffer;
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x07);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 4);
temp_buffer = _Wire.read(); // Get A1M1 and A1 Seconds
A1Second = bcdToDec(temp_buffer & 0b01111111);
// put A1M1 bit in position 0 of DS3231_AlarmBits.
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>7;
temp_buffer = _Wire.read(); // Get A1M2 and A1 minutes
A1Minute = bcdToDec(temp_buffer & 0b01111111);
// put A1M2 bit in position 1 of DS3231_AlarmBits.
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>6;
temp_buffer = _Wire.read(); // Get A1M3 and A1 Hour
// put A1M3 bit in position 2 of DS3231_AlarmBits.
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>5;
// determine A1 12/24 mode
A1h12 = temp_buffer & 0b01000000;
if (A1h12) {
A1PM = temp_buffer & 0b00100000; // determine am/pm
A1Hour = bcdToDec(temp_buffer & 0b00011111); // 12-hour
} else {
A1Hour = bcdToDec(temp_buffer & 0b00111111); // 24-hour
}
temp_buffer = _Wire.read(); // Get A1M4 and A1 Day/Date
// put A1M3 bit in position 3 of DS3231_AlarmBits.
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>4;
// determine A1 day or date flag
A1Dy = (temp_buffer & 0b01000000)>>6;
if (A1Dy) {
// alarm is by day of week, not date.
A1Day = bcdToDec(temp_buffer & 0b00001111);
} else {
// alarm is by date, not day of week.
A1Day = bcdToDec(temp_buffer & 0b00111111);
}
}
void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM, bool clearAlarmBits) {
if (clearAlarmBits) {
AlarmBits = 0x0;
}
getA1Time(A1Day, A1Hour, A1Minute, A1Second, AlarmBits, A1Dy, A1h12, A1PM);
}
void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM) {
byte temp_buffer;
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x0b);
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 3);
temp_buffer = _Wire.read(); // Get A2M2 and A2 Minutes
A2Minute = bcdToDec(temp_buffer & 0b01111111);
// put A2M2 bit in position 4 of DS3231_AlarmBits.
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>3;
temp_buffer = _Wire.read(); // Get A2M3 and A2 Hour
// put A2M3 bit in position 5 of DS3231_AlarmBits.
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>2;
// determine A2 12/24 mode
A2h12 = temp_buffer & 0b01000000;
if (A2h12) {
A2PM = temp_buffer & 0b00100000; // determine am/pm
A2Hour = bcdToDec(temp_buffer & 0b00011111); // 12-hour
} else {
A2Hour = bcdToDec(temp_buffer & 0b00111111); // 24-hour
}
temp_buffer = _Wire.read(); // Get A2M4 and A1 Day/Date
// put A2M4 bit in position 6 of DS3231_AlarmBits.
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>1;
// determine A2 day or date flag
A2Dy = (temp_buffer & 0b01000000)>>6;
if (A2Dy) {
// alarm is by day of week, not date.
A2Day = bcdToDec(temp_buffer & 0b00001111);
} else {
// alarm is by date, not day of week.
A2Day = bcdToDec(temp_buffer & 0b00111111);
}
}
void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM, bool clearAlarmBits) {
if (clearAlarmBits) {
AlarmBits = 0x0;
}
getA2Time(A2Day, A2Hour, A2Minute, AlarmBits, A2Dy, A2h12, A2PM);
}
void DS3231::setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM) {
// Sets the alarm-1 date and time on the DS3231, using A1* information
byte temp_buffer;
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x07); // A1 starts at 07h
// Send A1 second and A1M1
_Wire.write(decToBcd(A1Second) | ((AlarmBits & 0b00000001) << 7));
// Send A1 Minute and A1M2
_Wire.write(decToBcd(A1Minute) | ((AlarmBits & 0b00000010) << 6));
// Figure out A1 hour
if (A1h12) {
// Start by converting existing time to h12 if it was given in 24h.
if (A1Hour > 12) {
// well, then, this obviously isn't a h12 time, is it?
A1Hour = A1Hour - 12;
A1PM = true;
}
if (A1PM) {
// Afternoon
// Convert the hour to BCD and add appropriate flags.
temp_buffer = decToBcd(A1Hour) | 0b01100000;
} else {
// Morning
// Convert the hour to BCD and add appropriate flags.
temp_buffer = decToBcd(A1Hour) | 0b01000000;
}
} else {
// Now for 24h
temp_buffer = decToBcd(A1Hour);
}
temp_buffer = temp_buffer | ((AlarmBits & 0b00000100)<<5);
// A1 hour is figured out, send it
_Wire.write(temp_buffer);
// Figure out A1 day/date and A1M4
temp_buffer = ((AlarmBits & 0b00001000)<<4) | decToBcd(A1Day);
if (A1Dy) {
// Set A1 Day/Date flag (Otherwise it's zero)
temp_buffer = temp_buffer | 0b01000000;
}
_Wire.write(temp_buffer);
// All done!
_Wire.endTransmission();
}
void DS3231::setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM) {
// Sets the alarm-2 date and time on the DS3231, using A2* information
byte temp_buffer;
_Wire.beginTransmission(CLOCK_ADDRESS);
_Wire.write(0x0b); // A1 starts at 0bh
// Send A2 Minute and A2M2
_Wire.write(decToBcd(A2Minute) | ((AlarmBits & 0b00010000) << 3));
// Figure out A2 hour
if (A2h12) {
// Start by converting existing time to h12 if it was given in 24h.
if (A2Hour > 12) {
// well, then, this obviously isn't a h12 time, is it?
A2Hour = A2Hour - 12;
A2PM = true;
}
if (A2PM) {
// Afternoon
// Convert the hour to BCD and add appropriate flags.
temp_buffer = decToBcd(A2Hour) | 0b01100000;
} else {
// Morning
// Convert the hour to BCD and add appropriate flags.
temp_buffer = decToBcd(A2Hour) | 0b01000000;
}
} else {
// Now for 24h
temp_buffer = decToBcd(A2Hour);
}
// add in A2M3 bit
temp_buffer = temp_buffer | ((AlarmBits & 0b00100000)<<2);
// A2 hour is figured out, send it
_Wire.write(temp_buffer);
// Figure out A2 day/date and A2M4
temp_buffer = ((AlarmBits & 0b01000000)<<1) | decToBcd(A2Day);
if (A2Dy) {
// Set A2 Day/Date flag (Otherwise it's zero)
temp_buffer = temp_buffer | 0b01000000;
}
_Wire.write(temp_buffer);
// All done!
_Wire.endTransmission();
}
void DS3231::turnOnAlarm(byte Alarm) {
// turns on alarm number "Alarm". Defaults to 2 if Alarm is not 1.
byte temp_buffer = readControlByte(0);
// modify control byte
if (Alarm == 1) {
temp_buffer = temp_buffer | 0b00000101;
} else {
temp_buffer = temp_buffer | 0b00000110;
}
writeControlByte(temp_buffer, 0);
}
void DS3231::turnOffAlarm(byte Alarm) {
// turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1.
// Leaves interrupt pin alone.
byte temp_buffer = readControlByte(0);
// modify control byte
if (Alarm == 1) {
temp_buffer = temp_buffer & 0b11111110;
} else {
temp_buffer = temp_buffer & 0b11111101;
}
writeControlByte(temp_buffer, 0);
}
bool DS3231::checkAlarmEnabled(byte Alarm) {
// Checks whether the given alarm is enabled.
byte result = 0x0;
byte temp_buffer = readControlByte(0);
if (Alarm == 1) {
result = temp_buffer & 0b00000001;
} else {
result = temp_buffer & 0b00000010;
}
return result;
}
bool DS3231::checkIfAlarm(byte Alarm) {
// Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly.
// Turns flag off, also.
// defaults to checking alarm 2, unless Alarm == 1.
byte result;
byte temp_buffer = readControlByte(1);
if (Alarm == 1) {
// Did alarm 1 go off?
result = temp_buffer & 0b00000001;
// clear flag
temp_buffer = temp_buffer & 0b11111110;
} else {
// Did alarm 2 go off?
result = temp_buffer & 0b00000010;
// clear flag
temp_buffer = temp_buffer & 0b11111101;
}
writeControlByte(temp_buffer, 1);
return result;
}
bool DS3231::checkIfAlarm(byte Alarm, bool clearflag) {
// Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly.
// Clears flag, if clearflag is set
// defaults to checking alarm 2, unless Alarm == 1.
byte result;
byte temp_buffer = readControlByte(1);
if (Alarm == 1) {
// Did alarm 1 go off?
result = temp_buffer & 0b00000001;
// clear flag
temp_buffer = temp_buffer & 0b11111110;
} else {
// Did alarm 2 go off?
result = temp_buffer & 0b00000010;
// clear flag
temp_buffer = temp_buffer & 0b11111101;
}
if (clearflag) {
writeControlByte(temp_buffer, 1);
}
return result;
}
void DS3231::enableOscillator(bool TF, bool battery, byte frequency) {
// turns oscillator on or off. True is on, false is off.
// if battery is true, turns on even for battery-only operation,
// otherwise turns off if Vcc is off.
// frequency must be 0, 1, 2, or 3.
// 0 = 1 Hz
// 1 = 1.024 kHz
// 2 = 4.096 kHz
// 3 = 8.192 kHz (Default if frequency byte is out of range)
if (frequency > 3) frequency = 3;
// read control byte in, but zero out current state of RS2 and RS1.
byte temp_buffer = readControlByte(0) & 0b11100111;
if (battery) {
// turn on BBSQW flag
temp_buffer = temp_buffer | 0b01000000;
} else {
// turn off BBSQW flag
temp_buffer = temp_buffer & 0b10111111;
}
if (TF) {
// set ~EOSC to 0 and INTCN to zero.
temp_buffer = temp_buffer & 0b01111011;
} else {
// set ~EOSC to 1, leave INTCN as is.
temp_buffer = temp_buffer | 0b10000000;
}
// shift frequency into bits 3 and 4 and set.
frequency = frequency << 3;
temp_buffer = temp_buffer | frequency;
// And write the control bits
writeControlByte(temp_buffer, 0);
}
void DS3231::enable32kHz(bool TF) {
// turn 32kHz pin on or off
byte temp_buffer = readControlByte(1);
if (TF) {
// turn on 32kHz pin
temp_buffer = temp_buffer | 0b00001000;
} else {
// turn off 32kHz pin
temp_buffer = temp_buffer & 0b11110111;
}
writeControlByte(temp_buffer, 1);
}
bool DS3231::oscillatorCheck() {
// Returns false if the oscillator has been off for some reason.
// If this is the case, the time is probably not correct.
byte temp_buffer = readControlByte(1);
bool result = true;
if (temp_buffer & 0b10000000) {
// Oscillator Stop Flag (OSF) is set, so return false.
result = false;
}
return result;
}
/*****************************************
Private Functions
*****************************************/
byte DS3231::decToBcd(byte val) {
// Convert normal decimal numbers to binary coded decimal
return ( (val/10*16) + (val%10) );
}
byte DS3231::bcdToDec(byte val) {
// Convert binary coded decimal to normal decimal numbers
return ( (val/16*10) + (val%16) );
}
byte DS3231::readControlByte(bool which) {
// Read selected control byte
// first byte (0) is 0x0e, second (1) is 0x0f
_Wire.beginTransmission(CLOCK_ADDRESS);
if (which) {
// second control byte
_Wire.write(0x0f);
} else {
// first control byte
_Wire.write(0x0e);
}
_Wire.endTransmission();
_Wire.requestFrom(CLOCK_ADDRESS, 1);
return _Wire.read();
}
void DS3231::writeControlByte(byte control, bool which) {
// Write the selected control byte.
// which=false -> 0x0e, true->0x0f.
_Wire.beginTransmission(CLOCK_ADDRESS);
if (which) {
_Wire.write(0x0f);
} else {
_Wire.write(0x0e);
}
_Wire.write(control);
_Wire.endTransmission();
}

213
libraries/DS3231/DS3231.h Normal file
View File

@@ -0,0 +1,213 @@
/*
* DS3231.h
*
* Arduino Library for the DS3231 Real-Time Clock chip
*
* (c) Eric Ayars
* 4/1/11
* released into the public domain. If you use this, please let me know
* (just out of pure curiosity!) by sending me an email:
* eric@ayars.org
*
* Changed the parameter type in isleapYear() to uint16_t from uint8_t
* for two reasons: the function uses 16-bit arithmetic, i.e., (y % 400); and
* one of the calling functions sends a 16-bit parameter.
* David Sparks
* 08 Sept 2022
*
*/
// Modified by Andy Wickert 5/15/11: Spliced in stuff from RTClib
// Modified by Simon Gassner 11/28/2017: Changed Term "PM" to "PM_time" for compability with SAMD Processors
#ifndef DS3231_h
#define DS3231_h
// Changed the following to work on 1.0
//#include "WProgram.h"
#include <Arduino.h>
#include <time.h>
#include <Wire.h>
// DateTime (get everything at once) from JeeLabs / Adafruit
// Simple general-purpose date/time class (no TZ / DST / leap second handling!)
class DateTime {
public:
DateTime (uint32_t t =0);
DateTime (uint16_t year, uint8_t month, uint8_t day,
uint8_t hour =0, uint8_t min =0, uint8_t sec =0);
DateTime (const char* date, const char* time);
uint16_t year() const { return 2000 + yOff; }
uint8_t month() const { return m; }
uint8_t day() const { return d; }
uint8_t hour() const { return hh; }
uint8_t minute() const { return mm; }
uint8_t second() const { return ss; }
uint8_t dayOfTheWeek() const;
// 32-bit times as seconds since 1/1/2000
long secondstime() const;
// 32-bit times as seconds since 1/1/1970
// THE ABOVE COMMENT IS CORRECT FOR LOCAL TIME; TO USE THIS COMMAND TO
// OBTAIN TRUE UNIX TIME SINCE EPOCH, YOU MUST CALL THIS COMMAND AFTER
// SETTING YOUR CLOCK TO UTC
uint32_t unixtime(void) const;
protected:
uint8_t yOff, m, d, hh, mm, ss;
};
// Checks if a year is a leap year
bool isleapYear(const uint16_t);
class RTClib {
public:
// Get date and time snapshot
static DateTime now(TwoWire & _Wire = Wire);
};
// Eric's original code is everything below this line
class DS3231 {
public:
//Constructor
DS3231();
DS3231(TwoWire & w);
TwoWire & _Wire;
// Time-retrieval functions
// the get*() functions retrieve current values of the registers.
byte getSecond();
byte getMinute();
byte getHour(bool& h12, bool& PM_time);
// In addition to returning the hour register, this function
// returns the values of the 12/24-hour flag and the AM/PM flag.
byte getDoW();
byte getDate();
byte getMonth(bool& Century);
// Also sets the flag indicating century roll-over.
byte getYear();
// Last 2 digits only
// Time-setting functions
// Note that none of these check for sensibility: You can set the
// date to July 42nd and strange things will probably result.
// set epoch function gives the epoch as parameter and feeds the RTC
// epoch = UnixTime and starts at 01.01.1970 00:00:00
void setEpoch(time_t epoch = 0, bool flag_localtime = false);
void setSecond(byte Second);
// In addition to setting the seconds, this clears the
// "Oscillator Stop Flag".
void setMinute(byte Minute);
// Sets the minute
void setHour(byte Hour);
// Sets the hour
void setDoW(byte DoW);
// Sets the Day of the Week (1-7);
void setDate(byte Date);
// Sets the Date of the Month
void setMonth(byte Month);
// Sets the Month of the year
void setYear(byte Year);
// Last two digits of the year
void setClockMode(bool h12);
// Set 12/24h mode. True is 12-h, false is 24-hour.
// Temperature function
float getTemperature();
// Alarm functions
void getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM);
/* Retrieves everything you could want to know about alarm
* one.
* A1Dy true makes the alarm go on A1Day = Day of Week,
* A1Dy false makes the alarm go on A1Day = Date of month.
*
* byte AlarmBits sets the behavior of the alarms:
* Dy A1M4 A1M3 A1M2 A1M1 Rate
* X 1 1 1 1 Once per second
* X 1 1 1 0 Alarm when seconds match
* X 1 1 0 0 Alarm when min, sec match
* X 1 0 0 0 Alarm when hour, min, sec match
* 0 0 0 0 0 Alarm when date, h, m, s match
* 1 0 0 0 0 Alarm when DoW, h, m, s match
*
* Dy A2M4 A2M3 A2M2 Rate
* X 1 1 1 Once per minute (at seconds = 00)
* X 1 1 0 Alarm when minutes match
* X 1 0 0 Alarm when hours and minutes match
* 0 0 0 0 Alarm when date, hour, min match
* 1 0 0 0 Alarm when DoW, hour, min match
*
* Note: byte AlarmBits is not explicitly cleared for the getAXTime methods to
* support sequential retreival of both alarms with the same byte AlarmBits.
* Use the flag bool clearAlarmBits=True to explicitly clear byte AlarmBits on
* call to getAXTime.
*/
void getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM);
// Same as getA1Time();, but A2 only goes on seconds == 00.
void getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM, bool clearAlarmBits);
// Same as getA1Time();, but clears byte AlarmBits.
void getA2Time(byte& A1Day, byte& A1Hour, byte& A1Minute,byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM, bool clearAlarmBits);
// Same as getA1Time();, but clears byte AlarmBits.
void setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM);
// Set the details for Alarm 1
void setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM);
// Set the details for Alarm 2
void turnOnAlarm(byte Alarm);
// Enables alarm 1 or 2 and the external interrupt pin.
// If Alarm != 1, it assumes Alarm == 2.
void turnOffAlarm(byte Alarm);
// Disables alarm 1 or 2 (default is 2 if Alarm != 1);
// and leaves the interrupt pin alone.
bool checkAlarmEnabled(byte Alarm);
// Returns T/F to indicate whether the requested alarm is
// enabled. Defaults to 2 if Alarm != 1.
bool checkIfAlarm(byte Alarm);
// Checks whether the indicated alarm (1 or 2, 2 default);
// has been activated. Always clears flag.
bool checkIfAlarm(byte Alarm, bool clearflag);
// Checks whether the indicated alarm (1 or 2, 2 default);
// has been activated. IF clearflag is set, clears alarm flag.
// Oscillator functions
void enableOscillator(bool TF, bool battery, byte frequency);
// turns oscillator on or off. True is on, false is off.
// if battery is true, turns on even for battery-only operation,
// otherwise turns off if Vcc is off.
// frequency must be 0, 1, 2, or 3.
// 0 = 1 Hz
// 1 = 1.024 kHz
// 2 = 4.096 kHz
// 3 = 8.192 kHz (Default if frequency byte is out of range);
void enable32kHz(bool TF);
// Turns the 32kHz output pin on (true); or off (false).
bool oscillatorCheck();;
// Checks the status of the OSF (Oscillator Stop Flag);.
// If this returns false, then the clock is probably not
// giving you the correct time.
// The OSF is cleared by function setSecond();.
private:
byte decToBcd(byte val);
// Convert normal decimal numbers to binary coded decimal
byte bcdToDec(byte val);
// Convert binary coded decimal to normal decimal numbers
protected:
byte readControlByte(bool which);
// Read selected control byte: (0); reads 0x0e, (1) reads 0x0f
void writeControlByte(byte control, bool which);
// Write the selected control byte.
// which == false -> 0x0e, true->0x0f.
};
#endif

View File

@@ -0,0 +1,761 @@
#DS3231 Library
## Functions for Alarms
The DS3231 offers two, versatile, independent alarms.
Every second, a DS3231 updates the values in its "time" registers: second, minute, hour, day, date, month and year.
The hardware then instantly compares the new time values to corresponding values stored in the two alarm registers: second, minute, hour, and either day or date depending on a certain alarm setting.
When the values match for one of the alarms, the hardware writes a "flag" bit for that alarm to HIGH, that is, to logic 1.
What happens after that flag bit goes HIGH is... anything the developer can imagine!
This Library provides functions for setting, managing and detecting the alarms.
Optionally, the DS3231 can be configured to interrupt the Arduino when an alarm occurs. An Arduino program can execute specific instructions immediately upon receiving the interrupt.
Interrupts from a DS3231 enable an Arduino to perform time-sensitive tasks very accurately, without the need for any ```delay()``` statements, polling or timing loops running on the Arduino!
An [example program](https://github.com/NorthernWidget/DS3231/tree/master/examples/AlarmInterrupt) demonstrates using an alarm-triggered interrupt to blink an LED.
## Contents
* [Arduino Code Requirements](#arduino-code-requirements)
* [Alarm Bits Quick Reference](#alarm-bits-quick-reference)
* [The DS3231 Library Alarm Functions](#alarm-functions)
* [getA1Time()](#get-a1-time)
* [getA1Time() with Option](#get-a1-time-option)
* [getA2Time()](#get-a2-time)
* [getA2Time() with Option](#get-a2-time-option)
* [setA1Time()](#set-a1-time)
* [setA2Time()](#set-a2-time)
* [turnOnAlarm()](#turn-on-alarm)
* [turnOffAlarm()](#turn-off-alarm)
* [checkAlarmEnabled()](#check-alarm-enabled)
* [checkIfAlarm()](#check-if-alarm)
* [checkIfAlarm() with Option](#check-if-alarm-option)
* [Alarm Bits in Detail](#alarm-bits-in-detail)
* [How to Advance an Alarm Time](#how-to-advance-an-alarm-time)
* [How (and Why) to Prevent an Alarm Entirely](#prevent-alarm)
## Arduino Code Requirements
A program needs certain software resources to work with DS3231 alarms.
This includes a set of variables that are required in the parameter lists by some of the alarm methods. The listing below identifies the resources and suggests descriptive names a programmer could choose for the variables.
All of the code examples in this article will use the variables by these names in parameter lists, including those of functions that could otherwise use numeric constants.
```
#include <DS3231.h> // import this Library
DS3231 myRTC; // declare an object for access to the alarm methods
// declare variables to use with the alarm methods
// for the hour of an alarm
bool alarmH12; // (true) if hour is 1 - 12, (false) if hour is 00 - 23
byte alarmHour; // 1-12 if 12-hour mode, 0-23 if 24-hour mode
bool alarmPM; // (true) if 12-hour time is PM, (false) if AM
byte alarmMinute; // 00 - 59
byte alarmSecond; // 00 - 59
// for the day or date of an alarm
byte alarmDay; // 1-7 if day of week, 1-31 if date in month
bool alarmIsDay; // (true) if alarmDay is a day of the week, (false) if date in month
// for the frequency of an alarm
byte alarmBits; // a bitfield, to be explained below
void setup() {
Wire.begin(); // establish I2C communications
}
```
The following code fragment illustrates setting Alarm 1 to activate at 12:00 noon once per week, on Mondays. It makes use of the software resources described above.
```
// assign values to program variables
alarmH12 = true; // use 12-hour time mode
alarmHour = 12; // ambiguous value in 12-hour time mode
alarmPM = true; // determines that 12 means "noon"
alarmMinute = 0;
alarmSecond = 0;
// The following assignment will be correct only if
// the programmer determined 1 to mean Sunday
// when setting the time in the DS3231 clock registers.
alarmDay = 2; // 2 = Monday
// alarmDay to be interpreted as day in the week, not as date in the month
alarmIsDay = true;
alarmBits = 0b00000000; // alarm when day, hours, minutes and seconds match the time
// write to the DS3231 time registers for Alarm 1
myRTC.setA1Time(alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits, alarmIsDay, alarmH12, alarmPM);
```
[Back to Contents](#contents)
## Alarm Bits Quick Reference
Two of the alarm values shown in the foregoing example combine to determine the frequency of an alarm:
* alarmIsDay, and
* alarmBits.
In total, eleven different combinations of values exist for these two parameters. The following table lists the combinations and explains the result that each combination produces.
| |alarmIsDay | alarmBits |Explanation|
|--:|:------:|--------|-----------|
|1| (ignored) | 0b00001111 | Alarm 1 every second. All other settings for Alarm 1 are ignored.|
|2| (ignored) | 0b00001110 | Alarm 1 every minute, when the Alarm 1 seconds value matches the seconds value of the time. All other settings for Alarm 1 are ignored.|
|3| (ignored) | 0b00001100 | Alarm 1 every hour, when the Alarm 1 minutes and seconds values match those of the time. All other settings for Alarm 1 are ignored.|
|4| (ignored) | 0b00001000 | Alarm 1 every day, when the Alarm 1 hours, minutes and seconds values match those of the time. The time modes, 24- or 12-hour and AM/PM, are evaluated.|
|5| True<br>(Day of Week) | 0b00000000 | Alarm 1 every week, when the Alarm 1 day, hours, minutes and seconds values match those of the time. The time modes, 24- or 12-hour and AM/PM, are evaluated.|
|6| False<br>(Date in Month) | 0b00000000 | Alarm 1 every month, when the Alarm 1 date, hours, minutes and seconds values match those of the time. The time modes, 24- or 12-hour and AM/PM, are evaluated.|
|7| (ignored) | 0b01110000 | Alarm 2 every minute, when the seconds value of the time equals zero. All other settings for Alarm 2 are ignored.|
|8| (ignored) | 0b01100000 | Alarm 2 every hour, when the Alarm 2 minutes value matches that of the time and the time seconds value equals zero. All other settings for Alarm 2 are ignored.|
|9| (ignored) | 0b01000000 | Alarm 2 every day, when the Alarm 2 hours and minutes values match those of the time and the time seconds value equals zero. The time modes, 24- or 12-hour and AM/PM, are evaluated.|
|10| True<br>(Day in Week) | 0b00000000 | Alarm 2 every week, when the Alarm 2 day, hours and minutes values match those of the time and the time seconds value equals zero. The time modes, 24- or 12-hour and AM/PM, are evaluated.|
|11| False<br>(Date in Month) | 0b00000000 | Alarm 2 every month, when the Alarm 2 date, hours and minutes values match those of the time and the time seconds value equals zero. The time modes, 24- or 12-hour and AM/PM, are evaluated.|
Notice that combinations 5 and 6 appear identical to 11 and 12. Fortunately, the methods for setting Alarm 1 and Alarm 2 prevent confusion. Each method sets values for just one of the alarms.
[Back to Contents](#contents)
## Alarm Functions
The illustrations in this section assume that the program has already declared a DS3231 object named ```myRTC``. For example:
```DS3231 myRTC; // create a DS3231 object```
---
### <a id="get-a1-time">getA1Time()</a>
```
/*
* Retrieves values from the Alarm 1 time registers then stores the values
* in the external variables passed to the parameter list.
* NOTE: previous values of the parameter variables are over-written.
* returns: nothing (void)
* parameters (all of these are references to external variables):
* byte& A1Day, // "byte&" refers to a byte variable
* byte& A1Hour, //
* byte& A1Minute, //
* byte& A1Second, //
* byte& AlarmBits, //
* bool& A1Dy, // "bool&" refers to a boolean variable
* bool& A1h12, //
* bool& A1PM //
*/
void DS3231::getA1Time(
byte& A1Day, byte& A1Hour, byte& A1Minute,
byte& A1Second, byte& AlarmBits, bool& A1Dy,
bool& A1h12, bool& A1PM);
```
The variables passed in the parameter list must match the types of the parameters but need not match the names.
[Back to Contents](#contents)
---
### <a id="get-a1-time-option">getA1Time() with Option</a>
```
/*
* Retrieves values from the Alarm 1 time registers then stores the values
* in the external variables passed to the parameter list.
* NOTE: previous values of the parameter variables are over-written.
* Overloads getA1Time() to include clearing the AlarmBits parameter variable
* returns: nothing (void)
* parameters (the following are references to external variables):
* byte& A1Day, // "byte&" refers to a byte variable
* byte& A1Hour, //
* byte& A1Minute, //
* byte& A1Second, //
* byte& AlarmBits, //
* bool& A1Dy, // "bool&" refers to a boolean variable
* bool& A1h12, //
* bool& A1PM, //
* // the additional parameter is passed by value,
* // meaning that a literal true or false keyword may be used
* bool clearAlarmBits // (true) = clear the AlarmBits parameter
*/
void DS3231::getA1Time(
byte& A1Day, byte& A1Hour, byte& A1Minute,
byte& A1Second, byte& AlarmBits, bool& A1Dy,
bool& A1h12, bool& A1PM, bool clearAlarmBits);
```
The variables passed in the parameter list must match the types of the parameters but need not match the names.
#### Managing the Retrieved AlarmBits Value
Remember that the variable chosen to receiving the AlarmBits values is an 8-bit "bitfield".
* The bits for alarm 1 occupy positions 0-3 in the bitfield.
* Those for alarm 2 are found in positions 4-6. Bit 7 has no meaning.
Each of the alarm "get" functions addresses only the bits *for that alarm*. This means that the variable receiving the AlarmBits value will wind up containing:
* the actual bits for the targeted alarm in one-half of the field, plus
* any leftover bits that might have been lingering in the other half.
Any one of the following alternative approaches can guard against irrelevant bits cluttering the AlarmBits variable. The illustrations assume the variable holding the bitfield is named "AlarmBits".
Approach 1. *After* calling "get", use the bitwise & operator to distill only the desired bits.
* Alarm 1: ```AlarmBits &= 0b00001111;```
* Alarm 2: ```AlarmBits &= 0b01110000;```
Approach 2. *Before* calling "get", clear the bitfield by assigning 0 to the variable.
* ```AlarmBits = 0;```
Approach 3. Set the optional "clearAlarmBits" boolean parameter in the "get" function to "true".
* "true" tells the "get" function to perform Approach 2 internally.
* "false" or omitted leaves management of the bitfield in the hands of the program writer.
[Back to Contents](#contents)
---
### <a id="get-a2-time">getA2Time()</a>
```
/*
* Retrieves values from the Alarm 2 time registers then stores the values
* in the external variables passed to the parameter list.
* NOTE: previous values of the parameter variables are over-written.
* returns: nothing (void)
* parameters (all of these are references to external variables):
* byte& A2Day, // "byte&" refers to a byte variable
* byte& A2Hour, //
* byte& A2Minute, //
* byte& AlarmBits, //
* bool& A2Dy, // "bool&" refers to a boolean variable
* bool& A2h12, //
* bool& A2PM //
*/
void DS3231::getA2Time(
byte& A2Day, byte& A2Hour, byte& A2Minute,
byte& A2Second, byte& AlarmBits, bool& A2Dy,
bool& A2h12, bool& A2PM);
```
The variables passed in the parameter list must match the types of the parameters but need not match the names.
[Back to Contents](#contents)
---
### <a id="get-a2-time-option">getA2Time() with Option</a>
```
/*
* Retrieves values from the Alarm 2 time registers then stores the values
* in the external variables passed to the parameter list.
* NOTE: previous values of the parameter variables are over-written.
* Overloads getA2Time() to include clearing the AlarmBits parameter variable
* returns: nothing (void)
* parameters (the following are references to external variables):
* byte& A2Day, // "byte&" refers to a byte variable
* byte& A2Hour, //
* byte& A2Minute, //
* byte& A2Second, //
* byte& AlarmBits, //
* bool& A2Dy, // "bool&" refers to a boolean variable
* bool& A2h12, //
* bool& A2PM, //
* // the additional parameter is passed by value,
* // meaning that a literal true or false keyword may be used
* bool clearAlarmBits // (true) = clear the AlarmBits parameter
*/
void DS3231::getA1Time(
byte& A2Day, byte& A2Hour, byte& A2Minute,
byte& A2Second, byte& AlarmBits, bool& A2Dy,
bool& A2h12, bool& A2PM, bool clearAlarmBits);
```
The variables passed in the parameter list must match the types of the parameters but need not match the names.
See the discussion of [Managing the Retrieved AlarmBits Value](#managing-the-retrieved-alarmbits-value), above, for more information about the purpose and usage of this optional method.
[Back to Contents](#contents)
---
### <a id="set-a1-time">setA1Time()</a>
```
/*
* transfers the parameter values to Alarm 1 registers inside the DS3231
* returns: nothing (void)
* parameters:
* byte A1Day,
* byte A1Hour,
* byte A1Minute,
* byte A1Second
* byte AlarmBits,
* bool A1Dy,
* bool A1h12,
* bool A1PM
*
* depends upon I2C connection to DS3231 but does not validate it
*/
void DS3231::setA1Time(
byte A1Day, byte A1Hour, byte A1Minute,
byte A1Second, byte AlarmBits, bool A1Dy,
bool A1h12, bool A1PM);
```
Alarm 1 has a parameter for seconds of time. By contrast, Alarm 2 (below) does not.
[Back to Contents](#contents)
### <a id="set-a2-time">setA2Time()</a>
```
/*
* transfers the values to Alarm 2 registers inside the DS3231
* returns: nothing (void)
* parameters:
* byte A2Day,
* byte A2Hour,
* byte A2Minute,
* byte AlarmBits,
* bool A2Dy,
* bool A2h12,
* bool A2PM
*/
void DS3231::setA2Time(
byte A2Day, byte A2Hour, byte A2Minute,
byte AlarmBits, bool A2Dy, bool A2h12,
bool A2PM);
```
Alarm 2 does not have a parameter for seconds of time. By contrast, Alarm 1 (above) does.
[Back to Contents](#contents)
---
### <a id="turn-on-alarm">turnOnAlarm()</a>
```
/*
* Modifies control register 0x0E of DS3231 to enable the chosen alarm interrupt
* returns: nothing (void)
* one parameter:
* Alarm, 1 or 2, determines which alarm interrupt to enable
*/
void turnOnAlarm(byte Alarm);
```
* Enables the interrupt for alarm 1 if and only if the Alarm parameter is equal to 1.
* For all other values of the Alarm parameter, it sets the interrupt for alarm 2.
* The Alarm parameter is required.
Consider clearing both of the alarms' flags after enabling an interrupt, even if only one alarm is in use.
The reason is that the DS3231 cannot signal an interrupt if *either one of the flags* is set HIGH, that is, equal to logic 1.
See the discussion of [checkIfAlarm()](#check-if-alarm), below.
```
myRTC.turnOnAlarm(1); // enable alarm 1 interrupt
myRTC.checkIfAlarm(1); // clear alarm 1 flag
myRTC.checkIfAlarm(2); // better yet, clear both of the flags
```
[Back to Contents](#contents)
---
### <a id="turn-off-alarm">turnOffAlarm()</a>
```
/*
* Modifies control register 0x0E of DS3231 to disable the chosen alarm interrupt
* returns: nothing (void)
* one parameter:
* Alarm, 1 or 2, determines which alarm interrupt to disable
*/
void turnOffAlarm(byte Alarm) ;
```
* Disables the interrupt for alarm 1 if and only if the Alarm parameter is equal to 1.
* For all other values of the Alarm parameter, it disables the interrupt for alarm 2.
* The Alarm parameter is required.
Disabling interrupts for one of the alarms has no effect on the other alarm.
[Back to Contents](#contents)
---
### <a id="check-alarm-enabled">checkAlarmEnabled()</a>
```
/*
* Retrieves the interrupt status bit of the chosen alarm.
* returns: boolean,
* true = bit is set (enabled),
* false = bit is clear (disabled)
* one parameter:
* Alarm, 1 or 2, determines which alarm to check
*/
bool checkAlarmEnabled(byte Alarm);
```
[Back to Contents](#contents)
---
### <a id="check-if-alarm">checkIfAlarm()</a>
```
/*
* Retrieves the value of the alarm flag for the chosen alarm
* returns: boolean,
* (true) if the flag is set = logic 1,
* (false) if the flag is clear = logic 0
* only one parameter:
* Alarm, 1 or 2, determines which alarm flag to examine
* side effect:
* Clears the alarm flag after examining it, also.
*/
bool checkIfAlarm(byte Alarm);
```
NOTE TO PROGRAMMERS: this version of the method always overwrites the value of the alarm flag to zero. An alternate version, below, allows programs an option to preserve the value of the flag in the DS3231.
It may be desirable to do so in programs that re-use a single alarm repeatedly.
The flag value is returned to the program, where it may be assigned to a program variable for later evaluation and use.
Keep two things in mind:
1. The alarm flag *must* be reset to zero (cleared) by the program before a subsequent alarm event can be detected.
2. This method is the only one in the Library that can clear an alarm flag with certainty.
[Back to Contents](#contents)
---
### <a id="check-if-alarm-option">checkIfAlarm() with Option</a>
```
/*
* Retrieves the value of the alarm flag for the chosen alarm.
* Adds a parameter allowing the alarm flag to be preserved in the DS3231.
* returns: boolean,
* (true) if the flag is set = logic 1,
* (false) if the flag is clear = logic 0
* two parameters:
* Alarm, 1 or 2, determines which alarm flag to examine
* clearFlag, determines whether to clear the alarm flag
* (true) clears the flag
* (false) preserves the value of the flag in the DS3231
* side effect:
* Clears the alarm flag only if the clearFlag parameter is equal to true
*/
bool checkIfAlarm(byte Alarm, bool clearFlag);
```
This version of the method may be preferable when both alarms are in use, with interrupts enabled, and the program desires to preserve the flags in the hardware temporarily.
Version 2 performs exactly like Version 1, above, clearing the flag after checking, when the "clearFlag" parameter is equal to "true".
Note that this version cannot guarantee to clear the alarm flag automatically, because it will not clear the flag if the program passes a value of "false" to the second parameter.
[Back to Contents](#contents)
---
## Alarm Bits in Detail
Here is more about the magic of DS3231 alarms: the alarmBits parameter. It needs careful understanding and rewards attentive study.
It is a *bitfield*, that is, an array of individual bits, where each bit has a certain, definite meaning.
Actually, the AlarmBits parameter should be understood as a *pair of bitfields*. The least significant four bits, 0 through 3, regulate Alarm 1, while bits 4-6 apply to Alarm 2. Bit 7 is disregarded.
The parameter looks like an 8-bit integer but has no practical meaning as such. Instead, *each, individual bit* enables or disables a specific feature of an alarm in the DS3231.
The following table illustrates the arrangement of the two bitfields in the AlarmBits parameter.
|Bit7|Alarm 2|Alarm 1|
|----|-------|-------|
| X | bits 6:4 | bits 3:0 |
**Important, for Alarm 2**: locate the alarm bits in positions 6, 5 and 4 of the AlarmBits parameter for the functions that set and retrieve values for Alarm 2. This is addressed in greater detail below.
#### Locating the Alarm Bits
It will be helpful to name the seven bits. Inside the DS3231, each bit is stored separately, as Bit 7 in each of the 8-bit data registers storing values for the alarm.
##### Alarm 1
|Register|Bit7|Bits 6:0|
|--------|----|--------|
| 0x07 | A1M1 |Second|
| 0x08 | A1M2 |Minute|
| 0x09 | A1M3 |Hour|
| 0x0A | A1M4 |Day|
##### Alarm 2
|Register|Bit7|Bits 6:0|
|--------|----|--------|
| 0x0B | A2M2 |Minute|
| 0x0C | A2M3 |Hour|
| 0x0D | A2M4 |Day|
By contrast, the bits are stored adjacently in the parameter. It is very convenient for programming purposes to arrange them into a single byte this way.
Note again that Bit 7 *in the parameter* is marked "X", which means it is ignored.
##### alarmBits Parameter Map
|7|6|5|4|3|2|1|0|
|----|----|----|----|----|----|----|----|
| X |A2M4|A2M3|A2M2|A1M4|A1M3|A1M2|A1M1|
Clearly, the alarm setting and retrieving functions perform an elaborate but beneficial operation with these bits. They translate the bits from adjacent positions in a bitfield (the AlarmBits parameter) to separate registers in the DS3231, and vice versa.
#### Alarm Bits Determine Alarm Rate
The bits combine with the *alarmIsDay* parameter to form a "mask" that governs the Rate of an alarm. That is, how often, on which occasions, an alarm will occur.
The following table is adapted from Table 2 on page 12 of the [data sheet](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf). The column labeled *DY/DT* reflects the boolean value of the *alarmIsDay* parameter listed above. An "X" means the value of DY/DT is ignored.
##### Alarm 1
<table>
<tr> <!-- Top header row -->
<th rowspan="2">DY/DT</th>
<th colspan="4">Alarm 1 Mask Bits</th>
<th rowspan="2">Alarm Rate</th>
</tr>
<tr> <!-- Second header row -->
<th>A1M4</th>
<th>A1M3</th>
<th>A1M2</th>
<th>A1M1</th>
</tr>
<tr> <!-- Data row -->
<td>X</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>Alarm once per second</td>
</tr>
<tr> <!-- Data row -->
<td>X</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>Alarm when seconds match</td>
</tr>
<tr> <!-- Data row -->
<td>X</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>Alarm when minutes and seconds match</td>
</tr>
<tr> <!-- Data row -->
<td>X</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>Alarm when hours, minutes and seconds match</td>
</tr>
<tr> <!-- Data row -->
<td style="text-style:italic;">true</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>Alarm when day, hours, minutes and seconds match</td>
</tr>
<tr> <!-- Data row -->
<td style="text-style:italic;">false</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>Alarm when date, hours, minutes and seconds match</td>
</tr>
</table>
##### Alarm 2
<table>
<tr> <!-- Top header row -->
<th rowspan="2">DY/DT</th>
<th colspan="3">Alarm 2 Mask Bits</th>
<th rowspan="2">Alarm Rate</th>
</tr>
<tr> <!-- Second header row -->
<th>A2M4</th>
<th>A2M3</th>
<th>A2M2</th>
</tr>
<tr> <!-- Data row -->
<td>X</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>Alarm when seconds == 0 (once per minute) </td>
</tr>
<tr> <!-- Data row -->
<td>X</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>Alarm when minutes match</td>
</tr>
<tr> <!-- Data row -->
<td>X</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>Alarm when hours and minutes match</td>
</tr>
<tr> <!-- Data row -->
<td style="text-style:italic;">true</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>Alarm when day, hours and minutes match</td>
</tr>
<tr> <!-- Data row -->
<td style="text-style:italic;">false</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>Alarm when date, hours and minutes match</td>
</tr>
</table>
The binary number format might be the most clear way to spell out a constant value for the AlarmBits parameter. The following example would prepare a parameter writing bits 6 and 5 to logic 1, and bit 4 to logic 0, so that Alarm 2 would occur when the minutes value of the time first matches that of the alarm time. It will be used in the example for advancing the alarm time, in the next section.
```byte AlarmBits = 0b01100000; // alarm when minutes match```
[Back to Contents](#contents)
## How to Advance an Alarm Time
**Problem**: your program needs to execute a procedure at precise, ten-minute intervals. You would like to have the DS3231 generate an interrupt every ten minutes. The DS3231 hardware supports automatically repeating interrupts at intervals of one second, one minute, one hour, one day, one week or one month, but not ten minutes.
**Solution**: write program code to advance the alarm time by ten minutes after an alarm interrupt is received.
The code fragment listed below illustrates an approach that could be used for the purpose. It makes use of the versatile DateTime class and other resources that accompany the DS3231 class in this Library.
Please keep two thoughts in mind.
1. Interrupts are automatically disabled globally by AVR chip hardware when entering an Interrupt Service Routine (ISR).
2. DS3231 class methods use I2C for communications with the DS3231. I2C requires interrupts. For best results, code that needs to communicate with a DS3231 should not be executed while an ISR is running.
For this example to work, the ISR could be as short as one instruction: simply set a designated global boolean variable "true". The change can notify the idle process (called "loop()" in Arduino code) to execute the following code fragment in response to the interrupt.
```
// capture the time the alarm occurred
DateTime alarmTime = RTClib::now();
// disable Alarm 2 interrupt output
myRTC.turnOffAlarm(2);
// We need to clear both of the alarm flags
// before DS3231 can output another alarm interrupt.
// Capture Alarm 1 flag for later assessment
// and automatically clear Alarm 1 flag
bool alarm1Flag = myRTC.checkIfAlarm(1);
// Clear Alarm 2 flag. No need to retain its value.
myRTC.checkIfAlarm(2);
// prepare parameter values for setting a new alarm time
alarmBits = 0b01100000; // Alarm 2 when minutes match
alarmH12 = false; // interpret hour in 24-hour mode
alarmPM = false; // irrelevant in 24-hour mode, but it needs a value
alarmIsDay = false; // interpret "day" value as a date in the month
// add 600 seconds (10 minutes)
uint32_t nextAlarm = alarmTime.unixtime() + 600;
// update values in the DateTime
alarmTime = DateTime(nextAlarm);
// Set the next time for Alarm 2.
// Note that only the "minutes" value is significant,
// yet we must supply the day and hour also,
// and it does no harm to supply real ones.
myRTC.setA2Time (
// get these values from the DateTime object
alarmTime.day(), // from a DateTime, will be date of the month
alarmTime.hour(), // from a DateTime, will be in 24-hour format
alarmTime.minute(),
// these were assigned, above, by the program
alarmBits,
alarmIsDay,
alarmH12,
alarmPM
);
// enable Alarm 2 interrupt output
myRTC.turnOnAlarm(2);
```
An [example program](https://github.com/NorthernWidget/DS3231/tree/master/examples/AdvanceAlarm) demonstrates using an alarm-triggered interrupt to blink an LED and print a message to the screen at 3-second intervals.
[Back to Contents](#contents)
## <a id="prevent-alarm">How (and Why) to Prevent an Alarm Entirely</a>
An alarm that your program is not using can covertly block the output of an interrupt by the DS3231.
The unused alarm can produce this unwanted effect silently. All it takes is for a "match" to occur between the time registers and the settings in effect for the unused alarm.
When a match occurs, the DS3231 will write logic 1 to the flag bit for the unused alarm. While that flag remains at logic 1, it will prevent the DS3231 from sending out a FALLING interrupt signal.
It is not enough merely to clear the unused alarm flag. A better plan would be to prevent the flag from ever being raised in the first place.
One way to do it is to break a rule: assign a nonsensical value to an alarm time register. Select a value that cannot match the time. 255 (0xFF) will fit nicely in an 8-bit unsigned integer. The following code fragment uploads 0xFF to the A2Minute register.
Then it assigns the value 0b01100000 to the AlarmBits parameter, which means to activate Alarm 2 when the alarm minutes value matches that of the time. The match will never occur because the minutes value of time will not exceed 59.
```
// these parameters need to be provided
// but their values will not be evaluated
alarmDay = 1;
alarmHour = 1;
alarmDayIsDay = false;
alarmH12 = false;
alarmPM = false
// these parameters may prevent Alarm 2 from activating
alarmMinute = 0xFF; // a value that will never match the time
alarmBits = 0b01100000; // Alarm 2 when minutes match, in this case, never
// Upload the parameters to prevent Alarm 2 entirely
myRTC.setA2Time(
alarmDay, alarmHour, alarmMinute,
alarmBits, alarmDayIsDay, alarmH12, alarmPM);
// disable Alarm 2 interrupt
myRTC.turnOffAlarm(2);
// clear Alarm 2 flag
myRTC.checkIfAlarm(2);
```
[Back to Contents](#contents)

View File

@@ -0,0 +1,196 @@
# DS3231 Library
## DateTime
DateTime is a C++ class included within this DS3231 Library. Program code can store and manipulate date and time information in DateTime variables.
Date and time information can be entered in a variety of formats, not only as numbers but also as specially formatted strings.
The library even provides a function to transfer date and time data from the DS3231 hardware into a DateTime object, where it can be accessed for subsequent analysis and calculations.
Methods (another name for Functions) of a DateTime variable enable programs to access date and time data in different number formats. The values for years, months, days, hours, minutes and seconds can be retrieved either individually or as a "timestamp", a large integer number of seconds, and vice versa.
Timestamps make it easy to do arithmetic with dates and times.
DateTime variables afford convenient data management and are easy to use. Even so, please keep one limitation in mind when writing them into your code:
---
*DateTime objects are defined in this Library to work with dates of 01 January 2000 through 31 December 2099, only*.
---
## Contents
* [DateTime As a Data Type](#datetime-as-a-data-type)
* [DateTime() As a Function ](#datetime-as-a-function)
* [Uses and Limitations of the Timestamp](#uses-and-limitations-of-the-timestamp)
## DateTime As a Data Type
Simply use the class name as the type to declare a DateTime variable, for example:
```DateTime myDT;```
Date and time data can be stored in a DateTime variable by adding a parameter list to the declaration. The parameter list will invoke one of the "constructor functions" to initialize the variable's date and time values.
The following examples initialize three ways to initialize a DateTime named ```myDT``` so that it contains 4:50:59 P.M. on Sep 08 2022 16:50:59.
### Example using strings
```DateTime myDT("Sep 08 2022", "16:50:59");```
The constructor function prototype is:
```DateTime(const char* date, const char* time);```
* Strings must be formatted as "Mmm dd yyyy" and "hh:mm:ss".
* Valid month abbreviations are: "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov" and "Dec".
* The year must be four characters long.
* Space delimiters are required in the date, as are the colon delimiters in the time.
* Hours in the time are given in 24-hour format, i.e., "00" through "23".
### Example using six integer values
```DateTime myDT(2022, 09, 08, 16, 50, 59);```
The constructor function prototype is:
```
DateTime (uint16_t year, uint8_t month, uint8_t day,
uint8_t hour =0, uint8_t min =0, uint8_t sec =0);
```
* The year, month and day parameters are required.
* The time parameters are optional and are given default values of zero (0).
* The value for the year can be entered in the range 2000 through 2099, or in the range 0 through 99.
* The year will be stored internally as an offset from 2000. In the example above, 22 would be stored.
* It is the programmer's responsibility to provide sensible values for all of the parameters.
### Example using a Unix-style timestamp
```DateTime myDT(1662655859);```
The constructor function prototype is:
```DateTime myDT(uint32_t t);```
Note that this constructor function will treat the timestamp fed into it as a Unix-style timestamp, that is, the number of seconds elapsed since midnight (time 00:00:00) the morning of January 1, 1970.
As the DateTime class is limited by design to the years 2000 through 2099, the range of unambiguous values for timestamps to be used with this constructor includes:
* minimum: 946684801 = 00:00:01 (a.m.) January 1, 2000
* maximum: 4102444799 = 23:59:59 (p.m.) December 31, 2099
However, a DateTime variable does not keep track of which time zone its data actually represent. It makes no allowance for Daylight Savings Time, either.
The program writer is responsible for adjusting the date and time information, if necessary to respect a certain time zone.
---
<p style="font-style: italic;">Regardless of the method used to create it, DateTime variables store the components of date and time internally as six, distinct, 8-bit, unsigned integers:</p>
<ul style="font-style: italic;">
<li>yOff : offset from year 2000, i.e., 00 through 99</li>
<li>m : month, 1 through 12</li>
<li>d : date of the month, 1 through 28, 29, 30 or 31, depending on the month and year</li>
<li>hh : hour, 00 through 23, in 24-hour format</li>
<li>mm : minute, 00 through 59</li>
<li>ss : second, 00 through 59</li>
</ul>
---
## DateTime() As a Function
Constructor functions are called automatically when a new variable is declared, as shown above.
Your code can call a constructor directly, also, when it wants to update the values stored in a DateTime variable. Simply set the old variable equal to the return value of the chosen constructor function.
For example, consider the variable named ```myDT``` that was declared in the preceding examples. It was initialized to contain a date of September 8. Here is how to change the date to September 10, while holding all the other values constant (by repeating their previous values):
```myDT = DateTime(2022, 09, 10, 16, 50, 59);```
Compare the parameter list above to the one used to declare the variable: (2022, 09, 08, 16, 50, 59).
Notice that changing even one date or time value in a DateTime requires providing relevant information about all of the values.
This Library provides a fourth function designed to transfer date and time information directly from a DS3231 device into a DateTime variable. Its prototype is:
```DateTime RTClib::now()```
The following example transfers data from a DS3231 device into the ```myDT``` variable, assuming the DS3231 is in communication with the program via I2C:
```myDT = RTClib::now();```
Look closely at the name of that function. Write it just that way in your code.
### Summary
---
*Use a constructor function to modify the values stored inside a DateTime variable.*
---
## Retrieving Date and Time Data
DateTime objects provide seven different methods for retrieving their information. None of the methods takes any parameters.
The function names and the types of values they return are listed below:
* ```uint16_t year(); // returns 2000 + yOff```
* ```uint8_t month();```
* ```uint8_t day();```
* ```uint8_t hour();```
* ```uint8_t minute();```
* ```uint8_t second();```
* ```uint32_t unixtime(); // returns the date and time in the format of a Unix Timestamp```
The data access methods listed above are invoked from a DateTime variable with the dot (".") operator. For example, the following code segment would print the time information contained within a DateTime object named ```myDT```.
```
Serial.print(myDT.hour());
Serial.print(":");
Serial.print(myDT.minute());
Serial.print(":");
Serial.println(myDT.second());
```
## Uses and Limitations of the Timestamp
### Uses
The timestamp returned by the *unixtime()* method of a DateTime is an unsigned, 32-bit integer. It can be very useful for adding or subtracting an interval of time measured in seconds.
Suppose you want to add one day to to an existing DateTime variable. It could be cumbersome to do this by adjusting first the date, then perhaps the month, then perhaps the year.
A better procedure might be:
1. retrieve the DateTime data as a timestamp,
2. add 86,400 seconds, then
3. use the timestamp constructor to update the variable.
The following code segment illustrates the procedure:
```
#define SECONDS_IN_ONE_DAY 86400
uint32_t timeStamp = myDT.unixtime();
timeStamp += SECONDS_IN_ONE_DAY;
myDT = DateTime(timeStamp);
```
### Limitations
DateTime variables do not maintain information about time zones.
The idea of a timestamp originated in the notion of a "system time" accumulated inside a computer running the Unix operating system. It was just an integer that began at zero when the computer started up. It would keep count of how many seconds the computer had been running. A timestamp is just the value of the system time at a given instant.
A "Unix Epoch" timestamp is like a system time that began at zero, exactly at 00:00:00 o'clock midnight on the morning of January 1, 1970 on the Prime Meridian of longitude, which runs through Greenwich, a suburb of London, England.
Hence, Unix Timestamps represent Greenwich Meridian Time, or GMT. Adjusting a Unix timestamp to other time zones involves adding or subtracting a suitable number of seconds.
DateTime relaxes that definition. It deals with *Unix-style* timestamps. That is, it follows the Unix algorithm for converting between dates/times and timestamps for GMT, but makes no adjustment for a time zone.
A timestamp retrieved by the *unixtime()* method of a DateTime variable is best understood as just a (rather large) number of seconds.
### Fun Facts about Unix Timestamps
Did you know that some systems using *signed 32-bit values* for Unix Timestamps may require re-configuration before the 21st Century grows much older? For entertaining reading, look up "the 2038 Problem."
Fortunately, the DateTime class defined in this Library uses *unsigned 32-bit values* for its timestamps. In this way it postpones the 2038 Problem beyond December 2099, perhaps even through the year 2137.
Keep in mind, however: the DateTime class defined in this Library is designed to work correctly only with dates between January 1, 2000 and December 31, 2099.

View File

@@ -0,0 +1,178 @@
# DS3231 Library
## Time Retrieval Functions
The following methods of the DS3231 object use I2C hardware connection and the Wire library to read data from certain registers in the DS3231. The Library assumes that the DS3231 has an I2C address of 0x68.
The DS3231 register addresses mentioned below are documented on page 11 of the [manufacturer's datasheet](https://datasheets.maximintegrated.com/en/ds/DS3231-DS3231S.pdf).
The examples provided below assume a variable has been declared as follows:
```DS3231 myRTC;```
<ul>
<li><a href="#getSecond">getSecond&#40;&#41;</a></li>
<li><a href="#getMinute">getMinute&#40;&#41;</a></li>
<li><a href="#getHour">getHour&#40;&#41;</a></li>
<li><a href="#getDow">getDoW&#40;&#41;</a></li>
<li><a href="#getDate">getDate&#40;&#41;</a></li>
<li><a href="#getMonth">getMonth&#40;&#41;</a></li>
<li><a href="#getYear">getYear&#40;&#41;</a></li>
<h3 id="getSecond">getSecond&#40;&#41;</h3>
```
/*
* returns: byte = 0 to 59
* parameters: none
* asserts: none
* side effects: none
* DS3231 register addressed: 0x00
*/
byte theSecond = myRTC.getSecond();
```
<h3 id="getMinute">getMinute&#40;&#41;</h3>
```
/*
* returns: byte = 0 to 59
* parameters: none
* asserts: none
* side effects: none
* DS3231 register addressed: 0x01
*/
byte theMinute = myRTC.getMinute();
```
<h3 id="getHour">getHour&#40;&#41;</h3>
```
/*
* returns: byte, value depending on mode settings in the DS3231
* either 1 to 12 (12-hour mode)
* or 0 to 23 (24-hour mode)
* parameters: two boolean variables, passed by reference
* parameter #1: 12/24-hour flag
* parameter #2: AM/PM flag
* Note: must provide the variable names, not constants
* asserts: none
*
* side effects:
* the two boolean parameters are set or cleared
* according to the state of the corresponding flags
* in the DS3231 hardware register
* 12/24 hour flag = true if DS3231 is in 12-hour mode
* AM/PM flag = false if AM, true if PM, in 12-hour mode
*
* DS3231 register addressed: 0x02
*/
// declare global variables to be passed into the function
bool h12;
bool hPM;
byte theHour = myRTC.getHour(h12, hPM);
// example of printing the hour to the Serial monitor
Serial.print("The hour is ");
Serial.print( theHour ); // the value returned by the function
// test the values altered by side-effects of the function
if (h12 == true) { // 12-hour mode
if (hPM == true) {
Serial.println(" PM.");
} else {
Serial.println(" AM.");
}
} else { // 24-hour mode
Serial.println(" in 24-hour mode.");
}
```
Note that supplying boolean constants as parameters will halt program compilation with an error. The parameters must be the names of boolean variables defined in the program code.
<h3 id="getDoW">getDoW&#40;&#41;</h3>
```
/*
* returns: byte = 1 to 7
* parameters: none
* asserts: none
* side effects: none
* DS3231 register addressed: 0x03
*/
byte theWeekday = myRTC.getDoW();
```
Note that the meaning of the day-of-week value is determined by the user when the time is *set* on the DS3231. See the documentation for setDoW(). In other words, "1" can signify any day of the week that the code writer chooses it to mean when setting the time. The values "2" through "7" then refer to the succeeding days, in their usual order.
<h3 id="getDate">getDate&#40;&#41;</h3>
```
/*
* returns: byte = 1 to 28, 29, 30 or 31, depending on the month and year
* parameters: none
* asserts: none
* side effects: none
* DS3231 register addressed: 0x04
*/
byte theDate = myRTC.getDate();
```
<h3 id="getMonth">getMonth&#40;&#41;</h3>
```
/*
* returns: byte = 1 to 12
* parameters: one boolean variable, passed by reference
* asserts: none
* side effects:
* the boolean parameter is set or cleared
* according to the value of the "Century" flag
* in the hardware register of the DS3231
* DS3231 register addressed: 0x05
*/
// declare a variable to receive the Century bit
bool CenturyBit;
byte theDate = myRTC.getMonth(CenturyBit);
```
Note: according to the datasheet, "The century bit (bit 7 of the month register) is toggled when the years register overflows from 99 to 00."
Note also that supplying a boolean constant value of *true* or *false* as the parameter will halt program compilation with an error. The parameter must be the name of a boolean variable defined in the program code.
The "Contemplations", below, further discuss the Century Bit.
<h3 id="getYear">getYear&#40;&#41;</h3>
```
/*
* returns: byte = 00 to 99
* parameters: none
* asserts: none
* side effects: none
* DS3231 register addressed: 0x06
*/
byte theDate = myRTC.getYear();
```
### Contemplations of An Aging Documentarian
The Century bit may supply useful information when operating the DS3231 near the end of a century. For example, the bit would have toggled when the year changed from 1999 to 2000. It would have been important to recognize that a year "00" actually represented an *increase* of time compared to the year "99".
The bit will toggle again when the year changes from 2099 to 2100, and so forth.
For reasons best understood by its designers, the Century bit is stored in the "month" register of the DS3231, rather than in the "year" register.
It might have been nicer if the DS3231 afforded the capacity to maintain a 4-digit year value.
We users of the device might find little use for the Century bit during the years 2000 through 2098 or so. Anyone planning to use this Library with a DS3231 in the year 2099 may wish to experiment with code to evaluate and correctly use the Century bit.
My beard will probably not grow long enough for me to reach that future era. Even so, by then I would probably look for a different RTC chip. The reason is the DS3231 makes no promise to handle Leap Years correctly in or after the year 2100.

View File

@@ -0,0 +1,240 @@
# DS3231 Library
## Time Setting Functions
The following methods of the DS3231 object use I2C hardware connection and the Wire library to transfer data into certain hardware registers of the DS3231.
<ul>
<li><a href="#setClockMode">setClockMode&#40;&#41;</a></li>
<li><a href="#setSecond">setSecond&#40;&#41;</a></li>
<li><a href="#setMinute">setMinute&#40;&#41;</a></li>
<li><a href="#setHour">setHour&#40;&#41;</a></li>
<li><a href="#setDoW">setDoW&#40;&#41;</a></li>
<li><a href="#setDate">setDate&#40;&#41;</a></li>
<li><a href="#setMonth">setMonth&#40;&#41;</a></li>
<li><a href="#setYear">setYear&#40;&#41;</a></li>
<li><a href="#setEpoch">setEpoch&#40;&#41;</a></li>
</ul>
The Library assumes that the DS3231 has an I2C address of 0x68.
The DS3231 register addresses mentioned below are documented on page 11 of the [manufacturer's datasheet](https://datasheets.maximintegrated.com/en/ds/DS3231-DS3231S.pdf).
The examples provided below assume a variable has been declared as follows:
```DS3231 myRTC;```
### Note to Developers
It is the code developer's sole responsibility to upload only "sensible" values to the DS3231. For example:
* 0 to 59 for minutes or seconds,
* 1 to 7 for Day of the Week,
* 30 but not 31 for the month of June.
The Library does not check the values to be set. The compiler only checks that the value is of the right size, i.e., 8 bits. A large value such as 117 will fit in 8 bits and would be passed along by the Library. Yet it makes no sense for any part of a time or a date. Code with care!
<h3 id="setClockMode">void setClockMode(bool h12)</h3>
```
/*
* returns: nothing (void)
* parameters: bool = true or false
* effect: sets the clock mode, 12-hour (`true`) or 24-hour (`false`), in the DS3231
* DS3231 register addressed: 0x02
*/
// Illustrate passing the clock mode as a boolean variable
bool mode12 = true; // use 12-hour clock mode
myRTC.setClockMode(mode12); // uploads 'true' (1) to bit 6 of register 0x02
```
The clock mode should certainly be set prior to setting the hour. In other words, invoke this function before invoking ```setHour()``` or ```setEpoch()```. Best practice would be simply to set the mode before setting the date and time.
<h3 id="setSecond">void setSecond(byte Second)</h3>
```
/*
* returns: nothing (void)
* parameters: byte = 0 to 59
* effects:
* 1. writes the seconds to the DS3231
* 2. clears the Oscillator Stop Flag in the DS3231 hardware
* DS3231 registers addressed: 0x00, 0x0F
*/
// Illustrate passing the seconds value in a variable
byte theSecond = 42; // 42 seconds
myRTC.setSecond(theSecond); // uploads 42 to register 0x00
// side-effect: also clears bit 7 of register 0x0F
```
Note: the oscillator stop flag is an informational data item indicating that the DS3231's oscillator stopped at some point in the past. The flag does not affect the operation of the DS3231 hardware. Clearing the flag makes good housekeeping sense when setting the time.
<h3 id="setMinute">void setMinute(byte Minute)</h3>
```
/*
* returns: nothing (void)
* parameters: byte = 0 to 59
* effects: writes the minutes to the DS3231
* DS3231 register addressed: 0x01
*/
// Illustrate passing the minutes as a literal value
myRTC.setMinute(17); // uploads 17 to register 0x01
```
<h3 id="setHour">void setHour(byte Hour)</h3>
```
/*
* returns: nothing (void)
* parameters: byte = 0 to 23 (use 24-hour mode here)
* effect: writes the hour to the DS3231
* DS3231 register addressed: 0x02
*/
// Illustrate passing the hour in a variable
byte theHour = 19; // equal to 7:00 p.m.
myRTC.setHour(theHour); // uploads 19 to register 0x02
```
The setHour() function will convert the hour to 12-hour mode if the DS3231 is set to operate in 12-hour mode. The function does not change how the mode is set on the DS3231 hardware.
Rather, the mode should be set by another function, setClockMode(), prior to invoking the setHour() function.
<h3 id="setDoW">void setDoW(byte DoW)</h3>
```
/*
* returns: nothing (void)
* parameters: byte = 1 to 7
* effect: writes the Day of Week to the DS3231
* DS3231 register addressed: 0x03
*/
// Illustrate passing the Day of the Week as a literal value
myRTC.setDoW(1); // uploads 1 to register 0x03
```
The Day of Week value is user-determined. For example, if one chooses Sunday to be the first day of the week, then the DoW values would be:
1. Sunday
2. Monday
3. Tuesday. and so forth.
On the other hand, if Monday were selected as the start of a week, then:
1. Monday
2. Tuesday
3. Wednesday, etcetera.
<h3 id="setDate">void setDate(byte Date)</h3>
```
/*
* returns: nothing (void)
* parameters: byte = 1 to 28, 29, 30, or 31, depending on the month and year
* effect: writes the day of the month to the DS3231
* DS3231 register addressed: 0x04
*/
// Illustrate passing the date in a variable
byte theDate = 5; // the 5th day of the month
myRTC.setDate(theDate); // uploads 5 to register 0x04
```
Reminder: it is the code developer's responsibility to ensure that a sensible value be supplied for the date.
<h3 id="setMonth">void setMonth(byte Month)</h3>
```
/*
* returns: nothing (void)
* parameters: byte = 1 to 12
* effect: writes the month to the DS3231
* DS3231 register addressed: 0x05
*/
// Illustrate passing the month in a variable
byte theMonth = 6; // the 6th month of the year; in English, June
myRTC.setMonth(theMonth); // uploads 6 to register 0x05
```
<h3 id="setYear">void setYear(byte Year)</h3>
```
/*
* returns: nothing (void)
* parameters: byte = 00 to 99
* effect: writes the year to the DS3231
* DS3231 register addressed: 0x06
*/
// Illustrate passing the year as a literal value
myRTC.setYear(22); // uploads 22 to register 0x06
```
<h3 id="setEpoch">void setEpoch(time_t epoch = 0, bool flag_localtime = false)</h3>
Keep in mind that DS3231 has no concept of time zones or daylight savings time. The value of the "epoch" parameter will be interpreted as a Unix Epoch "timestamp", defined as the number of seconds elapsed since time 00:00:00 (midnight) on January 1, 1970, GMT.
The parameter is set to a value of zero (0) by default.
Note that this function does not verify that the *epoch* value is in fact the correct GMT time. The function relies upon the program writer to supply a value that is correct for the purposes of the program.
Developers are reminded that both the DS3231 hardware and this library are designed to work best with dates and times in the years 2000 through 2099. For this reason, code writers are strongly urged to limit the range of values for the "epoch" parameter as follows:
* Minimum: 946684801 (00:00:01 a.m. on January 1, 2000)
* Maximum: 4102444799 (23:59:59 p.m. on December 31, 2099)
Unexpected results may follow from the use of parameter values less than the recommended minimum or greater than the maximum.
#### A Note About the Second Parameter, *flag_localtime*
This parameter exists because the function makes calls deep into the C++ standard library, where "local time" and "GMT time" can be treated differently. Some hardware compatible with Arduino IDE may be sensitive to this difference.
"False" ensures that the value provided for "epoch" will be treated as representing GMT.
The function sets the parameter to *false* by default.
Both of the parameters are optional because default values are defined for them. Even so, it may be a best practice to specify both parameters when calling this function.
```
/*
* returns: nothing (void)
*
* parameters:
* 1. a time_t value (unsigned long)
* containing a "epoch" time value
* defined as the number of seconds
* that have elapsed since time 00:00:00,
* on the date January 1, 1970 GMT.
* 2. a boolean variable indicating whether
* to interpret the value as "local time" (true)
* or as GMT time (false).
*
* effects:
* calculates and uploads values
* into the DS3231 registers for
* seconds, minutes, hours,
* day of week (where Sunday = 1),
* date, month and year.
*
* DS3231 registers addressed: 0x00 through 0x06
*
* Worked Example
* Suppose a web site tells you that the "epoch" time
* at 12:25:00 p.m. GMT on August 7, 2022 was 1659875100.
*/
// Initialize a (unsigned long) variable to contain that value.
time_t epochNow = 1659875100UL;
// Set the time on the DS3231 using that variable.
myRTC.setEpoch(epochNow, false);
```
The reader is encouraged to experiment with this function. Approach it playfully and check the results until you feel satisfied with your own understanding of what to expect from it on the hardware you plan to use.
The DS3231 data sheet mentions that the device can track leap years accurately "up to (the year) 2100." Perhaps that capacity will suffice for most present-day needs.
After 2099? Not our problem. The kids will have changed everything by then anyway.

View File

@@ -0,0 +1,389 @@
#DS3231
## Utility Functions
The versatile DS3231 is more than just an alarm clock. It supplies a timer and a temperature sensor, also. This Library includes utility functions giving access to these other capabilities.
### Contents
* [enable32kHz()](#32k)
* [enableOscillator()](#enable-oscillator)
* [oscillatorCheck()](#oscillator-check)
* [getTemperature()](#temperature)
* [Pin Change Interrupt](#pin-change-interrupt)
### <a id="32k">enable32kHz()</a>
The 32K output pin of a DS3231 can supply a highly accurate timer for interrupt-driven Arduino programming. Connect the pin to an interrupt-enabled Arduino input pin, enable the 32K output, and explore the benefits of programming with timer-driven interrupts, without the complexities of configuring timers built into the Arduino's microcontroller.
```
/*
* Turn on or off the 32.768 kHz output of a DS3231 RTC
*
* returns: nothing (void)
* one parameter: TF, boolean
* effect:
* TF == true, turns the output on
* TF = false, turns the output off
*/
void enable32kHz(bool TF);
/* example of use */
myRTC.enable32kHz( true );
```
The heart of a DS3231 Real Time Clock device is an oscillator switching a voltage from HIGH to LOW and back again at an exquisitely maintained frequency of 32,768 cycles per second, or 32.768 kHz.
An output pin on the DS3231 makes these voltage levels available to other devices. The Arduino can use the signal as a timer for interrupt-drive applications. It takes just a few steps:
* Connect an interrrupt-capable input pin to the 32K output.
* Set a few bits in certain Arduino registers to enable the interrupt.
* Write program code to "service" the interrupt, that is, to be executed after each interrupt occurs.
The following example toggles an LED on or off 16 times per second, using the output of a DS3231 oscillator. Notice that there is *no code* in the idle process, also known as *loop()*.
Significantly, the alarms of the DS3231 do not come into play here. Instead, the Arduino program determines the interval between actions.
The ease of using such a simple timer could support a very nice pathway into learning timer/counter interrupt programming techniques.
```
/*
* Control an LED from an interrupt service routine
* activated by a 32.768 kHz oscillator in a DS3231 RTC
*
* Hardware setup for Uno/Nano (ATmega328P-based):
* Connect digital pin 3 to the 32K pin on the DS3231
*
*/
#import <DS3231.h>
DS3231 myRTC; // Set up access to the DS3231
#define PIN32K 3 // the pin to receive signal from DS3231
void blinky(); // prototype for the interrupt service routine
void setup() {
Wire.begin(); // establish I2C communications
// configure I/O pins on the Arduino
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PIN32K, INPUT);
/*
* Configure the interrupt to detect the 32K oscillator.
* Note that interrupting on CHANGE will generate 2 interrupts per cycle,
* once when the voltage changes to LOW from HIGH,
* then again when it changes to HIGH from LOW,
* for a total of 32768 x 2 = 65536 interrupts per second
*/
attachInterrupt(digitalPinToInterrupt(PIN32K), blinky, CHANGE);
// enable output on the 32K pin of the DS3231
myRTC.enable32kHz(true);
}
void loop() {
// no code here
}
// the interrupt service routine
void blinky() {
static byte state = 0; // for turning LED on and off
static uint16_t counter = 0; // accumulates count of interrupts received
// Increment the counter and test it. Perform an action if the test result is true.
// Examples of several tests are defined as macros here.
#define BLINK_ONCE (++counter == 0) // once per second, when counter rolls over
#define BLINK_TWICE (++counter >> 15) // twice per second, when counter reaches 2^15
#define BLINK_FOUR (++counter >> 14) // four times per second, when counter reaches 2^14
#define BLINK_EIGHT (++counter >> 13) // eight per second, when counter reaches 2^13
#define BLINK_16 (++counter >> 12) // 16 times per second, when counter reaches 2^12
if (BLINK_16) // Write-in the macro you wish to evaluate.
{
state = !state; // flipflop the state
digitalWrite(LED_BUILTIN, state); // flipflop the LED
tCount = 0; // start the count over
}
}
```
Let's follow the math in that example.
The oscillator frequency, 32.768 kHz lends itself easily to binary arithmetic because the number of cycles is a power of two: 2<sup>15</sup> = 32768, to be precise.
Each cycle is made up of two voltage changes, e.g., HIGH to LOW, then LOW back to HIGH. Thus, the oscillator produces 2<sup>15</sup> × 2 = 2<sup>16</sup> voltage changes per second.
Each voltage change triggers an interrupt because the code specifies CHANGE to be the trigger. Thus, the interrupt service routine (ISR) will be invoked 2<sup>16</sup> times each second.
A counter variable is incremented upon each interrupt. An unsigned 16-bit counter being incremented 2<sup>16</sup> times per second will "roll over" to a value of zero exactly once every second.
The ISR can test for and act upon lesser values. It means programmers can design ISRs to perform actions at intervals shorter than one second. For an example of an interval that is not a power of two, see the section on [Pin Change Interrupts](#pin-change-interrupt), below.
Note that the output on the 32K pin is independent of that on the INT/SQW pin. The two pins can separately drive interrupts to two, different input pins on an Arduino.
### <a id="enable-oscillator">enableOscillator()</a>
```
/*
* Regulates the output on the INT/SQW pin
*
* returns: nothing (void)
* three parameters:
* TF, boolean
* (true) output a square wave on the INT/SQW pin
* (false) output alarm interrupts on the INT/SQW pin
* battery, boolean
* (true) allow square wave output when running on battery
* (false) do not output square wave if running on battery
* frequency, 8-bit unsigned integer, select the frequency of the square wave output
* 0: 1 Hz = one cycle per second
* 1: 1.024 kHz
* 2: 4.096 kHz
* 3 - 255: 8.192 kHz
*/
void enableOscillator(bool TF, bool battery, byte frequency)
/*
* example of use
* output square wave at frequency 1 Hz even when running on battery
*/
bool outputSQW = true;
bool batteryUseAllowed = true;
byte outputFrequency = 0; // select 1 Hz
myRTC.enableOscillator(outputSQW, batteryUseAllowed, outputFrequency);
```
The INT/SQW output pin on a DS3231 can operate in either one of two, different modes.
* Interrupt mode is described in the documentation for Alarms. It:
* uses the pin to generate an interrupt signal when an alarm flag goes HIGH.
* is ideal for occasional signals under program control.
* can automate a repeating series of pulses at long intervals ranging from one second up to one month.
* Oscillator mode is described below. It:
* generates a series of pulses that repeat automatically.
* offers programs a choice of four, rapid frequencies ranging from one second (1 Hz) to 8.192 KHz.
By default when power is first applied to the DS3231, the INT/SQW pin is configured to operate in Interrupt mode.
Note that the output on the INT/SQW pin is independent of that on the 32K pin. The two pins can separately drive interrupts to two, different input pins on an Arduino.
### <a id="oscillator-check">oscillatorCheck()</a>
```
/*
* Detects trouble that might have made the time inaccurate in the DS3231.
*
* returns: boolean,
* (false) means the DS3231 oscillator has stopped running for some reason
* in which case the time in the DS3231 could be inaccurate.
*
* (true) means that the DS3231 oscillator has not stopped since the flag was last cleared
*
* parameters: none
*/
bool oscillatorCheck()
/* example of usage */
bool rtcRemainsHealthy = myRTC.oscillatorCheck();
```
A flag named Oscillator Stop Flag (OSF)in the DS3231 gets written to logic 1 by the hardware whenever one of the following four conditions occurs:
1. Power is first applied to the DS3231.
2. DS3231 switches to battery backup power, unless another flag ("EOSC", see the data sheet) is set to disable the oscillator. In that situation, the oscillator stops and data in all of the DS3231 registers is held static.
3. The power has decreased, on both the V<sub>CC</sub> and V<sub>BAT</sub> pins, to a level less than what is needed to sustain reliable operation.
4. Certain other "external influences", such as electrical noise, cause the flag to be set.
The timekeeping data in the DS3231 should be viewed with doubt in the event the OSF flag is found to be at logic level 1.
The flag will remain at logic level 1 until it is written to zero by program code. In this Library, the function that writes OSF to zero is the setSeconds() method of the DS3231 class.
The setEpoch() method invokes setSeconds(), which means that setEpoch() will clear the OSF flag also.
Reminder: the OSF flag will be written to 1 and the oscillator will not be running when power is first applied to the DS3231. Setting the time, specifically setting the seconds value of the time, writes OSF to zero and starts the oscillator which drives the timekeeping process.
### <a id="temperature">getTemperature()</a>
```
/*
* Retrieve the internal temperature of the DS3231
*
* returns: the floating-point value of the temperature
*
* error return: 9999, if a valid temperature could not be retrieved
*
* parameters: none
*
*/
float getTemperature()
/* example of usage */
float rtcTemp = myRTC.getTemperature();
if (float > -9999)
{
// it may be OK to use the returned value
}
else
{
// the value returned is not valid
}
```
Why would a clock chip contain a temperature sensor? The answer helps to understand how the DS3231 can maintain a very high level of accuracy.
Your friendly neighborhood Documentarian will make a non-engineer's attempt to explain.
An oscillator's frequency can vary with temperature. It can also vary with small changes in electrical capacitance.
The DS3231 hardware includes an array of tiny capacitors that become engaged or disengaged in regulating the oscillator, based on the measured voltage level from a temperature sensor.
Long story short, the temperature sensor is there to help maintain the accuracy of the clock. The sensor's measurement is updated at 64-second intervals in two memory registers.
This function retrieves the values in those two registers and combines them into a floating-point value.
According to the data sheet, the temperature values stored in the DS3231 registers claim to be accurate within a range of three degrees Celsius above or below the actual temperature.
### Pin Change Interrupt
The oscillating output from the 32K pin of a DS3231 makes an excellent source of timer input for the Pin Change Interrupt capability of AVR-based Arduino boards.
All of the I/O pins on popular models such as the Uno and Nano can be used to generate interrupts in response to a fluctuating voltage. Each change, whether to HIGH from LOW or vice versa, will trigger an interrupt.
65,536 interrupts will result from connecting the 32K output of a DS3231 to a Pin Change Interrupt-enabled input pin of an Arduino.
It falls outside the scope of this article to explain interrupt-based programming in general. Many books and articles cover the topic very well.
Pin Change Interrupts may be a lesser-known feature of AVR microcontrollers. Their usage is documented in the different data sheets for each model of controller. The example below was written for the ATmega328P chip found on Arduino Uno and Nano models. Refer to that data sheet for more information.
The example also illustrates a solution to a general problem of working with powers of 2: how to handle remainders following division by numbers other than 2.
An algorithm is developed to divide the 65,536 interrupts into ten, nearly-equal intervals of time, while completing all ten intervals in exactly one second.
Dividing by other values can be approached similarly. The program writer would need to work out the applicable numbers of steps and interval lengths.
Comments in the example provide additional documentaiton.
```
#include <DS3231.h>
DS3231 myRTC;
void setup() {
// set up the LED blink pin
pinMode(LED_BUILTIN, OUTPUT);
// start I2C and enable 32.768 KHz output from the DS3231
Wire.begin();
myRTC.enable32kHz(true);
// enable pin A2 to receive the 32.768 KHz output from DS3231
pinMode(A2, INPUT); // not INPUT_PULLUP
// Configure pin A2 to trigger interrupts
// each time the voltage level changes between LOW and HIGH.
//
// The following explicitly uses hardware address names
// defined in the ATmega328P datasheet
// to modify relevant bits in relevant registers.
// It avoids the use of special libraries, by programmer's preference.
PCMSK1 = (1<<PCINT10); // select pin A2 for pin change interrupt
cli(); // temporarily disable interrupts globally
PCICR |= (1<<PCIE1); // enable pin change interrupt for A2
PCIFR |= (1<<PCIF1); // clear the interrupt flag bit for A2
sei(); // re-enable interrupts globally
}
void loop() {
// no code in the loop!
}
/*
* The Interrupt Service Routine initiates an action
* at intervals very close to 1/10 second in length.
* The DS3231 outputs precisely 65,536 voltage level changes per second.
* Arduino hardware generates 65,536 interrupts per second in response.
* Alas, 65536 does not divide evenly by 10.
* However, a well-designed algorithm can accurately
* separate the 65,536 interrupts into ten segments
* of very nearly the same length, as follows:
* four segments of 6553
* plus six segments of 6554.
* This is accurate because ( 4 × 6553) + (6 × 6554) = 65536.
* Humans cannot discern a difference of less than 1/65000th of a second.
*
*/
ISR(PCINT1_vect) {
// housekeeping variables
static uint16_t interval = 0; // step length counter
static uint8_t led_state = 0;
static uint8_t next_step = 0; // step number
if (interval > 0) {
// The interval of time as not yet expired.
// Decrement the counter and exit the ISR.
interval -= 1;
} else {
// The step length counter has timed out ( == 0 )
// which means the interval of time has expired.
// First, take care of the housekeeping.
// Renew the step length counter
// assign value of 6553 on steps 0 through 3
// but value of 6554 on steps 4 through 9
interval = (next_step < 4) ? 6553 : 6554;
// Increment the step number
next_step += 1;
// but roll it over to 0 after step 9.
if (next_step > 9) next_step = 0;
// The housekeeping is complete.
// Now execute code for the action to be taken
// after an interval of time expires.
// In this example, we merely toggle an LED.
// "Real" code could initiate almost any action.
led_state = ! led_state;
digitalWrite(LED_BUILTIN, led_state);
}
}
```
#### References for Pin Change Interrupts
* Almy, Tom. *Far Inside the Arduino*. 2020. pp 54-83
* Williams, Elliot. *Make: AVR Programming". 2014. Maker Media Inc. pp 155-165.
* "Engineer, Wandering". *Arduino Pin Change Interrupts*. 2014. Web page: https://thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/. Accessed Sept 17, 2022.

24
libraries/DS3231/LICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

325
libraries/DS3231/README.md Normal file
View File

@@ -0,0 +1,325 @@
# DS3231 Library
An Arduino library for the DS3231 real-time clock (RTC).
## Description
The library provides easy-to-use methods to:
* set and read the current date and time;
* set, clear, and detect two, independent, Time-of-Day alarms;
* perform certain conversions and calculations with time data;
* manage certain hardware inside the DS3231 RTC module.
This document explains the installation and usage of the Library with the Arduino IDE.
You do have to install the Library in your Arduino IDE environment before you can use it. Installation instructions are provided, below.
## Contents
* [Summary](#summary)
* [How to Cite](#how-to-cite)
* [About the DS3231](#about-the-ds3231)
* [How to Install the Library](#installation)
* [Functions Provided in the Library](#functions)
* [Examples of Using the Library](#examples-of-use)
* [Helpful Resources](#additional-resources-and-references)
* [Contributing, Credits and License](#contributing)
* [To-Do List](#to-do)
<hr>
## Summary
After installing the Library in your Arduino IDE, using it in a program starts with three, simple steps:
<ol start="1">
<li>Import the Library into the program code:</li>
</ol>
```
#include <DS3231.h>
```
<ol start="2">
<li>Declare a DS3231 object, for example:</li>
</ol>
```
DS3231 myRTC;
```
<ol start="3">
<li>Start the Wire library to enable I2C communications with the DS3231 hardware, typically in the setup() code block:</li>
</ol>
```
Wire.begin();
```
Then, Library functions are typically accessed through the DS3231 object. For example, to read the current date of the month (1 through 28, 29, 30 or 31, depending on the month and the year):
```
unsigned int theDate = myRTC.getDate();
```
The Library incorporates two other classes to assist with managing date and time data:
* ```DateTime``` enables a versatile object for managing date and time data. A variable of the DateTime type can represent a specific date and time in two different ways:
1. as distinct values for year, month, day, hour, minute and second, or
2. as a single, unsigned integer. The latter is handy for doing arithmetic with dates.
* ```RTClib``` institutes a convenient ```RTClib::now()``` function for receiving a date/time snapshot, as a DateTime object, from the DS3231 device.
## How to Cite
If you use this library in a publication, please cite it in one or both of the following two ways:
1. Through the `CITATION.cff` file here, which should be up to date with the permanent archive available from Zenodo
2. If you need an academic journal reference and/or you are discussing the integration of the DS3231 into a larger hardware + firmware ecosystem,<br/>
**Wickert, A. D., Sandell, C. T., Schulz, B., & Ng, G. H. C. (2019), [Open-source Arduino-compatible data loggers designed for field research](https://hess.copernicus.org/articles/23/2065/2019/), *Hydrology and Earth System Sciences*, *23*(4), 2065-2076, doi:10.5194/hess-23-2065-2019.**<br/>
This option should not be the only one used because it does not credit the original library developer, Eric Ayars.
## About the DS3231
DS3231 is a low-cost integrated circuit (IC) providing a highly accurate, real time clock for use with Arduino, Raspberry Pi, BBC micro:bit and other popular small computing devices.
The IC is typically mounted on a circuit board or module, along with other hardware, such as header pins, supportive electrical components, and even EEPROM memory chips, for convenient attachment to a breadboard or an Arduino.
Several different modules are available from a number of competing vendors. This Library aspires to work with any DS3231 module that supports I2C communications with the IC.
DS3231 runs independently and can be kept running for a considerable length of time by a small, backup battery, even if power to the Arduino is turned off.
According to the [datasheet](https://datasheets.maximintegrated.com/en/ds/DS3231-DS3231S.pdf), the DS3231 hardware "completely manages all timekeeping functions (including):
* Seconds,
* Minutes,
* Hours
* 12-hour format with AM/PM indication, or
* 24-hour format,
* Day of the Week,
* Date of the Month,
* Month, and
* Year, with Leap-Year Compensation Valid Up to 2100"
Data for the time and date are stored in registers (memory locations) on the DS3231. Each, distinct value is stored separately. This means the seconds are in one register, the minutes in another, and so forth. The DS3231 updates the values in the date and time registers every second.
The device keeps track of time by operating its own 32.768 kHz crystal oscillator, similar to the timekeeper in an electronic watch. Temperature can affect oscillator speed. Accordingly, the DS3231 takes further steps to maintain accuracy. It senses the temperature around the crystal and adjusts the speed of the oscillator.
The oscillator can be accessed directly, independent of the date and time registers, for use as an external timer or source of interrupts.
The temperature can be read from the DS3231 using a Library function. The data sheet declares it to be accurate to within 3 degrees, Celsius.
### Power Supply and Battery Backup
The DS3231 can run in a range between 2.3 volts and 5.5 volts. The device actually has two power supply pins: the primary source, V<sub>CC</sub>, and a secondary, backup source, V<sub>BAT</sub>.
Some popular modules mounting a DS3231 provide a receptacle for a coin battery, attaching it to the V<sub>BAT</sub> pin. If a sufficiently-charged battery is present, the DS3231 will switch automatically to the battery after detecting a drop in V<sub>CC</sub> voltage below a certain "power-fail" level.
It will switch back to V<sub>CC</sub> automatically, if and when that voltage rises back up above both the power-fail and the battery voltage level.
One point regarding the choice of battery may deserve consideration: the question of whether to install a rechargeable coin battery, or to disable a charging circuit if such a thing is provided on the module being used. The topic is controversial and the authors of this Library do not express any opinion about it. Readers may choose to search online for more information.
[back to top](#ds3231-library)
<hr>
## Installation
### First Method
![image](https://user-images.githubusercontent.com/36513474/68038497-eae68200-fceb-11e9-9812-b47ff6e06e2f.png)
1. In the Arduino IDE, navigate to Sketch > Include Library > Manage Libraries
1. Then the Library Manager will open and you will find a list of libraries that are already installed or ready for installation.
1. Then search for DS3231 using the search bar.
1. Click on the text area and then select the specific version and install it.
### Second Method
1. Navigate to the [Releases page](https://github.com/NorthernWidget/DS3231/releases).
1. Download the latest release.
1. Extract the zip file
1. In the Arduino IDE, navigate to Sketch > Include Library > Add .ZIP Library
### Dependencies
The user must also ensure that two, other, required libraries are available to the Arduino IDE. This DS3231 library takes care to ```#include``` the following in a program, but it does not install them in your Arduino IDE:
* Wire.h : a widely-used Arduino library for I2C communications
* time.h : a modified C language header file packaged with avr-libc and the AVR-GCC compiler
Note: At the time of writing, both of these libraries were included by default with a standard installation of the 1.8.x version of Arduino IDE for AVR-based devices.
A simple way to check for the availability of the two libraries is to compile the following, blank Arduino sketch. If the IDE does not complain that anything is missing, then the required libraries are available for use with this DS3231 library.
```
#include <Wire.h>
#include <time.h>
void setup() {}
void loop() {}
```
[back to top](#ds3231-library)
<hr>
## Functions
Readers are encouraged to visit the [Documentation folder](https://github.com/NorthernWidget/DS3231/tree/master/Documentation) for detailed information about the functions in this Library. Additional information is available in the [Examples of Use](#examples-of-use) described below, and in the code source files of this repository:
* [DS3231.h](https://github.com/NorthernWidget/DS3231/blob/master/DS3231.h)
* [DS3231.cpp](https://github.com/NorthernWidget/DS3231/blob/master/DS3231.cpp)
### [Read the Date or Time](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md)
- [RTClib::now() <sup>\*</sup>](https://github.com/NorthernWidget/DS3231#the-special-rtclibnow-function-)
- [getSecond()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md#getsecond)
- [getMinute()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md#getminute)
- [getHour(bool, bool)](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md#gethour)
- [getDoW()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md#getdow)
- [getDate()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md#getdate)
- [getMonth(bool)](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md#getmonth)
- [getYear()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Retrieval.md#getyear)
\* The *RTClib::now()* function is not accessed through the DS3231 object. Rather, it has a very specific syntax as described below in <a href="#RTClib_now_function">The Special RTClib::now() Function</a>.
### [Set the Date or Time](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md)
We emphasize here and elsewhere that the code writer bears responsibility to ensure that the values passed into the following functions fall within the valid range, as specified in the documentation for each function.
Unexpected values in the DS3231 hardware registers may follow from the insertion of an invalid parameter into any one of these functions.
- [setEpoch()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setepochtime_t-epoch--0-bool-flag_localtime--false)
- [setSecond()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setsecondbyte-second)
- [setMinute()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setminutebyte-minute)
- [setHour()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-sethourbyte-hour)
- [setDoW()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setdowbyte-dow)
- [setDate()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setdatebyte-date)
- [setMonth()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setmonthbyte-month)
- [setYear()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setyearbyte-year)
- [setClockMode()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Time-Set.md#void-setclockmodebool-h12)
### [Set, Clear and Check Alarms](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md)
The following functions set and retrieve time and date values in the DS3231 hardware alarm registers.
Parameters include a special 8-bit value named "AlarmBits". Readers may find additional information about it at the following links: [Alarm Bits Quick Reference](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#alarm-bits-quick-reference), and [Alarm Bits in Detail](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#alarm-bits-in-detail).
- [getA1Time()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#geta1time)
- [getA1Time() with Option](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#geta1time-with-option)
- [getA2Time()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#geta2time)
- [getA2Time() with Option](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#geta2time-with-option)
- [setA1Time()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#seta1time)
- [setA2Time()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#seta2time)
The remaining functions in this group set and retrieve certain flags in the DS3231 hardware that govern or report the operation of the alarms.
- [turnOnAlarm()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#turnonalarm)
- [turnOffAlarm()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#turnoffalarm)
- [checkAlarmEnabled()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#checkalarmenabled)
- [checkIfAlarm()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#checkifalarm)
- [checkIfAlarm() with Option](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Alarms.md#checkifalarm-with-option)
### [Manage DS3231 Hardware](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Utilities.md)
The functions in this group support uses for a DS3231 other than as an alarm clock.
- [getTemperature()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Utilities.md#gettemperature)
- [enableOscillator()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Utilities.md#enableoscillator)
- [enable32kHz()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Utilities.md#enable32khz)
- [oscillatorCheck()](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/Utilities.md#oscillatorcheck)
### [DateTime Object](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/DateTime.md)
A limited DateTime class is defined in this DS3231.h library. The link, above, provides more information about the class.
[Retrieving Date and Time Data](https://github.com/NorthernWidget/DS3231/blob/master/Documentation/DateTime.md#retrieving-date-and-time-data) further documents the DateTime class methods listed below.
- year()
- month()
- day()
- hour()
- minute()
- second()
- unixtime()
<h3 id="RTClib_now_function">The Special <code>RTClib::now()</code> Function </h3>
```RTClib::now()``` is the precise, complete name for a special function that returns a ```DateTime``` object from the DS3231. Always write it just so: ```RTClib::now()```.
The function returns a DateTime object. To use it in your program, declare a DateTime type of variable to receive the value. For example,
```DateTime currentMoment = RTClib::now();```
The value of ```currentMoment``` can then be accessed as either:
* an unsigned integer containing the number of seconds since a certain reference date, or
* distinct values for Year, Month, Day, Date, Hour, Minute, or Second.
[back to the list of functions](#functions)<br>
[back to top](#ds3231-library)
<hr>
## Examples of Use
There are many examples provided in the [examples](https://github.com/NorthernWidget/DS3231/tree/master/examples) folder of this repository. At the time of writing the examples include:
* *set*: demonstrates selected time-setting functions
* *test*: demonstrates selected time-reading functions
* *echo*: demonstrates setting the time and date then reading it back
* *echo_time*: similar to *echo*, demonstrates setting and reading time/date data
* *oscillator_test*: demonstrates advanced techniques for managing and using the DS3231 device as a pulse generator
Future development plans include updating these examples and adding more of them.
See also [Working with the DS3231 libraries and interrupts](https://github.com/IowaDave/RTC-DS3231-Arduino-Interrupt), a tutorial provided by [IowaDave](https://github.com/IowaDave).
[back to top](#ds3231-library)
<hr>
## Additional Resources and References
* [Maxim DS3231 Data Sheet](https://datasheets.maximintegrated.com/en/ds/DS3231-DS3231S.pdf)
[back to top](#ds3231-library)
<hr>
## Contributing
If you want to contribute to this project:
- Report bugs and errors
- Ask for enhancements
- Create issues and pull requests
- Tell others about this library
- Contribute new protocols
Please read [CONTRIBUTING.md](https://github.com/NorthernWidget/DS3231/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
[back to top](#ds3231-library)
<hr>
## Credits
This is a splice of [Ayars'](http://hacks.ayars.org/2011/04/ds3231-real-time-clock.html) and [Jeelabs/Ladyada's](https://github.com/adafruit/RTClib) libraries.
The authors of this library are A. Wickert <awickert@umn.edu>, E. Ayars, J. C. Wippler, N. W. LLC <info@northernwidget.com> and it is maintained by A. Wickert. This library is released into the public domain by Jeelabs, Ladyada, and E. Ayars.
Based on previous work by:
- S. T. Andersen
- SimGas
- Per1234
- Glownt
[back to top](#ds3231-library)
<hr>
## License
DS3231 is licensed under [The Unlicense](https://github.com/NorthernWidget/DS3231/blob/master/LICENSE).
[back to top](#ds3231-library)
<hr>
## To Do
A project is underway to update the library's documentation.
### Existing Repo Issues to be addressed:
* \#42 Alarm Documentation
* \#24 DateTime Comparison Operators
* \#20 AlarmBits Documentation

View File

@@ -0,0 +1,92 @@
struct bits3231 {
// 00h time seconds 00 - 59
unsigned timeSec:4; // seconds digit 0 - 9
unsigned timeSec10:3; // tens of seconds digit 0 - 5
unsigned :1; // skip high bit
// 01h time minutes 00 - 59
unsigned timeMin:4; // minutes digit 0 - 9
unsigned timeMin10:3; // tens of minuts digit 0 - 5
unsigned: 1; // skip high bit
// 02h time hour 1 - 12 or 0 - 23
unsigned timeHour:4; // hours digit 0 - 9
unsigned timeHour10:1; // hour ten digit 0 - 1
unsigned timeAP20:1; // 0=am, 1=pm if timeMode12 = 1, else 0 = 0, 1 = 20-hour
unsigned timeMode12:1; // 1=12-hour mode, 0=24-hour mode
unisgned :1; // skip high bit
// 03h time day of week
unsigned timeDay:3; // 1 - 7, user-defined
unisgned :5; // skip bits 7:3
// 04h time date of month 01 - 31
unsigned timeDate:4; // date 0 - 9
unsigned timeDate10:2; // tens 0 - 3
unsigned :2; // skip bits 7:6
// 05h time month 01 - 12
unsigned timeMonth:4; // month digit 0 - 9
unsigned timeMonth10:1; // month tens digit 0 - 1
unsigned :2; // skip bits 6:5
unsigned timeCentury:1; // century bit
// 06h time year 00-99
unsigned timeYear:4; // year digit 0 - 9
unsigned timeYear10:4; // year tens digit 0 - 9
// 07h Alarm 1 seconds 00 - 59
unsigned A1Sec:4; // seconds digit 0 - 9
unsigned A1Sec10:3; // tens of seconds digit 0 - 5
unsigned A1M1:1; // Alarm 1 Bit 1
// 08h Alarm 1 minutes 00 - 59
unsigned A1Min:4; // minutes digit 0 - 9
unsigned A1Min10:3; // tens of minuts digit 0 - 5
unsigned: A1M2; // Alarm 1 Bit 2
// 09h Alarm 1 hour 1 - 12 or 0 - 23
unsigned A1Hour:4; // hours digit 0 - 9
unsigned A1Hour10:1; // hour ten digit 0 - 1
unsigned A1AP20:1; // 0=am, 1=pm if time12 = 1, else 0 = 0, 1 = 20-hour
unsigned A1Mode12:1; // 1=12-hour mode, 0=24-hour mode
unsigned A1M3:1; // Alarm 1 Bit 3
// 0Ah Alarm 1 date
unsigned A1DayDate:4; // month digit 0 - 9 if A1DYDT = 1, else day 1 - 7
unsigned A1Date10:2; // month tens 0-3 if A1DyDT = 1
unsigned A1DYDT:1; // 1 = match month, 0 = match day of week
unsigned A1M4:1; // Alarm 1 Bit 4
//
// note: there is no alarm bit named A2M1
//
// 0Bh Alarm 2 minutes 00 - 59
unsigned A2Min:4; // minutes digit 0 - 9
unsigned A2Min10:3; // tens of minuts digit 0 - 5
unsigned: A2M2; // Alarm 2 Bit 2
// 0Ch Alarm 2 hour 1 - 12 or 0 - 23
unsigned A2Hour:4; // hours digit 0 - 9
unsigned A2Hour10:1; // hour ten digit 0 - 1
unsigned A2AP20:1; // 0=am, 1=pm if time12 = 1, else 0 = 0, 1 = 20-hour
unsigned A2Mode12:1; // 1=12-hour mode, 0=24-hour mode
unsigned A2M3:1; // Alarm 2 Bit 3
// 0Dh Alarm 2 date
unsigned A2DayDate:4; // month digit 0 - 9 if A1DYDT = 1, else day 1 - 7
unsigned A2Date10:2; // month tens 0-3 if A1DyDT = 1
unsigned A2DYDT:1; // 1 = match month, 0 = match day of week
unsigned A2M4:1; // Alarm 2 Bit 4
// 0Eh Device control register; see data sheet
unsigned EOSC:1; // enable oscillator, active low; all data static when high
unsigned BBSQW:1; // battery-backed square wave enable
unsigned CONV:1; // convert temperature, active high
unsigned RS2:1; // set square wave frequency
unsigned RS1:1; // set square wave frequency
unsigned INTCN:1; // interrupt control, high enables interrupt output
unsigned A2IE:1; // enbable interrupt on A2 match
unsigned A1IE:1; // enable interrupt on A1 match
// 0Fh Device control / status register
unsigned OSF:1; // oscillator stop flag
unsigned :3; // skip bits 6:4
unsigned EN23KHZ:1; // enable 32.768kHz square wave output, active high
unsigned BSY:1; // device busy flag
unsigned A2F:1; // alarm 2 flag, high = time match with Alarm 2
unsigned A1F:1; // alarm 1 flag, high = time match with Alarm 1
// 10h Aging offset
signed AgingOffset:8; // aging offset in two's-complement form
// 11h MSB of temperature
signed tempMSB:8; // MSB of temperature in two's complement form
// 12h LSB of temperature
unsigned :6; // skip bits 5:0
unsigned tempLSB:2; // LSB in bits 7:6
}

View File

@@ -0,0 +1,167 @@
/*
AdvanceAlarm.ino
David Sparks Sept 2022
Example of advancing an alarm by an arbitrary interval of time.
Hardware setup:
Connect DS3231 SQW pin to Arduino interrupt pin 2
Builds upon ArduinoInterrupt.ino example by
Jacob Nuernberg August 2022
Example on using recurring interrupts
of arbitrary frequency with DS3231 alarms.
Hardware setup:
Connect DS3231 SQW pin to Arduino interrupt pin 2
Tested on:
- Arduino Nano (ATmega328P)
*/
#include <DS3231.h>
#include <Wire.h>
// Interrupt frequency, in seconds
#define INT_FREQ 3UL // 3 seconds, characterized as unsigned long
// myRTC interrupt pin
#define CLINT 2
// Setup clock
DS3231 myRTC;
// Variables for use in method parameter lists
byte alarmDay;
byte alarmHour;
byte alarmMinute;
byte alarmSecond;
byte alarmBits;
bool alarmDayIsDay;
bool alarmH12;
bool alarmPM;
// Interrupt signaling byte
volatile byte tick = 1;
void setup() {
// Begin I2C communication
Wire.begin();
// Begin Serial communication
Serial.begin(9600);
while (!Serial);
// Set the DS3231 clock mode to 24-hour
myRTC.setClockMode(false); // false = not using the alternate, 12-hour mode
// Set the clock to an arbitrarily chosen time of
// 00:00:00 midnight the morning of January 1, 2020
// using a suitable Unix-style timestamp
myRTC.setEpoch(1640995200);
// Assign parameter values for Alarm 1
alarmDay = myRTC.getDate();
alarmHour = myRTC.getHour(alarmH12, alarmPM);
alarmMinute = myRTC.getMinute();
alarmSecond = INT_FREQ; // initialize to the interval length
alarmBits = 0b00001110; // Alarm 1 when seconds match
alarmDayIsDay = false; // using date of month
// Upload initial parameters of Alarm 1
myRTC.turnOffAlarm(1);
myRTC.setA1Time(
alarmDay, alarmHour, alarmMinute, alarmSecond,
alarmBits, alarmDayIsDay, alarmH12, alarmPM);
// clear Alarm 1 flag after setting the alarm time
myRTC.checkIfAlarm(1);
// now it is safe to enable interrupt output
myRTC.turnOnAlarm(1);
// When using interrupt with only one of the DS3231 alarms, as in this example,
// it may be possible to prevent the other alarm entirely,
// so it will not covertly block the outgoing interrupt signal.
// Try to prevent Alarm 2 altogether by assigning a
// nonsensical alarm minute value that cannot match the clock time,
// and an alarmBits value to activate "when minutes match".
alarmMinute = 0xFF; // a value that will never match the time
alarmBits = 0b01100000; // Alarm 2 when minutes match, i.e., never
// Upload the parameters to prevent Alarm 2 entirely
myRTC.setA2Time(
alarmDay, alarmHour, alarmMinute,
alarmBits, alarmDayIsDay, alarmH12, alarmPM);
// disable Alarm 2 interrupt
myRTC.turnOffAlarm(2);
// clear Alarm 2 flag
myRTC.checkIfAlarm(2);
// NOTE: both of the alarm flags must be clear
// to enable output of a FALLING interrupt
// attach clock interrupt
pinMode(CLINT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CLINT), isr_TickTock, FALLING);
// Configure the LED for blinking
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// static variable to keep track of LED on/off state
static byte state = false;
// Do when alarm interrupt received:
if (tick) {
// right away, capture the current time in a DateTime variable
// for later processing
DateTime alarmDT = RTClib::now();
// disable Alarm 1 interrupt
myRTC.turnOffAlarm(1);
// Clear Alarm 1 flag
myRTC.checkIfAlarm(1);
tick = 0; // reset the local interrupt-received flag
state = ~state; // reverse the state of the LED
digitalWrite(LED_BUILTIN, state);
// optional serial output
Serial.print("Turning LED ");
Serial.print((state ? "ON" : "OFF"));
Serial.print(" at ");
Serial.print(alarmDT.hour());
Serial.print(":");
Serial.print(alarmDT.minute());
Serial.print(":");
Serial.println(alarmDT.second());
// extract the DateTime values as a timestamp
uint32_t nextAlarm = alarmDT.unixtime();
// add the INT_FREQ number of seconds
nextAlarm += INT_FREQ;
// update the DateTime with the new timestamp
alarmDT = DateTime(nextAlarm);
// upload the new time to Alarm 1
myRTC.setA1Time(
alarmDT.day(), alarmDT.hour(), alarmDT.minute(), alarmDT.second(),
alarmBits, alarmDayIsDay, alarmH12, alarmPM);
// enable Alarm 1 interrupts
myRTC.turnOnAlarm(1);
// clear Alarm 1 flag again after enabling interrupts
myRTC.checkIfAlarm(1);
}
}
void isr_TickTock() {
// interrupt signals to loop
tick = 1;
return;
}

View File

@@ -0,0 +1,139 @@
/*
AlarmInterrupt.ino
Jacob Nuernberg
08/22
Example on using interrupts with DS3231 alarms.
Hardware setup:
Connect DS3231 SQW pin to Arduino interrupt pin 2
Tested on:
- Arduino UNO
- Arduino nano
Added to this example:
1. Descriptively named variables to pass parameter values.
2. Modify AlarmBits for Alarm 1 to 0b00001111,
for clarity, because Alarm 1 uses only bits 3:0.
3. Add code to prevent Alarm 2 from interfering with the interrupt,
by setting A2Minute to a value that can never match the time
and setting AlarmBits to 0b01100000: alarm "when minutes match".
Also clear the A2 alarm flag.
David Sparks, September 2022
*/
#include <DS3231.h>
#include <Wire.h>
// myRTC interrupt pin
#define CLINT 2
// Setup clock
DS3231 myRTC;
// Variables for use in method parameter lists
byte alarmDay;
byte alarmHour;
byte alarmMinute;
byte alarmSecond;
byte alarmBits;
bool alarmDayIsDay;
bool alarmH12;
bool alarmPM;
// Interrupt signaling byte
volatile byte tick = 1;
void setup() {
// Begin I2C communication
Wire.begin();
// Begin Serial communication
Serial.begin(9600);
while (!Serial);
Serial.println();
Serial.println("Starting Serial");
// Assign parameter values for Alarm 1
alarmDay = 0;
alarmHour = 0;
alarmMinute = 0;
alarmSecond = 0;
alarmBits = 0b00001111; // Alarm 1 every second
alarmDayIsDay = false;
alarmH12 = false;
alarmPM = false;
// Set alarm 1 to fire at one-second intervals
myRTC.turnOffAlarm(1);
myRTC.setA1Time(
alarmDay, alarmHour, alarmMinute, alarmSecond,
alarmBits, alarmDayIsDay, alarmH12, alarmPM);
// enable Alarm 1 interrupts
myRTC.turnOnAlarm(1);
// clear Alarm 1 flag
myRTC.checkIfAlarm(1);
// When using interrupt with only one of the DS3231 alarms, as in this example,
// it may be advisable to prevent the other alarm entirely,
// so it will not covertly block the outgoing interrupt signal.
// Prevent Alarm 2 altogether by assigning a
// nonsensical alarm minute value that cannot match the clock time,
// and an alarmBits value to activate "when minutes match".
alarmMinute = 0xFF; // a value that will never match the time
alarmBits = 0b01100000; // Alarm 2 when minutes match, i.e., never
// Upload the parameters to prevent Alarm 2 entirely
myRTC.setA2Time(
alarmDay, alarmHour, alarmMinute,
alarmBits, alarmDayIsDay, alarmH12, alarmPM);
// disable Alarm 2 interrupt
myRTC.turnOffAlarm(2);
// clear Alarm 2 flag
myRTC.checkIfAlarm(2);
// NOTE: both of the alarm flags must be clear
// to enable output of a FALLING interrupt
// attach clock interrupt
pinMode(CLINT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CLINT), isr_TickTock, FALLING);
// Use builtin LED to blink
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// static variable to keep track of LED on/off state
static byte state = false;
// if alarm went of, do alarm stuff
if (tick) {
tick = 0;
state = ~state;
digitalWrite(LED_BUILTIN, state);
// optional serial output
Serial.print("Turning LED ");
Serial.println((state ? "ON" : "OFF"));
// Clear Alarm 1 flag
myRTC.checkIfAlarm(1);
}
// Loop delay to emulate other running code
delay(10);
}
void isr_TickTock() {
// interrupt signals to loop
tick = 1;
return;
}

View File

@@ -0,0 +1,50 @@
/*
AlarmPolilng.ino
Jacob Nuernberg
08/22
Example on using DS3231 alarms with polling and test checkIfAlarm()
Tested on:
- Arduino nano
*/
#include <DS3231.h>
#include <Wire.h>
// Setup clock
DS3231 myRTC;
void setup() {
// Begin I2C communication
Wire.begin();
// Setup alarm one to fire every second
myRTC.turnOffAlarm(1);
myRTC.setA1Time(0, 0, 0, 0, 0b01111111, false, false, false);
myRTC.turnOnAlarm(1);
myRTC.checkIfAlarm(1);
// Use builtin LED to blink
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
}
void loop() {
// static variable to keep track of LED on/off state
static byte state = false;
// if alarm went of, do alarm stuff
// first call to checkIFAlarm does not clear alarm flag
if (myRTC.checkIfAlarm(1, false)) {
state = ~state;
digitalWrite(LED_BUILTIN, state);
// Clear alarm state
myRTC.checkIfAlarm(1, true);
}
// Loop delay to emulate other running code
delay(10);
}

View File

@@ -0,0 +1,38 @@
/*
oscillator_test.pde
Eric Ayars
4/11
Test/demo of oscillator routines for a DS3231 RTC.
Use a scope after loading this to check if things are
working as they should.
*/
#include <DS3231.h>
#include <Wire.h>
DS3231 myRTC;
byte j;
bool on = false;
void setup() {
// Start the I2C interface
Wire.begin();
// Start the serial interface
Serial.begin(57600);
}
void loop() {
for (j=0;j<4;j++) {
// invert state of 32kHz oscillator.
on = !on;
myRTC.enable32kHz(on);
// Turn on oscillator pin, frequency j
myRTC.enableOscillator(true, false, j);
delay(4000);
}
// So... The 32kHz oscillator (pin 1) will turn on or off once each 2s,
// and the oscillator out pin (pin 3) will cycle through frequencies.
}

View File

@@ -0,0 +1,113 @@
/*
DS3231_set.pde
Eric Ayars
4/11
Test of set-time routines for a DS3231 RTC
*/
#include <DS3231.h>
#include <Wire.h>
DS3231 myRTC;
byte year;
byte month;
byte date;
byte dOW;
byte hour;
byte minute;
byte second;
void getDateStuff(byte& year, byte& month, byte& date, byte& dOW,
byte& hour, byte& minute, byte& second) {
// Call this if you notice something coming in on
// the serial port. The stuff coming in should be in
// the order YYMMDDwHHMMSS, with an 'x' at the end.
boolean gotString = false;
char inChar;
byte temp1, temp2;
char inString[20];
byte j=0;
while (!gotString) {
if (Serial.available()) {
inChar = Serial.read();
inString[j] = inChar;
j += 1;
if (inChar == 'x') {
gotString = true;
}
}
}
Serial.println(inString);
// Read year first
temp1 = (byte)inString[0] -48;
temp2 = (byte)inString[1] -48;
year = temp1*10 + temp2;
// now month
temp1 = (byte)inString[2] -48;
temp2 = (byte)inString[3] -48;
month = temp1*10 + temp2;
// now date
temp1 = (byte)inString[4] -48;
temp2 = (byte)inString[5] -48;
date = temp1*10 + temp2;
// now Day of Week
dOW = (byte)inString[6] - 48;
// now hour
temp1 = (byte)inString[7] -48;
temp2 = (byte)inString[8] -48;
hour = temp1*10 + temp2;
// now minute
temp1 = (byte)inString[9] -48;
temp2 = (byte)inString[10] -48;
minute = temp1*10 + temp2;
// now second
temp1 = (byte)inString[11] -48;
temp2 = (byte)inString[12] -48;
second = temp1*10 + temp2;
}
void setup() {
// Start the serial port
Serial.begin(57600);
// Start the I2C interface
Wire.begin();
}
void loop() {
// If something is coming in on the serial line, it's
// a time correction so set the clock accordingly.
if (Serial.available()) {
getDateStuff(year, month, date, dOW, hour, minute, second);
myRTC.setClockMode(false); // set to 24h
//setClockMode(true); // set to 12h
myRTC.setYear(year);
myRTC.setMonth(month);
myRTC.setDate(date);
myRTC.setDoW(dOW);
myRTC.setHour(hour);
myRTC.setMinute(minute);
myRTC.setSecond(second);
// Test of alarm functions
// set A1 to one minute past the time we just set the clock
// on current day of week.
myRTC.setA1Time(dOW, hour, minute+1, second, 0x0, true,
false, false);
// set A2 to two minutes past, on current day of month.
myRTC.setA2Time(date, hour, minute+2, 0x0, false, false,
false);
// Turn on both alarms, with external interrupt
myRTC.turnOnAlarm(1);
myRTC.turnOnAlarm(2);
}
delay(1000);
}

View File

@@ -0,0 +1,157 @@
/*
DS3231_test.pde
Eric Ayars
4/11
Test/demo of read routines for a DS3231 RTC.
Turn on the serial monitor after loading this to check if things are
working as they should.
*/
#include <DS3231.h>
#include <Wire.h>
DS3231 myRTC;
bool century = false;
bool h12Flag;
bool pmFlag;
byte alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits;
bool alarmDy, alarmH12Flag, alarmPmFlag;
void setup() {
// Start the I2C interface
Wire.begin();
// Start the serial interface
Serial.begin(57600);
}
void loop() {
// send what's going on to the serial monitor.
// Start with the year
Serial.print("2");
if (century) { // Won't need this for 89 years.
Serial.print("1");
} else {
Serial.print("0");
}
Serial.print(myRTC.getYear(), DEC);
Serial.print(' ');
// then the month
Serial.print(myRTC.getMonth(century), DEC);
Serial.print(" ");
// then the date
Serial.print(myRTC.getDate(), DEC);
Serial.print(" ");
// and the day of the week
Serial.print(myRTC.getDoW(), DEC);
Serial.print(" ");
// Finally the hour, minute, and second
Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC);
Serial.print(" ");
Serial.print(myRTC.getMinute(), DEC);
Serial.print(" ");
Serial.print(myRTC.getSecond(), DEC);
// Add AM/PM indicator
if (h12Flag) {
if (pmFlag) {
Serial.print(" PM ");
} else {
Serial.print(" AM ");
}
} else {
Serial.print(" 24h ");
}
// Display the temperature
Serial.print("T=");
Serial.print(myRTC.getTemperature(), 2);
// Tell whether the time is (likely to be) valid
if (myRTC.oscillatorCheck()) {
Serial.print(" O+");
} else {
Serial.print(" O-");
}
// Indicate whether an alarm went off
if (myRTC.checkIfAlarm(1)) {
Serial.print(" A1!");
}
if (myRTC.checkIfAlarm(2)) {
Serial.print(" A2!");
}
// New line on display
Serial.println();
// Display Alarm 1 information
Serial.print("Alarm 1: ");
myRTC.getA1Time(alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag);
Serial.print(alarmDay, DEC);
if (alarmDy) {
Serial.print(" DoW");
} else {
Serial.print(" Date");
}
Serial.print(' ');
Serial.print(alarmHour, DEC);
Serial.print(' ');
Serial.print(alarmMinute, DEC);
Serial.print(' ');
Serial.print(alarmSecond, DEC);
Serial.print(' ');
if (alarmH12Flag) {
if (alarmPmFlag) {
Serial.print("pm ");
} else {
Serial.print("am ");
}
}
if (myRTC.checkAlarmEnabled(1)) {
Serial.print("enabled");
}
Serial.println();
// Display Alarm 2 information
Serial.print("Alarm 2: ");
myRTC.getA2Time(alarmDay, alarmHour, alarmMinute, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag);
Serial.print(alarmDay, DEC);
if (alarmDy) {
Serial.print(" DoW");
} else {
Serial.print(" Date");
}
Serial.print(" ");
Serial.print(alarmHour, DEC);
Serial.print(" ");
Serial.print(alarmMinute, DEC);
Serial.print(" ");
if (alarmH12Flag) {
if (alarmPmFlag) {
Serial.print("pm");
} else {
Serial.print("am");
}
}
if (myRTC.checkAlarmEnabled(2)) {
Serial.print("enabled");
}
// display alarm bits
Serial.println();
Serial.print("Alarm bits: ");
Serial.println(alarmBits, BIN);
Serial.println();
delay(1000);
}

View File

@@ -0,0 +1,51 @@
/*
Prints time stamps for 5 seconds using getXXX functions
Based on DS3231_set.pde
by Eric Ayars
4/11
Added printing back of time stamps and increased baud rate
(to better synchronize computer and RTC)
Andy Wickert
5/15/2011
*/
#include <DS3231.h>
#include <Wire.h>
DS3231 myRTC;
bool century = false;
bool h12Flag;
bool pmFlag;
void setup() {
// Start the serial port
Serial.begin(57600);
// Start the I2C interface
Wire.begin();
for (int i=0; i<5; i++){
delay(1000);
Serial.print(myRTC.getYear(), DEC);
Serial.print("-");
Serial.print(myRTC.getMonth(century), DEC);
Serial.print("-");
Serial.print(myRTC.getDate(), DEC);
Serial.print(" ");
Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC); //24-hr
Serial.print(":");
Serial.print(myRTC.getMinute(), DEC);
Serial.print(":");
Serial.println(myRTC.getSecond(), DEC);
}
}
void loop() {
}

View File

@@ -0,0 +1,42 @@
// now.pde
// Prints a snapshot of the current date and time along with the UNIX time
// Modified by Andy Wickert from the JeeLabs / Ladyada RTC library examples
// 5/15/11
#include <Wire.h>
#include <DS3231.h>
RTClib myRTC;
void setup () {
Serial.begin(57600);
Wire.begin();
delay(500);
Serial.println("Nano Ready!");
}
void loop () {
delay(1000);
DateTime now = myRTC.now();
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(' ');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
Serial.print(" since midnight 1/1/1970 = ");
Serial.print(now.unixtime());
Serial.print("s = ");
Serial.print(now.unixtime() / 86400L);
Serial.println("d");
}

View File

@@ -0,0 +1,96 @@
#include <Wire.h>
#include <DS3231.h>
void showTimeFormated(time_t t) {
#if defined (__AVR__)
t -= 946684800UL;
#endif
char buffer[50];
struct tm *ptm;
ptm = gmtime (&t);
const char * timeformat {"%a %F %X - weekday %w; CW %W"};
strftime(buffer, sizeof(buffer), timeformat, ptm);
Serial.print(buffer);
Serial.print("\n");
}
constexpr time_t tstmp {1660644000UL};
RTClib myRTC;
DS3231 Clock;
void setup () {
Serial.begin(115200);
Wire.begin();
delay(500);
Serial.println("\n\n\nTest of DS3231 - setEpoch()\n\n\n");
#if defined (__AVR__)
#warning using AVR platform
Serial.println("AVR Microcontroller Ready!");
Wire.begin();
#elif defined (ESP8266)
Serial.println("ESP8266 Microcontroller Ready!");
#warning using espressif platform
// SDA = 0, SCL = 2
Wire.begin(0U, 2U);
#endif
// set the Ds3131 with a specific UnixTimestamp
// ==> Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33
// ==> 1660644000
Serial.println("Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33");
Serial.println("UnixTimestamp - 1660644000");
// feed UnixTimeStamp and don' t use localtime
Clock.setEpoch(tstmp, false);
// set to 24h
Clock.setClockMode(false);
// Just for verification of DS3231 Data
// check now the data from ESP8266 and DS3231
// get year
bool century = false;
bool h12Flag;
bool pmFlag;
DateTime now = myRTC.now();
Serial.print("\n\n");
Serial.print(" DateTime of DS3231: ");
Serial.print(Clock.getYear(), DEC);
Serial.print("-");
Serial.print(Clock.getMonth(century), DEC);
Serial.print("-");
Serial.print(Clock.getDate(), DEC);
Serial.print(" ");
Serial.print(Clock.getHour(h12Flag, pmFlag), DEC);
Serial.print(":");
Serial.print(Clock.getMinute(), DEC);
Serial.print(":");
Serial.print(Clock.getSecond(), DEC);
Serial.print(" - weekday ");
Serial.print(Clock.getDoW(), DEC);
Serial.println();
Serial.print("\n\n");
Serial.print(" DateTime of RTC: ");
Serial.print(now.year(), DEC);
Serial.print("-");
Serial.print(now.month(), DEC);
Serial.print("-");
Serial.print(now.day(), DEC);
Serial.print(" ");
Serial.print(now.hour(), DEC);
Serial.print(":");
Serial.print(now.minute(), DEC);
Serial.print(":");
Serial.print(now.second(), DEC);
Serial.println();
Serial.print("\n\n Output of Struct tm: ");
showTimeFormated(tstmp);
}
void loop () {
}

View File

@@ -0,0 +1,182 @@
/*
Sets the time from input and prints back time stamps for 5 seconds
Based on DS3231_set.pde
by Eric Ayars
4/11
Added printing back of time stamps and increased baud rate
(to better synchronize computer and RTC)
Andy Wickert
5/15/2011
Clean for SAMD arch, add explanation, respect code-style and
fix interpretation of Serial input if used more than once
Olivier Staquet
4/26/2020
*/
#include <DS3231.h>
#include <Wire.h>
DS3231 myRTC;
byte year;
byte month;
byte date;
byte dow;
byte hour;
byte minute;
byte second;
bool century = false;
bool h12Flag;
bool pmFlag;
/*****************************************************************************************************
* Setup
* - Open Serial and Wire connection
* - Explain to the user how to use the program
*****************************************************************************************************/
void setup() {
// Start the serial port
Serial.begin(57600);
// Start the I2C interface
Wire.begin();
// Request the time correction on the Serial
delay(4000);
Serial.println("Format YYMMDDwhhmmssx");
Serial.println("Where YY = Year (ex. 20 for 2020)");
Serial.println(" MM = Month (ex. 04 for April)");
Serial.println(" DD = Day of month (ex. 09 for 9th)");
Serial.println(" w = Day of week from 1 to 7, 1 = Sunday (ex. 5 for Thursday)");
Serial.println(" hh = hours in 24h format (ex. 09 for 9AM or 21 for 9PM)");
Serial.println(" mm = minutes (ex. 02)");
Serial.println(" ss = seconds (ex. 42)");
Serial.println("Example for input : 2004095090242x");
Serial.println("-----------------------------------------------------------------------------");
Serial.println("Please enter the current time to set on DS3231 ended by 'x':");
}
/*****************************************************************************************************
* Loop
* - Each time we receive the time correction on the Serial
* - Set the clock of the DS3231
* - Echo the value from the DS3231 during 5 seconds
*****************************************************************************************************/
void loop() {
// If something is coming in on the serial line, it's
// a time correction so set the clock accordingly.
if (Serial.available()) {
inputDateFromSerial();
myRTC.setClockMode(false); // set to 24h
myRTC.setYear(year);
myRTC.setMonth(month);
myRTC.setDate(date);
myRTC.setDoW(dow);
myRTC.setHour(hour);
myRTC.setMinute(minute);
myRTC.setSecond(second);
// Give time at next five seconds
for (uint8_t i = 0; i < 5; i++){
delay(1000);
Serial.print(myRTC.getYear(), DEC);
Serial.print("-");
Serial.print(myRTC.getMonth(century), DEC);
Serial.print("-");
Serial.print(myRTC.getDate(), DEC);
Serial.print(" ");
Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC); //24-hr
Serial.print(":");
Serial.print(myRTC.getMinute(), DEC);
Serial.print(":");
Serial.println(myRTC.getSecond(), DEC);
}
// Notify that we are ready for the next input
Serial.println("Please enter the current time to set on DS3231 ended by 'x':");
}
delay(1000);
}
/*****************************************************************************************************
* inputDateFromSerial
* - Read and interpret the data from the Serial input
* - Store the data in global variables
*****************************************************************************************************/
void inputDateFromSerial() {
// Call this if you notice something coming in on
// the serial port. The stuff coming in should be in
// the order YYMMDDwHHMMSS, with an 'x' at the end.
boolean isStrComplete = false;
char inputChar;
byte temp1, temp2;
char inputStr[20];
uint8_t currentPos = 0;
while (!isStrComplete) {
if (Serial.available()) {
inputChar = Serial.read();
inputStr[currentPos] = inputChar;
currentPos += 1;
// Check if string complete (end with "x")
if (inputChar == 'x') {
isStrComplete = true;
}
}
}
Serial.println(inputStr);
// Find the end of char "x"
int posX = -1;
for(uint8_t i = 0; i < 20; i++) {
if(inputStr[i] == 'x') {
posX = i;
break;
}
}
// Consider 0 character in ASCII
uint8_t zeroAscii = '0';
// Read Year first
temp1 = (byte)inputStr[posX - 13] - zeroAscii;
temp2 = (byte)inputStr[posX - 12] - zeroAscii;
year = temp1 * 10 + temp2;
// now month
temp1 = (byte)inputStr[posX - 11] - zeroAscii;
temp2 = (byte)inputStr[posX - 10] - zeroAscii;
month = temp1 * 10 + temp2;
// now date
temp1 = (byte)inputStr[posX - 9] - zeroAscii;
temp2 = (byte)inputStr[posX - 8] - zeroAscii;
date = temp1 * 10 + temp2;
// now Day of Week
dow = (byte)inputStr[posX - 7] - zeroAscii;
// now Hour
temp1 = (byte)inputStr[posX - 6] - zeroAscii;
temp2 = (byte)inputStr[posX - 5] - zeroAscii;
hour = temp1 * 10 + temp2;
// now Minute
temp1 = (byte)inputStr[posX - 4] - zeroAscii;
temp2 = (byte)inputStr[posX - 3] - zeroAscii;
minute = temp1 * 10 + temp2;
// now Second
temp1 = (byte)inputStr[posX - 2] - zeroAscii;
temp2 = (byte)inputStr[posX - 1] - zeroAscii;
second = temp1 * 10 + temp2;
}

View File

@@ -0,0 +1,33 @@
DS3231 KEYWORD1
RTClib KEYWORD1
DateTime KEYWORD1
now KEYWORD2
secondstime KEYWORD2
unixtime KEYWORD2
getSecond KEYWORD2
getMinute KEYWORD2
getHour KEYWORD2
getDoW KEYWORD2
getDate KEYWORD2
getMonth KEYWORD2
getYear KEYWORD2
setSecond KEYWORD2
setMinute KEYWORD2
setHour KEYWORD2
setDoW KEYWORD2
setDate KEYWORD2
setMonth KEYWORD2
setYear KEYWORD2
setClockMode KEYWORD2
getTemperature KEYWORD2
getA1Time KEYWORD2
getA2Time KEYWORD2
setA1Time KEYWORD2
setA2Time KEYWORD2
turnOnAlarm KEYWORD2
turnOffAlarm KEYWORD2
checkAlarmEnabled KEYWORD2
checkIfAlarm KEYWORD2
enableOscillator KEYWORD2
enable32kHz KEYWORD2
oscillatorCheck KEYWORD2

View File

@@ -0,0 +1,10 @@
name=DS3231
version=1.1.2
author=Andrew Wickert <awickert@umn.edu>, Eric Ayars, Jean-Claude Wippler, Northern Widget LLC <info@northernwidget.com>
maintainer=Andrew Wickert <awickert@umn.edu>
sentence=Arduino library for the DS3231 real-time clock (RTC)
paragraph=Abstracts functionality for clock reading, clock setting, and alarms for the DS3231 high-precision real-time clock. This is a splice of Ayars' (http://hacks.ayars.org/2011/04/ds3231-real-time-clock.html) and Jeelabs/Ladyada's (https://github.com/adafruit/RTClib) libraries.
category=Timing
url=https://github.com/NorthernWidget/DS3231
architectures=*
includes=DS3231.h

View File

@@ -0,0 +1,80 @@
/*
getAXTimeTest.ino
Jacob Nuernberg
08/22
Testing getAXTimeMethods.
Code should generate the following output every 5s on the Serial Monitor:
-> Initialize AlarmBits: 0
-> getA1Time(): 1110
-> getA2Time(): 1111110
-> getA1Time(): 1110
-> getA2Time(): 1110000
Tested on:
- Arduino nano
*/
#include <DS3231.h>
#include <Wire.h>
// Setup clock
DS3231 myRTC;
void setup() {
// Begin I2C communication
Wire.begin();
// Begin Serial communication
Serial.begin(57600);
// Setup alarm one to fire every minute
// No need to turn Alarm one on.
myRTC.turnOffAlarm(1);
myRTC.setA1Time(0, 0, 0, 0, 0b01111110, false, false, false);
myRTC.checkIfAlarm(1);
// Setup alarm two to fire every minute
// No need to turn Alarm two on.
myRTC.turnOffAlarm(2);
myRTC.setA2Time(0, 0, 0, 0b01111110, false, false, false);
myRTC.checkIfAlarm(2);
}
void loop() {
// Initialize AlarmBits
byte AlarmBits = 0x0;
Serial.print("Initialize AlarmBits: ");
Serial.println(AlarmBits, BIN);
// Initialize Others
byte ADay, AHour, AMinute, ASecond;
bool ADy, Ah12, APM;
// getA1Time (not clearing).
// Expected AlarmBits = 0x(0000)1110
myRTC.getA1Time(ADay, AHour, AMinute, ASecond, AlarmBits, ADy, Ah12, APM);
Serial.print("getA1Time(): ");
Serial.println(AlarmBits, BIN);
// getA2Time (not clearing).
// Expected AlarmBits = 0x01111110
myRTC.getA2Time(ADay, AHour, AMinute, AlarmBits, ADy, Ah12, APM);
Serial.print("getA2Time(): ");
Serial.println(AlarmBits, BIN);
// getA1Time (clearing).
// Expected AlarmBits = 0x(0000)1110
myRTC.getA1Time(ADay, AHour, AMinute, ASecond, AlarmBits, ADy, Ah12, APM, true);
Serial.print("getA1Time(): ");
Serial.println(AlarmBits, BIN);
// getA2Time (clearing).
// Expected AlarmBits = 0x01110000
myRTC.getA2Time(ADay, AHour, AMinute, AlarmBits, ADy, Ah12, APM, true);
Serial.print("getA2Time(): ");
Serial.println(AlarmBits, BIN);
delay(5000);
}