xsns_33_ds3231.ino 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. xsns_33_ds3231.ino - ds3231 RTC chip, act like sensor support for Sonoff-Tasmota
  3. Copyright (C) 2018 Guy Elgabsi (guy.elg AT gmail.com)
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_I2C
  16. #ifdef USE_DS3231
  17. /*********************************************************************************************\
  18. DS3231 - its a accurate RTC that used in the SONOFF for get time when you not have internet connection
  19. This is minimal library that use only for read/write time !
  20. We store UTC time in the DS3231 , so we can use the standart functions.
  21. HOWTO Use : first time, you must to have internet connection (use your mobile phone or try in other location).
  22. once you have ntp connection , the DS3231 internal clock will be updated automatically.
  23. you can now power off the device, from now and on the time is stored in the module and will
  24. be restored when the is no connection to NTP.
  25. Source: Guy Elgabsi with special thanks to Jack Christensen
  26. I2C Address: 0x68
  27. \*********************************************************************************************/
  28. #define XSNS_33 33
  29. //DS3232 I2C Address
  30. #ifndef USE_RTC_ADDR
  31. #define USE_RTC_ADDR 0x68
  32. #endif
  33. //DS3232 Register Addresses
  34. #define RTC_SECONDS 0x00
  35. #define RTC_MINUTES 0x01
  36. #define RTC_HOURS 0x02
  37. #define RTC_DAY 0x03
  38. #define RTC_DATE 0x04
  39. #define RTC_MONTH 0x05
  40. #define RTC_YEAR 0x06
  41. #define RTC_CONTROL 0x0E
  42. #define RTC_STATUS 0x0F
  43. //Control register bits
  44. #define OSF 7
  45. #define EOSC 7
  46. #define BBSQW 6
  47. #define CONV 5
  48. #define RS2 4
  49. #define RS1 3
  50. #define INTCN 2
  51. //Other
  52. #define HR1224 6 //Hours register 12 or 24 hour mode (24 hour mode==0)
  53. #define CENTURY 7 //Century bit in Month register
  54. #define DYDT 6 //Day/Date flag bit in alarm Day/Date registers
  55. boolean ds3231ReadStatus = false , ds3231WriteStatus = false; //flag, we want to wriet/write to DS3231 onlu once
  56. boolean DS3231chipDetected;
  57. /*----------------------------------------------------------------------*
  58. Detect the DS3231 Chip
  59. ----------------------------------------------------------------------*/
  60. boolean DS3231Detect(void)
  61. {
  62. if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1))
  63. {
  64. snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "DS3231", USE_RTC_ADDR);
  65. AddLog(LOG_LEVEL_INFO);
  66. return true;
  67. }
  68. else
  69. {
  70. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "%s *NOT* " D_FOUND_AT " 0x%x"), "DS3231", USE_RTC_ADDR);
  71. AddLog(LOG_LEVEL_INFO);
  72. return false;
  73. }
  74. }
  75. /*----------------------------------------------------------------------*
  76. BCD-to-Decimal conversion
  77. ----------------------------------------------------------------------*/
  78. uint8_t bcd2dec(uint8_t n)
  79. {
  80. return n - 6 * (n >> 4);
  81. }
  82. /*----------------------------------------------------------------------*
  83. Decimal-to-BCD conversion
  84. ----------------------------------------------------------------------*/
  85. uint8_t dec2bcd(uint8_t n)
  86. {
  87. return n + 6 * (n / 10);
  88. }
  89. /*----------------------------------------------------------------------*
  90. Read time from DS3231 and return the epoch time (second since 1-1-1970 00:00)
  91. ----------------------------------------------------------------------*/
  92. uint32_t ReadFromDS3231(void)
  93. {
  94. TIME_T tm;
  95. tm.second = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_SECONDS));
  96. tm.minute = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MINUTES));
  97. tm.hour = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_HOURS) & ~_BV(HR1224)); //assumes 24hr clock
  98. tm.day_of_week = I2cRead8(USE_RTC_ADDR, RTC_DAY);
  99. tm.day_of_month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_DATE));
  100. tm.month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MONTH) & ~_BV(CENTURY)); //don't use the Century bit
  101. tm.year = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_YEAR));
  102. return MakeTime(tm);
  103. }
  104. /*----------------------------------------------------------------------*
  105. Get time as TIME_T and set the DS3231 time to this value
  106. ----------------------------------------------------------------------*/
  107. void SetDS3231Time (uint32_t epoch_time) {
  108. TIME_T tm;
  109. BreakTime(epoch_time, tm);
  110. I2cWrite8(USE_RTC_ADDR, RTC_SECONDS, dec2bcd(tm.second));
  111. I2cWrite8(USE_RTC_ADDR, RTC_MINUTES, dec2bcd(tm.minute));
  112. I2cWrite8(USE_RTC_ADDR, RTC_HOURS, dec2bcd(tm.hour));
  113. I2cWrite8(USE_RTC_ADDR, RTC_DAY, tm.day_of_week);
  114. I2cWrite8(USE_RTC_ADDR, RTC_DATE, dec2bcd(tm.day_of_month));
  115. I2cWrite8(USE_RTC_ADDR, RTC_MONTH, dec2bcd(tm.month));
  116. I2cWrite8(USE_RTC_ADDR, RTC_YEAR, dec2bcd(tm.year));
  117. I2cWrite8(USE_RTC_ADDR, RTC_STATUS, I2cRead8(USE_RTC_ADDR, RTC_STATUS) & ~_BV(OSF)); //clear the Oscillator Stop Flag
  118. }
  119. /*********************************************************************************************\
  120. Interface
  121. \*********************************************************************************************/
  122. boolean Xsns33(byte function)
  123. {
  124. boolean result = false;
  125. if (i2c_flg) {
  126. switch (function) {
  127. case FUNC_INIT:
  128. DS3231chipDetected = DS3231Detect();
  129. result = DS3231chipDetected;
  130. break;
  131. case FUNC_EVERY_SECOND:
  132. TIME_T tmpTime;
  133. if (!ds3231ReadStatus && DS3231chipDetected && utc_time < 1451602800 ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231
  134. ntp_force_sync = 1; //force to sync with ntp
  135. utc_time = ReadFromDS3231(); //we read UTC TIME from DS3231
  136. // from this line, we just copy the function from "void RtcSecond()" at the support.ino ,line 2143 and above
  137. // We need it to set rules etc.
  138. BreakTime(utc_time, tmpTime);
  139. if (utc_time < 1451602800 ) {
  140. ds3231ReadStatus = true; //if time in DS3231 is valid, do not update again
  141. }
  142. RtcTime.year = tmpTime.year + 1970;
  143. daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year);
  144. standard_time = RuleToTime(Settings.tflag[0], RtcTime.year);
  145. 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"),
  146. GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
  147. AddLog(LOG_LEVEL_INFO);
  148. if (local_time < 1451602800) { // 2016-01-01
  149. rules_flag.time_init = 1;
  150. } else {
  151. rules_flag.time_set = 1;
  152. }
  153. result = true;
  154. }
  155. 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
  156. 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"),
  157. GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
  158. AddLog(LOG_LEVEL_INFO);
  159. SetDS3231Time (utc_time); //update the DS3231 time
  160. ds3231WriteStatus = true;
  161. }
  162. else {
  163. result = false;
  164. }
  165. break;
  166. }
  167. }
  168. return result;
  169. }
  170. #endif // USE_DS3231
  171. #endif // USE_I2C