diff --git a/DS3231.cpp b/DS3231.cpp index 79a973a..43ae6da 100644 --- a/DS3231.cpp +++ b/DS3231.cpp @@ -1,30 +1,5 @@ /* -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. + DS3231.cpp: DS3231 Real-Time Clock library */ #include "DS3231.h" @@ -36,47 +11,21 @@ Released into the public domain. #elif defined(ESP8266) #include #endif -// Changed the following to work on 1.0 -//#include "WProgram.h" -#include - -#define CLOCK_ADDRESS 0x68 - -#define SECONDS_FROM_1970_TO_2000 946684800 - - -// Constructor +/** + * @brief Construct a new DS3231::DS3231 object + * + */ 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; +/** + * @brief Construct a new DS3231::DS3231 object + * + * @param w reference of twoWire + */ +DS3231::DS3231(TwoWire &twowire) : _Wire(twowire) { } /***************************************** @@ -90,178 +39,195 @@ static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) { * 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); +/** + * @brief Construct a new Date Time:: Date Time object + * + * @param timestamp + */ +DateTime::DateTime (time_t unix_timestamp) +: _unix_timestamp{unix_timestamp}, _y2k_timestamp{unix_timestamp - UNIX_OFFSET} +{ + gmtime_r(&_unix_timestamp, &_tm); +} + +/** + * @brief Construct a new Date Time:: Date Time object + * + * @param year year e.g. 2022 + * @param month months since January - [ 1...12 ] + * @param day day of the month - [ 1...31 ] + * @param hour hours since midnight - [ 0...23 ] + * @param min inutes after the hour - [ 0...59 ] + * @param sec seconds after the minute - [ 0...59 ] + * @param wday wdays since Sunday - [ 1...7 ] + * @param dst Daylight Saving Time flag + */ +DateTime::DateTime(int16_t year, int8_t month, int8_t day, int8_t hour, int8_t min, int8_t sec, int8_t wday, int16_t yday, int16_t dst) +{ + _tm.tm_sec = sec; + _tm.tm_min = min; + _tm.tm_hour = hour; + _tm.tm_mday = day; + _tm.tm_mon = month-1; + _tm.tm_year = year-1900; + _tm.tm_yday = yday; + _tm.tm_wday = wday-1; + _tm.tm_isdst = dst; + + set_timstamps(); +} + + +/** + * @brief Construct a new Date Time:: Date Time object by givin the precompiler marcos + * as __DATE__ and __TIME__ + * + * @param date as Mmm dd yyyy (e.g. "Jan 14 2012") + * @param time as HH:MM:SS (e.g. "23:59:01") + */ +DateTime::DateTime(const char *date, const char *time) { + static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + static char month_buff[4] = {'0','0','0','0'}; + int year, day; + sscanf(date, "%s %2d %4d", month_buff, &day, &year); + int month = (strstr(month_names, month_buff) - month_names) / 3 + 1; + _tm.tm_year = year-1900; + _tm.tm_mon = month-1; + _tm.tm_mday = day; + byte hour, min, sec; + sscanf(time, "%hhu:%hhu:%hhu", &hour, &min, &sec); + _tm.tm_hour = hour; + _tm.tm_min = min; + _tm.tm_sec = sec; + set_timstamps(); +} + +/** + * @brief set_timstamps fom struct tm entries + * + */ +void DateTime::set_timstamps() { +#if defined (__AVR__) + _y2k_timestamp = mktime(&_tm); + _unix_timestamp = _y2k_timestamp + UNIX_OFFSET; +#else + _unix_timestamp = mktime(&_tm); + _y2k_timestamp = _unix_timestamp - UNIX_OFFSET; +#endif } -// 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; +/** + * @brief smart time print function based on the standard strftime function + * see: https://en.cppreference.com/w/cpp/chrono/c/strftime + * + * @param buffer buffer for time string + * @param buffersize size of buffer + * @param formatSpec define format see strftime + * @return size_t lenth of used buffer + */ +size_t DateTime::show_DateTime(char *buffer, size_t buffersize, const char *formatSpec) { + size_t len {strftime(buffer, buffersize, formatSpec, &_tm)}; + return len; } // 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); +// static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); } +static uint8_t bcd2bin (uint8_t val) { + return val - 6 * (val >> 4); } 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); + _Wire.beginTransmission(CLOCK_ADDRESS); + // This is the first register address (Seconds) + _Wire.write(0); + // We'll read from here on for 7 bytes from registers: + // seconds, minutes, hours, day(1...7), date(1...31), month, year. + _Wire.endTransmission(); + + _Wire.requestFrom(CLOCK_ADDRESS, 7); + int8_t sec = bcd2bin(_Wire.read() & 0x7F); + int8_t min = bcd2bin(_Wire.read()); + int8_t hour = bcd2bin(_Wire.read()); + int8_t wday = bcd2bin(_Wire.read())-1; + int8_t day = bcd2bin(_Wire.read()); + int8_t month = bcd2bin(_Wire.read()); + int16_t year = bcd2bin(_Wire.read()) + 2000; + int16_t yday = 0; + int16_t dst = 0; + + // add yday and DST calculation if needed + // use the complete set also yday and dst to keep in mind the parameters + return DateTime{year, month, day, hour, min, sec, wday, yday, dst}; } -///// 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()); + _Wire.beginTransmission(CLOCK_ADDRESS); + _Wire.write(0x00); + _Wire.endTransmission(); + return getRegisterValue(); } byte DS3231::getMinute() { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x01); - _Wire.endTransmission(); - - _Wire.requestFrom(CLOCK_ADDRESS, 1); - return bcdToDec(_Wire.read()); + _Wire.beginTransmission(CLOCK_ADDRESS); + _Wire.write(0x01); + _Wire.endTransmission(); + return getRegisterValue(); } 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 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()); + _Wire.beginTransmission(CLOCK_ADDRESS); + _Wire.write(0x03); + _Wire.endTransmission(); + return getRegisterValue(); } byte DS3231::getDate() { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x04); - _Wire.endTransmission(); - - _Wire.requestFrom(CLOCK_ADDRESS, 1); - return bcdToDec(_Wire.read()); + _Wire.beginTransmission(CLOCK_ADDRESS); + _Wire.write(0x04); + _Wire.endTransmission(); + return getRegisterValue(); } byte DS3231::getMonth(bool& Century) { - byte temp_buffer; - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x05); - _Wire.endTransmission(); + 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)) ; + _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()); + _Wire.beginTransmission(CLOCK_ADDRESS); + _Wire.write(0x06); + _Wire.endTransmission(); + return getRegisterValue(); } // setEpoch function gives the epoch as parameter and feeds the RTC @@ -269,222 +235,222 @@ byte DS3231::getYear() { // 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; + epoch -= UNIX_OFFSET; #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); + 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); + // 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(); + // 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(); + // 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(); + // 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(); + // 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(); + // 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(); + // 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(); + // 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. + // 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 + // 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; + 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); + // 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()) { + // 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 { + } + else { temp3231 = -9999; // Impossible temperature; error value - } + } - return temp3231; + 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); - } + 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) { @@ -495,41 +461,41 @@ void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second } 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); - } + 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) { @@ -540,229 +506,229 @@ void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBit } 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(); + // 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(); + // 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); + // 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); + // 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; + // 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; + // 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; + // 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); + // 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); + // 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; + // 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; } /***************************************** @@ -771,39 +737,39 @@ bool DS3231::oscillatorCheck() { byte DS3231::decToBcd(byte val) { // Convert normal decimal numbers to binary coded decimal - return ( (val/10*16) + (val%10) ); + 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) ); + 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(); + // 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(); + // 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(); } diff --git a/DS3231.h b/DS3231.h index 6f27495..87dd604 100644 --- a/DS3231.h +++ b/DS3231.h @@ -9,18 +9,12 @@ * (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 +#ifndef __DS3231_H__ +#define __DS3231_H__ // Changed the following to work on 1.0 //#include "WProgram.h" @@ -28,186 +22,241 @@ #include #include -// DateTime (get everything at once) from JeeLabs / Adafruit -// Simple general-purpose date/time class (no TZ / DST / leap second handling!) +#define CLOCK_ADDRESS 0x68 + +#if !defined (UNIX_OFFSET) +// SECONDS_FROM_1970_TO_2000 +// Difference between the Y2K and the UNIX epochs, in seconds. +// To convert a Y2K timestamp to UNIX... +#define UNIX_OFFSET 946684800UL +#endif + +#if !defined (NTP_OFFSET) +// SECONDS_FROM_1900_TO_2000 +// Difference between the Y2K and the NTP epochs, in seconds. +// To convert a Y2K timestamp to NTP... +#define NTP_OFFSET 3155673600UL +#endif + +// 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 }; + + +// DateTime class restructured by using standardized time functions 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; -}; + public: + DateTime (time_t unix_timestamp = 0); -// Checks if a year is a leap year -bool isleapYear(const uint16_t); + DateTime ( int16_t year, int8_t month, int8_t mday, + int8_t hour = 0, int8_t min = 0, int8_t sec = 0, + int8_t wday = 0, int16_t yday = 0, int16_t dst = 0); + + DateTime (const char *date, const char *time); + + int16_t getYear() const { return _tm.tm_year + 1900; } + int8_t getMonth() const { return _tm.tm_mon + 1; } + int8_t getDay() const { return _tm.tm_mday; } + int8_t getHour() const { return _tm.tm_hour; } + int8_t getMinute() const { return _tm.tm_min; } + int8_t getSecond() const { return _tm.tm_sec; } + int8_t getWeekDay() const { return _tm.tm_wday; } + int16_t getDST() const { return _tm.tm_isdst; } + size_t show_DateTime(char *buffer, size_t buffersize, const char *formatSpec = "%a %h %d %T %Y"); + + // time_t value as seconds since 1/1/2000 + time_t getY2kTime() const { return _y2k_timestamp; } + + // time_t value 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 + time_t getUnixTime() const { return _unix_timestamp; } + + private: + void set_timstamps(); + + protected: + time_t _unix_timestamp; + time_t _y2k_timestamp; + struct tm _tm; +}; class RTClib { - public: - // Get date and time snapshot - static DateTime now(TwoWire & _Wire = Wire); + 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. + public: + // Constructor + DS3231(); + DS3231(TwoWire &twowire); + + TwoWire &_Wire; -}; + // Time-retrieval functions + + // the getter functions retrieve current values of the registers. + byte getRegisterValue() { + _Wire.requestFrom(CLOCK_ADDRESS, 1); + return bcdToDec(_Wire.read()); + } + + byte getSecond(); + byte getMinute(); + // In addition to returning the hour register, this function + // returns the values of the 12/24-hour flag and the AM/PM flag. + byte getHour(bool& h12, bool& PM_time); + + byte getDoW(); + byte getDate(); + + // Also sets the flag indicating century roll-over. + byte getMonth(bool& Century); + + // Last 2 digits only e.g. 2023 => 23 + byte getYear(); + + + // ************************************ + // 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); + + // In addition to setting the seconds, this clears the + // "Oscillator Stop Flag". + void setSecond(byte Second); + void setMinute(byte Minute); + void setHour(byte Hour); + // Sets the Day of the Week (1...7); + void setDoW(byte DoW); + void setDate(byte Date); + void setMonth(byte Month); + // Last two digits of the year e.g. 23 + void setYear(byte Year); + // Set 12/24h mode. True is 12-h, false is 24-hour. + void setClockMode(bool h12); + + + // ************************************ + // 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. + */ + + // Same as getA1Time();, but A2 only goes on seconds == 00. + void getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM); + + // Same as getA1Time();, but clears byte AlarmBits. + 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); + + // Set the details for Alarm 1 + void setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM); + + // Set the details for Alarm 2 + void setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM); + + // Enables alarm 1 or 2 and the external interrupt pin. + // If Alarm != 1, it assumes Alarm == 2. + void turnOnAlarm(byte Alarm); + + // Disables alarm 1 or 2 (default is 2 if Alarm != 1); + // and leaves the interrupt pin alone. + void turnOffAlarm(byte Alarm); + + // Returns T/F to indicate whether the requested alarm is + // enabled. Defaults to 2 if Alarm != 1. + bool checkAlarmEnabled(byte Alarm); + + // Checks whether the indicated alarm (1 or 2, 2 default); + // has been activated. Always clears flag. + bool checkIfAlarm(byte Alarm); + + // Checks whether the indicated alarm (1 or 2, 2 default); + // has been activated. IF clearflag is set, clears alarm flag. + bool checkIfAlarm(byte Alarm, bool clearflag); + + + // ************************************ + // Oscillator functions + // ************************************ + + // 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 enableOscillator(bool TF, bool battery, byte frequency); + + // Turns the 32kHz output pin on (true); or off (false). + void enable32kHz(bool TF); + + // 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();. + bool oscillatorCheck(); + + + private: + // Convert normal decimal numbers to binary coded decimal + byte decToBcd(byte val); + // Convert binary coded decimal to normal decimal numbers + byte bcdToDec(byte val); + + + protected: + // Read selected control byte: (0); reads 0x0e, (1) reads 0x0f + byte readControlByte(bool which); + + // Write the selected control byte. + // which == false -> 0x0e, true->0x0f. + void writeControlByte(byte control, bool which); +}; #endif diff --git a/README.md b/README.md index 7bc7745..fcfc1f1 100644 --- a/README.md +++ b/README.md @@ -230,13 +230,17 @@ A limited DateTime class is defined in this DS3231.h library. The link, above, p [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() +- getYear() +- getMonth() +- getDay() +- getHour() +- getMinute() +- getSecond() +- getWeekDay() +- getDST() +- show_DateTime() +- getUnixTime() +- getY2KTime()

The Special RTClib::now() Function

diff --git a/examples/AdvanceAlarm/AdvanceAlarm.ino b/examples/AdvanceAlarm/AdvanceAlarm.ino index 583f613..024c999 100644 --- a/examples/AdvanceAlarm/AdvanceAlarm.ino +++ b/examples/AdvanceAlarm/AdvanceAlarm.ino @@ -135,14 +135,14 @@ void loop() { Serial.print("Turning LED "); Serial.print((state ? "ON" : "OFF")); Serial.print(" at "); - Serial.print(alarmDT.hour()); + Serial.print(alarmDT.getHour()); Serial.print(":"); - Serial.print(alarmDT.minute()); + Serial.print(alarmDT.getMinute()); Serial.print(":"); - Serial.println(alarmDT.second()); + Serial.println(alarmDT.getSecond()); // extract the DateTime values as a timestamp - uint32_t nextAlarm = alarmDT.unixtime(); + uint32_t nextAlarm = alarmDT.getUnixTime(); // add the INT_FREQ number of seconds nextAlarm += INT_FREQ; // update the DateTime with the new timestamp @@ -150,7 +150,7 @@ void loop() { // upload the new time to Alarm 1 myRTC.setA1Time( - alarmDT.day(), alarmDT.hour(), alarmDT.minute(), alarmDT.second(), + alarmDT.getDay(), alarmDT.getHour(), alarmDT.getMinute(), alarmDT.getSecond(), alarmBits, alarmDayIsDay, alarmH12, alarmPM); // enable Alarm 1 interrupts diff --git a/examples/now/now.ino b/examples/now/now.ino index 3984a46..f4a794d 100644 --- a/examples/now/now.ino +++ b/examples/now/now.ino @@ -21,22 +21,22 @@ void loop () { DateTime now = myRTC.now(); - Serial.print(now.year(), DEC); + Serial.print(now.getYear(), DEC); Serial.print('/'); - Serial.print(now.month(), DEC); + Serial.print(now.getMonth(), DEC); Serial.print('/'); - Serial.print(now.day(), DEC); + Serial.print(now.getDay(), DEC); Serial.print(' '); - Serial.print(now.hour(), DEC); + Serial.print(now.getHour(), DEC); Serial.print(':'); - Serial.print(now.minute(), DEC); + Serial.print(now.getMinute(), DEC); Serial.print(':'); - Serial.print(now.second(), DEC); + Serial.print(now.getSecond(), DEC); Serial.println(); Serial.print(" since midnight 1/1/1970 = "); - Serial.print(now.unixtime()); + Serial.print(now.getUnixTime()); Serial.print("s = "); - Serial.print(now.unixtime() / 86400L); + Serial.print(now.getUnixTime() / 86400L); Serial.println("d"); -} \ No newline at end of file +} diff --git a/examples/setEpoch/setEpoch.ino b/examples/setEpoch/setEpoch.ino index c995359..70d0887 100644 --- a/examples/setEpoch/setEpoch.ino +++ b/examples/setEpoch/setEpoch.ino @@ -55,7 +55,8 @@ Serial.println("ESP8266 Microcontroller Ready!"); bool century = false; bool h12Flag; bool pmFlag; - DateTime now = myRTC.now(); + + // read directly from DS3231 device Serial.print("\n\n"); Serial.print(" DateTime of DS3231: "); Serial.print(Clock.getYear(), DEC); @@ -73,24 +74,31 @@ Serial.println("ESP8266 Microcontroller Ready!"); Serial.print(Clock.getDoW(), DEC); Serial.println(); + // Read now from DateTime class + DateTime datetime = myRTC.now(); Serial.print("\n\n"); Serial.print(" DateTime of RTC: "); - Serial.print(now.year(), DEC); + Serial.print(datetime.getYear(), DEC); Serial.print("-"); - Serial.print(now.month(), DEC); + Serial.print(datetime.getMonth(), DEC); Serial.print("-"); - Serial.print(now.day(), DEC); + Serial.print(datetime.getDay(), DEC); Serial.print(" "); - Serial.print(now.hour(), DEC); + Serial.print(datetime.getHour(), DEC); Serial.print(":"); - Serial.print(now.minute(), DEC); + Serial.print(datetime.getMinute(), DEC); Serial.print(":"); - Serial.print(now.second(), DEC); + Serial.print(datetime.getSecond(), DEC); Serial.println(); Serial.print("\n\n Output of Struct tm: "); showTimeFormated(tstmp); + + Serial.print("\nUse show_DateTime function:\n "); + char buffer[80]; + datetime.show_DateTime(buffer, sizeof(buffer)); + Serial.println(buffer); } void loop () { -} \ No newline at end of file +}