/* Arduino SoftRTClib Library * Copyright (C) 2011 by William Greiman * * This file is part of the Arduino SoftRTClib Library * * This Library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the Arduino SoftRTClib Library. If not, see * . */ #include #include #define EPOCH_YEAR DateTime::FIRST_YEAR #include //------------------------------------------------------------------------------ inline uint8_t bcd2bin (uint8_t val) {return val - 6 * (val >> 4);} //------------------------------------------------------------------------------ inline uint8_t bin2bcd (uint8_t val) {return val + 6 * (val / 10);} //------------------------------------------------------------------------------ // parse two digit field static uint8_t c2b(const char *s) { uint8_t b = '0' <= s[0] && s[0] <= '9' ? s[0] - '0' : 0; return 10*b + s[1] - '0'; } //------------------------------------------------------------------------------ // used by c2m and printMmm static const char Mmm[] PROGMEM = "JanFebMarAprMayJunJulAugSepOctNovDec"; //------------------------------------------------------------------------------ // convert Mmm string to [0,12] static uint8_t c2m(const char* s) { uint8_t m; for (m = 0; m < 12; m ++) { if (!strncmp_P(s, &Mmm[3*m], 3)) return m + 1; } return 0; } //------------------------------------------------------------------------------ // print two digit field with zero fill static void print2d(Print* pr, uint8_t n) { if (n < 10) pr->write('0'); pr->print(n, DEC); } //============================================================================== // DateTime member functions //------------------------------------------------------------------------------ /** \return Day of Week (Sunday == 0) */ int DateTime::dayOfWeek() const { uint16_t eday = daysSinceEpoch(year_, month_, day_); return epochDayToDayOfWeek(eday); } //------------------------------------------------------------------------------ /** \return Day of Year [0, 365] */ int DateTime::dayOfYear() const { return daysBeforeMonth(year_, month_) + day_ - 1; } //------------------------------------------------------------------------------ /** Print day with zero fill * \param[in] pr Print stream. */ void DateTime::printDD(Print* pr) const { print2d(pr, day()); } //------------------------------------------------------------------------------ /** Print date in DD Mmm YYYY format * \param[in] pr Print stream. */ void DateTime::printDate(Print* pr) const { print2d(pr, day()); pr->write(' '); printMmm(pr); pr->write(' '); pr->print(year()); } //------------------------------------------------------------------------------ /** Print date in DD Mmm YYYY format * \param[in] pr Print stream. */ void DateTime::printDateTime(Print* pr) const { printDate(pr); pr->write(' '); printIsoTime(pr); } //------------------------------------------------------------------------------ /** Print three character day of week * \param[in] pr Print stream. */ void DateTime::printDdd(Print* pr) const { static const char Ddd[] PROGMEM = "SunMonTueWedThuFriSat"; char buf[4]; uint8_t w = dayOfWeek(); strncpy_P(buf, &Ddd[3*w], 3); buf[3] = 0; pr->write(buf); } //------------------------------------------------------------------------------ /** Print date in ISO YYY-MM-DD format. * \param[in] pr Print stream. */ void DateTime::printIsoDate(Print* pr) const { pr->print(year()); pr->write('-'); print2d(pr, month()); pr->write('-'); print2d(pr, day()); } //------------------------------------------------------------------------------ /** Print date/time in ISO YYY-MM-DD hh:mm:ss format. * \param[in] pr Print stream. */ void DateTime::printIsoDateTime(Print* pr) const { printIsoDate(pr); pr->write(' '); printIsoTime(pr); } //------------------------------------------------------------------------------ /** Print time in ISO hh:mm:ss format. * \param[in] pr Print stream. */ void DateTime::printIsoTime(Print* pr)const { print2d(pr, hour()); pr->write(':'); print2d(pr, minute()); pr->write(':'); print2d(pr, second()); } //------------------------------------------------------------------------------ /** Print month with zero fill * \param[in] pr Print stream. */ void DateTime::printMM(Print* pr) const { print2d(pr, month()); } //------------------------------------------------------------------------------ /** Print three character month * \param[in] pr Print stream. */ void DateTime::printMmm(Print* pr) const { char buf[4]; strncpy_P(buf, &Mmm[3*month() - 3], 3); buf[3] = 0; pr->write(buf); } //------------------------------------------------------------------------------ /** Print date in USA MM/DD/YYYY format * \param[in] pr Print stream . */ void DateTime::printUsaDate(Print* pr) const { print2d(pr, month()); pr->write('/'); print2d(pr, day()); pr->write('/'); pr->print(year()); } //------------------------------------------------------------------------------ /** Print date/time in USA MM/DD/YYYY hh:mm:ss format * \param[in] pr Print stream . */ void DateTime::printUsaDateTime(Print* pr) const{ printUsaDate(pr); pr->write(' '); printIsoTime(pr); } //------------------------------------------------------------------------------ /** set time to epoch, 1/1/1970 00:00:00 */ void DateTime::settime() { year_ = FIRST_YEAR; month_ = 1; day_ = 1; hour_ = 0; minute_ = 0; second_ = 0; } //------------------------------------------------------------------------------ /** Convert posix seconds to broken-down time. * \param[in] t Posix time in seconds since epoch. * \return true for success else false */ bool DateTime::settime(time_t t) { if (t < 0) return false; int32_t tmp = t; t /= 60L; second_ = tmp - 60L * t; tmp = t; t /= 60L; minute_ = tmp - 60L * t; uint16_t days = t / 24L; hour_ = t - 24L * days; year_ = epochDayToYear(days); days -= daysBeforeYear(year_); month_ = dayOfYearToMonth(year_, days); day_ = 1 + days - daysBeforeMonth(year_, month_); return true; } //------------------------------------------------------------------------------ /** Set date time * \param[in] year 1970 <= year <= 2037. * \param[in] month 1 <= month <= 12. * \param[in] day 1 <= day <= (days in month). * \param[in] hour 0 <= hour <= 23. * \param[in] minute 0 <= minute <= 59. * \param[in] second 0 <= second <= 59. * \return true for success or false for failure. Fails if invalid time/date. */ bool DateTime::settime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { if (hour > 23 || minute > 59 || second > 59 || year < FIRST_YEAR || year > LAST_YEAR || month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) { return false; } year_ = year; month_ = month; day_ = day; hour_ = hour; minute_ = minute; second_ = second; return true; } //------------------------------------------------------------------------------ /** Set date time using __DATE__ and __TIME__ strings * * \param[in] date string in __DATE__ format. * \param[in] time string in __TIME__ format. * \return true for success or false for failure. Fails if invalid time/date. */ bool DateTime::settime(const char* date, const char* time) { return settime(2000 + c2b(date + 9), c2m(date), c2b(date + 4), c2b(time), c2b(time + 3), c2b(time + 6)); } //------------------------------------------------------------------------------ /** \return Posix time in seconds since 1 Jan 1970 00:00:00 */ time_t DateTime::time() const { uint16_t days = daysSinceEpoch(year_, month_, day_); return second_ + 60L * (minute_ + 60L * (hour_ + 24L * days)); } //============================================================================== /** read date time * * \param[out] dt destination for date time * * \return true if read is successful else false */ bool RTC_DS1307::getTime(DateTime* dt) { uint8_t r[7]; if (!readTime(r)) return false; dt->year_ = 2000 + r[6]; dt->month_ = r[5]; dt->day_ = r[4]; dt->hour_ = r[2]; dt->minute_ = r[1]; dt->second_ = r[0]; return true; } //------------------------------------------------------------------------------ /** \return true if no I/O error and DS1307 is running */ bool RTC_DS1307::isrunning(void) { uint8_t r; if (!read(0, &r, 1)) return false; return !(r & 0x80); } //------------------------------------------------------------------------------ /** \return current DS1307 date and time */ DateTime RTC_DS1307::now() { uint8_t r[7]; readTime(r); return DateTime(2000 + r[6], r[5], r[4], r[2], r[1], r[0]); } //------------------------------------------------------------------------------ /** * Read RTC time registers * \param[out] r Location for return of seven RTC time registers. * Values are converted from BCD to binary. * \return true for success or false for failure. */ bool RTC_DS1307::readTime(uint8_t *r) { if (!read(0, r, 7)) return false; r[0] &= 0X7F; for (uint8_t i = 0; i < 7; i++) r[i] = bcd2bin(r[i]); return true; } //------------------------------------------------------------------------------ /** set mode for SQW/OUT pin * * \param[in] sqwMode DS1307_SQW_LOW, DS1307_SQW_HIGH, DS1307_SQW_1_HZ, * DS1307_SQW_4096_HZ, DS1307_SQW_8192_HZ, or DS1307_SQW_32768_HZ * \return true for success or false for failure. */ bool RTC_DS1307::setSQW(uint8_t sqwMode) { return write(DS1307_CONTROL_ADDRESS, &sqwMode, 1); } //------------------------------------------------------------------------------ /** set date and time * \param[in] dt date and time to be used. * \return true for success or false for failure. */ bool RTC_DS1307::setTime(const DateTime* dt) { uint8_t r[7]; r[0] = dt->second(); r[1] = dt->minute(); r[2] = dt->hour(); #if SET_RTC_DAY_OF_WEEK r[3] = dt->isoDayOfWeek(); #else // SET_RTC_DAY_OF_WEEK r[3] = 0; #endif r[4] = dt->day(); r[5] = dt->month(); r[6] = dt->year() % 100; for (uint8_t i = 0; i < 7; i++) { r[i] = bin2bcd(r[i]); } return write(0, r, 7); } //============================================================================== #if DS_RTC_USE_WIRE //------------------------------------------------------------------------------ /** * Read data from the RTC. * * \param[in] address starting address. * \param[out] buf Location for data. * \param[in] count Number of bytes to write. * \return The value true, 1, for success or false, 0, for failure. */ bool RTC_DS1307::read(uint8_t address, uint8_t *buf, uint8_t count) { Wire.beginTransmission(DS_RTC_I2C_ADD/2); #if ARDUINO < 100 Wire.send(0); #else // ARDUINO < 100 Wire.write(0); #endif // ARDUINO < 100 if (Wire.endTransmission()) return false; Wire.requestFrom((uint8_t)(DS_RTC_I2C_ADD/2), count); if (Wire.available() != count) return false; for (uint8_t i = 0; i < count; i++) { #if ARDUINO < 100 buf[i] = Wire.receive(); #else // ARDUINO < 100 buf[i] = Wire.read(); #endif // ARDUINO < 100 } return true; } //------------------------------------------------------------------------------ /** * Write data to the RTC. * * \param[in] address Starting address. * \param[in] buf Location of data. * \param[in] count Number of bytes to write. * \return The value true, 1, for success or false, 0, for failure. */ bool RTC_DS1307::write(uint8_t address, uint8_t *buf, uint8_t count) { Wire.beginTransmission(DS_RTC_I2C_ADD/2); #if ARDUINO < 100 Wire.send(0); Wire.send(buf, count); #else // ARDUINO < 100 Wire.write(0); Wire.write(buf, count); #endif // ARDUINO < 100 return Wire.endTransmission() ? false : true; } #else // DS_RTC_USE_WIRE //------------------------------------------------------------------------------ /** * Read data from the RTC. * * \param[in] address starting address. * \param[out] buf Location for data. * \param[in] count Number of bytes to write. * \return The value true, 1, for success or false, 0, for failure. */ bool RTC_DS1307::read(uint8_t address, uint8_t *buf, uint8_t count) { // issue a start condition, send device address and write direction bit if (!i2cBus_->start(DS_RTC_I2C_ADD | I2C_WRITE)) goto fail; // send address if (!i2cBus_->write(address)) goto fail; // issue a repeated start condition, send device address and direction bit if (!i2cBus_->restart(DS_RTC_I2C_ADD | I2C_READ)) goto fail; // read data for (uint8_t i = 0; i < count; i++) { // send Ack until last byte then send Nak buf[i] = i2cBus_->read(i == (count - 1)); } // issue a stop condition i2cBus_->stop(); return true; fail: i2cBus_->stop(); return false; } //------------------------------------------------------------------------------ /** * Write data to the RTC. * * \param[in] address Starting address. * \param[in] buf Location of data. * \param[in] count Number of bytes to write. * \return The value true, 1, for success or false, 0, for failure. */ bool RTC_DS1307::write(uint8_t address, uint8_t *buf, uint8_t count) { // issue a start condition, send device address and write direction bit if (!i2cBus_->start(DS_RTC_I2C_ADD | I2C_WRITE)) goto fail; // send the address if (!i2cBus_->write(address)) goto fail; // send data for (uint8_t i = 0; i < count; i++) { if (!i2cBus_->write(buf[i])) goto fail; } // issue a stop condition i2cBus_->stop(); return true; fail: i2cBus_->stop(); return false; } #endif // DS_RTC_USE_WIRE