DS3231_RTC.cpp
#include <DS3231_RTC.h>

datetime unixtime isleapyear now
getSecond getMinute getHour getDow
getDate getMonth getYear setEpoch
setSecond setMinute setHour setDow
setDate setMonth setYear setClockMode
getTemperature getAlarm1 getAlarm2 setAlarm1
setAlarm2 getA1Time getA2Time setA1Time
setA2Time turnOnAlarm turnOffAlarm checkAlarmEnabled
checkIfAlarm enableOscillator enable32kHz oscillatorCheck
decToBcd bcdToDec readControlByte writeControlByte

/*H*******************************************************
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 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 
setting of PM flag for 12:xx PM.  These address certain DS3231 errors in properly setting 
AM/PM (bit 5) flag in 02h register when passing from AM to PM and PM to AM.
David Merrifield 04/14/2020
Released into public domain.
********************************************************/
#include "DS3231.h"
// These included for DateTime class inclusion; will try to find a way to
// not need them in future...
#if defined(__AVR__)
#include 
#elif defined(ESP8266)
#include 
#endif
// Changed following to work on 1.0
//#include "WProgram.h"
#include 

//************************* DEFINES ************************************
#define CLOCK_ADDRESS 0x68
#define SECONDS_FROM_1970_TO_2000 946684800

//************************* PROTOTYPES ************************************

//************************* VARIABLES ************************************
// Constructor
DS3231::
DS3231() : _Wire(Wire) 
{
// nothing to do for this constructor.
}
/*F********************************************************************
*
**********************************************************************/
DS3231::
DS3231( TwoWire  & w) : _Wire(w) 
{
}
// Utilities from JeeLabs/Ladyada
////////////////////////////////////////////////////////////////////////////////
// utility code, some of this could be exposed in 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 };

/*F********************************************************************
* 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 );
}
/*F********************************************************************
*
**********************************************************************/
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 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


* 4 PARTS /*F******************************************************************** * **********************************************************************/ 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( yOff ); if( days < 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; } /*F******************************************************************** * **********************************************************************/ 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; } /*F******************************************************************** * 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 %c %d", buff, &d, &y); yOff = y >= 2000 ? y - 2000 : y; m = (strstr( month_names, buff) - month_names) / 3 + 1; sscanf( time, "%c:%c:%c", &hh, &mm, &ss); }
/*F******************************************************************** * 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 ); }
/*F******************************************************************** * 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); } **********************************************************************/ bool isleapYear( const uint8_t y ) { if( y & 3 ) // CHECK IF DIVISIBLE BY 4 return( false ); // ONLY CHECK OTHER, WHEN FIRST FAILED return( (y % 100 || y % 400 == 0 )); }
/*F******************************************************************** * **********************************************************************/ DateTime RTClib:: now( TwoWire & _Wire ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0 ); // FIRST REGISTER ADDRESS (SECONDS) // 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 )); }
/*F******************************************************************** * 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() ) ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231:: getMinute() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x01 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return( bcdToDec( _Wire.read() ) ); }
/*F******************************************************************** * **********************************************************************/ 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 ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231:: getDoW() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x03 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return( bcdToDec( _Wire.read()) ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231:: getDate() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x04 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return( bcdToDec( _Wire.read()) ); }
/*F******************************************************************** * **********************************************************************/ 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)) ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231:: getYear() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x06 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return( bcdToDec( _Wire.read() ) ); }
/*F******************************************************************** * setEpoch function gives epoch as parameter and feeds RTC epoch = UnixTime and starts at 01.01.1970 00:00:00 **********************************************************************/ void DS3231:: setEpoch( time_t epoch, bool flag_localtime ) { 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 + 1); setDate( tmnow.tm_mday); setMonth( tmnow.tm_mon + 1); setYear( tmnow.tm_year - 100); }
/*F******************************************************************** * SETS SECONDS ALSO RESETS OSCILLATOR STOP FLAG, WHICH IS SET WHENEVER POWER IS INTERRUPTED. **********************************************************************/ void DS3231:: setSecond( byte Second ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x00 ); _Wire.write( decToBcd( Second ) ); _Wire.endTransmission(); byte temp_buffer = readControlByte( 1 ); // CLEAR OSF FLAG writeControlByte( (temp_buffer & 0b01111111), 1 ); }
/*F******************************************************************** * Sets minutes **********************************************************************/ void DS3231:: setMinute( byte Minute) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x01 ); _Wire.write( decToBcd( Minute ) ); _Wire.endTransmission(); }
/*F******************************************************************** * Following setHour revision by David Merrifield 4/14/2020 correcting handling of 12-hour clock Sets hour, without changing 12/24h mode. hour must be in 24h format. **********************************************************************/ void DS3231:: setHour( byte Hour ) { bool h12; byte temp_hour; // Start by figuring out what 12/24 mode is _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x02 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); h12 = ( _Wire.read() & 0b01000000 ); if( h12 ) // IF H12 IS TRUE, IT'S 12H MODE; FALSE IS 24H { // 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(); }
/*F******************************************************************** * SETS DAY OF WEEK **********************************************************************/ void DS3231:: setDoW(byte DoW) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x03 ); _Wire.write( decToBcd( DoW ) ); _Wire.endTransmission(); }
/*F******************************************************************** * SETS DATE **********************************************************************/ void DS3231:: setDate( byte Date ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x04 ); _Wire.write( decToBcd( Date ) ); _Wire.endTransmission(); }
/*F******************************************************************** * SETS MONTH **********************************************************************/ void DS3231:: setMonth( byte Month ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x05 ); _Wire.write( decToBcd( Month ) ); _Wire.endTransmission(); }
/*F******************************************************************** * SETS YEAR **********************************************************************/ void DS3231:: setYear( byte Year) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x06 ); _Wire.write( decToBcd( Year ) ); _Wire.endTransmission(); }
/*F******************************************************************** * SETS MODE TO 12-HOUR (TRUE) OR 24-HOUR (FALSE). ONE THING THAT BOTHERS ME ABOUT HOW I'VE WRITTEN THIS IS THAT IF READ AND RIGHT HAPPEN AT RIGHT HOURLy MILLISECND, CLOCK WILL BE SET BACK AN HOUR. NOT SURE HOW TO DO IT BETTER, THOUGH, AND AS LONG AS ONE DOESN'T SET MODE FREQUENTLY IT'S A VERY MINIMAl RISK. IT'S ZERO RISK IF YOU CALL THIS before SETTING HOUR, SINCE SEThOUR() FUNCTION DOESN'T CHANGE THIS MODE. **********************************************************************/ void DS3231:: setClockMode( bool h12) { byte temp_buffer; _Wire.beginTransmission( CLOCK_ADDRESS ); // START BY READING BYTE 0X02 _Wire.write( 0x02 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); temp_buffer = _Wire.read(); if( h12 ) // SET FLAG TO REQUESTED VALUE temp_buffer = temp_buffer | 0b01000000; else temp_buffer = temp_buffer & 0b10111111; _Wire.beginTransmission( bCLOCK_ADDRESS ); // WRITE BYTE _Wire.write( 0x02 ); _Wire.write( temp_buffer ); _Wire.endTransmission(); }
/*F******************************************************************** * Checks internal thermometer on DS3231 and returns 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 **********************************************************************/ float DS3231:: getTemperature() { 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 HI BYTE, ADD LO temp3231 = ( (float)itemp / 256.0 ); // SCALE AND RETURN } else temp3231 = -9999; // IMPOSSIBLE TEMPERATURE; ERROR VALUE return( temp3231 ); }
/*F******************************************************************** * ADDITIONAL CODE FROM AFFAN **********************************************************************/ void DS3231:: getAlarm1( byte &A1Hour, byte &A1Minute ) { byte temp_buffer; byte A1Second; _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); temp_buffer = _Wire.read(); // GET A1M2 AND A1 MINUTES A1Minute = bcdToDec( temp_buffer & 0b01111111); temp_buffer = _Wire.read(); // GET A1M3 AND A1 HOUR A1Hour = bcdToDec( temp_buffer & 0b00111111); // 24-HOUR }
/*F******************************************************************** * **********************************************************************/ void DS3231:: getAlarm2( byte & A2Hour, byte & A2Minute) { 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); temp_buffer = _Wire.read(); // GET A2M3 AND A2 HOUR A2Hour = bcdToDec( temp_buffer & 0b00111111); // 24-HOUR }
/*F******************************************************************** * Sets alarm-1 date and time on DS3231, using A1* information **********************************************************************/ void DS3231:: setAlarm1( byte A1Hour, byte A1Minute) { byte temp_buffer; byte A1Day = 0x0; byte A1Second = 0x0; byte AlarmBits = 0x0; bool A1Dy = true; bool A1h12 = false; bool A1PM = true; _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 HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd( A1Hour ) | 0b01100000; } else { // MORNING CONVERT 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); _Wire.write( temp_buffer ); // A1 HOUR IS FIGURED OUT, SEND IT // 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 ); _Wire.endTransmission(); // ALL DONE! }
/*F******************************************************************** * Sets alarm-2 date and time on DS3231, using A2* information **********************************************************************/ void DS3231:: setAlarm2( byte A2Hour, byte A2Minute ) { byte temp_buffer; byte A2Day = 0x0; byte AlarmBits = 0x0; bool A2Dy = true; bool A2h12 = false; bool A2PM = true; _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x0b ); // A1 STARTS AT 0BH // SEND A2 MINUTE AND A2M2 _Wire.write( decToBcd( A2Minute ) | ((AlarmBits & 0b00010000) << 3)); if( A2h12 ) // FIGURE OUT A2 HOUR { // 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 HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd( A2Hour ) | 0b01100000; } else // MORNING { // CONVERT 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 ); _Wire.endTransmission(); // ALL DONE! }
/*F******************************************************************** * LAST ADDITIONAL CODE FROM AFFAN **********************************************************************/ 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 ); }
/*F******************************************************************** * **********************************************************************/ 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); }
/*F******************************************************************** * SETS ALARM-1 DATE AND TIME ON DS3231, USING A1* INFORMATION **********************************************************************/ void DS3231:: setA1Time( 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 ); // 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)); if( A1h12 ) // FIGURE OUT A1 HOUR { // 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 HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd( A1Hour ) | 0b01100000; } else // Morning { // CONVERT 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 ); _Wire.endTransmission(); // ALL DONE! }
/*F******************************************************************** * SETS ALARM-2 DATE AND TIME ON DS3231, USING A2* INFORMATION **********************************************************************/ void DS3231:: setA2Time( byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits , bool A2Dy, bool A2h12, bool A2PM) { 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 HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd( A2Hour ) | 0b01100000; } else // MORNING { // CONVERT 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 ); _Wire.write( temp_buffer ); // A2 HOUR IS FIGURED OUT, SEND IT // 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 ); _Wire.endTransmission(); // ALL DONE! }
/*F******************************************************************** * TURNS ON ALARM NUMBER "Alarm". DEFAULTS TO 2 IF ALARM IS NOT 1 **********************************************************************/ void DS3231:: turnOnAlarm( byte Alarm ) { 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 ); }
/*F******************************************************************** * turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1. Leaves interrupt pin alone. **********************************************************************/ void DS3231:: turnOffAlarm( byte Alarm ) { 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); }
/*F******************************************************************** * CHECKS WHETHER GIVEN ALARM IS ENABLED **********************************************************************/ bool DS3231:: checkAlarmEnabled( byte Alarm) { byte result = 0x0; byte temp_buffer = readControlByte( 0 ); if( Alarm == 1) result = temp_buffer & 0b00000001; else result = temp_buffer & 0b00000010; return( result ); }
/*F******************************************************************** * 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. **********************************************************************/ bool DS3231:: checkIfAlarm( byte Alarm ) { byte result; byte temp_buffer = readControlByte( 1 ); if( Alarm == 1) { result = temp_buffer & 0b00000001; // DID ALARM 1 GO OFF? temp_buffer = temp_buffer & 0b11111110; // CLEAR FLAG } else { result = temp_buffer & 0b00000010; // DID ALARM 2 GO OFF? temp_buffer = temp_buffer & 0b11111101; // CLEAR FLAG } writeControlByte( temp_buffer, 1); return( result ); }
/*F******************************************************************** 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 DS3231:: enableOscillator( bool TF, bool battery, byte frequency ) { 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 temp_buffer = temp_buffer & 0b10111111; // TURN OFF BBSQW FLAG if( TF ) temp_buffer = temp_buffer & 0b01111011; // SET ~EOSC= 0 & INTCN = ZRO else temp_buffer = temp_buffer | 0b10000000; // SET ~EOSC= 1, INTCN SAME frequency = frequency << 3; // SHIFT FREQUENCY INTO BITS 3 AND 4 AND SET temp_buffer = temp_buffer | frequency; writeControlByte( temp_buffer, 0); // AND WRITE CONTROL BITS }
/*F******************************************************************** * TURN 32kHz PIN ON OR OFF **********************************************************************/ void DS3231:: enable32kHz( bool TF ) { byte temp_buffer = readControlByte( 1 ); if( TF ) temp_buffer = temp_buffer | 0b00001000; // TURN ON 32kHz PIN else temp_buffer = temp_buffer & 0b11110111; // TURN OFF 32kHz PIN writeControlByte( temp_buffer, 1 ); }
/*F******************************************************************** * Returns false if oscillator has been off for some reason. If this is case, time is probably not correct. **********************************************************************/ bool DS3231:: oscillatorCheck() { byte temp_buffer = readControlByte( 1 ); bool result = true; if( temp_buffer & 0b10000000) result = false; // OSCILLATOR sTOP fLAG (OSF) IS SET, SO RETURN FALSE return( result ); } /***************************************** Private Functions *****************************************/
/*F******************************************************************** * CONVERT NORMAL DECIMAL NUMBERS TO BINARY CODED DECIMAL **********************************************************************/ byte DS3231:: decToBcd( byte val ) { return( ( (val/10*16) + (val%10) ) ); }
/*F******************************************************************** * CONVERT BINARY CODED DECIMAL TO NORMAL DECIMAL NUMBERS **********************************************************************/ byte DS3231:: bcdToDec( byte val ) { return( ( (val/16*10) + (val%16) ) ); }
/*F******************************************************************** * Read selected control byte first byte (0) is 0x0e, second (1) is 0x0f **********************************************************************/ byte DS3231:: readControlByte( bool which ) { _Wire.beginTransmission(CLOCK_ADDRESS); if( which ) _Wire.write( 0x0f ); // SECOND CONTROL BYTE else _Wire.write( 0x0e ); // FIRST CONTROL BYTE _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return( _Wire.read() ); }
/*F******************************************************************** * WRITE SELECTED CONTROL BYTE. WHICH =false -> 0x0e, true->0x0f. **********************************************************************/ void DS3231:: writeControlByte( byte control, bool which) { _Wire.beginTransmission( CLOCK_ADDRESS ); if( which ) _Wire.write( 0x0f ); else _Wire.write( 0x0e ); _Wire.write( control ); _Wire.endTransmission(); }