| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- /*
- xsns_33_ds3231.ino - ds3231 RTC chip, act like sensor support for Sonoff-Tasmota
- Copyright (C) 2018 Guy Elgabsi (guy.elg AT gmail.com)
- This program 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 program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #ifdef USE_I2C
- #ifdef USE_DS3231
- /*********************************************************************************************\
- DS3231 - its a accurate RTC that used in the SONOFF for get time when you not have internet connection
- This is minimal library that use only for read/write time !
- We store UTC time in the DS3231 , so we can use the standart functions.
- HOWTO Use : first time, you must to have internet connection (use your mobile phone or try in other location).
- once you have ntp connection , the DS3231 internal clock will be updated automatically.
- you can now power off the device, from now and on the time is stored in the module and will
- be restored when the is no connection to NTP.
- Source: Guy Elgabsi with special thanks to Jack Christensen
- I2C Address: 0x68
- \*********************************************************************************************/
- #define XSNS_33 33
- //DS3232 I2C Address
- #ifndef USE_RTC_ADDR
- #define USE_RTC_ADDR 0x68
- #endif
- //DS3232 Register Addresses
- #define RTC_SECONDS 0x00
- #define RTC_MINUTES 0x01
- #define RTC_HOURS 0x02
- #define RTC_DAY 0x03
- #define RTC_DATE 0x04
- #define RTC_MONTH 0x05
- #define RTC_YEAR 0x06
- #define RTC_CONTROL 0x0E
- #define RTC_STATUS 0x0F
- //Control register bits
- #define OSF 7
- #define EOSC 7
- #define BBSQW 6
- #define CONV 5
- #define RS2 4
- #define RS1 3
- #define INTCN 2
- //Other
- #define HR1224 6 //Hours register 12 or 24 hour mode (24 hour mode==0)
- #define CENTURY 7 //Century bit in Month register
- #define DYDT 6 //Day/Date flag bit in alarm Day/Date registers
- boolean ds3231ReadStatus = false , ds3231WriteStatus = false; //flag, we want to wriet/write to DS3231 onlu once
- boolean DS3231chipDetected;
- /*----------------------------------------------------------------------*
- Detect the DS3231 Chip
- ----------------------------------------------------------------------*/
- boolean DS3231Detect(void)
- {
- if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1))
- {
- snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "DS3231", USE_RTC_ADDR);
- AddLog(LOG_LEVEL_INFO);
- return true;
- }
- else
- {
- snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "%s *NOT* " D_FOUND_AT " 0x%x"), "DS3231", USE_RTC_ADDR);
- AddLog(LOG_LEVEL_INFO);
- return false;
- }
- }
- /*----------------------------------------------------------------------*
- BCD-to-Decimal conversion
- ----------------------------------------------------------------------*/
- uint8_t bcd2dec(uint8_t n)
- {
- return n - 6 * (n >> 4);
- }
- /*----------------------------------------------------------------------*
- Decimal-to-BCD conversion
- ----------------------------------------------------------------------*/
- uint8_t dec2bcd(uint8_t n)
- {
- return n + 6 * (n / 10);
- }
- /*----------------------------------------------------------------------*
- Read time from DS3231 and return the epoch time (second since 1-1-1970 00:00)
- ----------------------------------------------------------------------*/
- uint32_t ReadFromDS3231(void)
- {
- TIME_T tm;
- tm.second = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_SECONDS));
- tm.minute = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MINUTES));
- tm.hour = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_HOURS) & ~_BV(HR1224)); //assumes 24hr clock
- tm.day_of_week = I2cRead8(USE_RTC_ADDR, RTC_DAY);
- tm.day_of_month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_DATE));
- tm.month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MONTH) & ~_BV(CENTURY)); //don't use the Century bit
- tm.year = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_YEAR));
- return MakeTime(tm);
- }
- /*----------------------------------------------------------------------*
- Get time as TIME_T and set the DS3231 time to this value
- ----------------------------------------------------------------------*/
- void SetDS3231Time (uint32_t epoch_time) {
- TIME_T tm;
- BreakTime(epoch_time, tm);
- I2cWrite8(USE_RTC_ADDR, RTC_SECONDS, dec2bcd(tm.second));
- I2cWrite8(USE_RTC_ADDR, RTC_MINUTES, dec2bcd(tm.minute));
- I2cWrite8(USE_RTC_ADDR, RTC_HOURS, dec2bcd(tm.hour));
- I2cWrite8(USE_RTC_ADDR, RTC_DAY, tm.day_of_week);
- I2cWrite8(USE_RTC_ADDR, RTC_DATE, dec2bcd(tm.day_of_month));
- I2cWrite8(USE_RTC_ADDR, RTC_MONTH, dec2bcd(tm.month));
- I2cWrite8(USE_RTC_ADDR, RTC_YEAR, dec2bcd(tm.year));
- I2cWrite8(USE_RTC_ADDR, RTC_STATUS, I2cRead8(USE_RTC_ADDR, RTC_STATUS) & ~_BV(OSF)); //clear the Oscillator Stop Flag
- }
- /*********************************************************************************************\
- Interface
- \*********************************************************************************************/
- boolean Xsns33(byte function)
- {
- boolean result = false;
- if (i2c_flg) {
- switch (function) {
- case FUNC_INIT:
- DS3231chipDetected = DS3231Detect();
- result = DS3231chipDetected;
- break;
- case FUNC_EVERY_SECOND:
- TIME_T tmpTime;
- if (!ds3231ReadStatus && DS3231chipDetected && utc_time < 1451602800 ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231
- ntp_force_sync = 1; //force to sync with ntp
- utc_time = ReadFromDS3231(); //we read UTC TIME from DS3231
- // from this line, we just copy the function from "void RtcSecond()" at the support.ino ,line 2143 and above
- // We need it to set rules etc.
- BreakTime(utc_time, tmpTime);
- if (utc_time < 1451602800 ) {
- ds3231ReadStatus = true; //if time in DS3231 is valid, do not update again
- }
- RtcTime.year = tmpTime.year + 1970;
- daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year);
- standard_time = RuleToTime(Settings.tflag[0], RtcTime.year);
- snprintf_P(log_data, sizeof(log_data), PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"),
- GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
- AddLog(LOG_LEVEL_INFO);
- if (local_time < 1451602800) { // 2016-01-01
- rules_flag.time_init = 1;
- } else {
- rules_flag.time_set = 1;
- }
- result = true;
- }
- else if (!ds3231WriteStatus && DS3231chipDetected && utc_time > 1451602800 && abs(utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second
- snprintf_P(log_data, sizeof(log_data), PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"),
- GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
- AddLog(LOG_LEVEL_INFO);
- SetDS3231Time (utc_time); //update the DS3231 time
- ds3231WriteStatus = true;
- }
- else {
- result = false;
- }
- break;
- }
- }
- return result;
- }
- #endif // USE_DS3231
- #endif // USE_I2C
|