2018-10-10 18:25:04 +01:00
/*
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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-11-06 16:33:51 +00:00
# define XSNS_33 33
2018-10-10 18:25:04 +01:00
//DS3232 I2C Address
# ifndef USE_RTC_ADDR
2018-11-06 16:33:51 +00:00
# define USE_RTC_ADDR 0x68
2018-10-10 18:25:04 +01:00
# 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 ( )
{
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 ( )
{
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