diff --git a/extras/test/src/test_CloudSchedule.cpp b/extras/test/src/test_CloudSchedule.cpp index 53b582aef..04ecbad7a 100644 --- a/extras/test/src/test_CloudSchedule.cpp +++ b/extras/test/src/test_CloudSchedule.cpp @@ -13,16 +13,22 @@ unsigned long time_now = 1; /************************************************************************************** - * TimeService Fake CTOR/DTOR + * TimeService Fake CTOR **************************************************************************************/ -TimeService::TimeService() {} +TimeServiceClass::TimeServiceClass() {} /************************************************************************************** * TimeService Fake Methods **************************************************************************************/ -unsigned long TimeService::getLocalTime() {return time_now;} +unsigned long TimeServiceClass::getLocalTime() {return time_now;} + +/************************************************************************************** + * TimeService Fake local instance + **************************************************************************************/ + +TimeServiceClass TimeService; /************************************************************************************** TEST CODE diff --git a/extras/test/src/util/PropertyTestUtil.cpp b/extras/test/src/util/PropertyTestUtil.cpp index ca71cd765..1bf41c437 100644 --- a/extras/test/src/util/PropertyTestUtil.cpp +++ b/extras/test/src/util/PropertyTestUtil.cpp @@ -18,8 +18,3 @@ unsigned long getTime() { return 0; } - -TimeService & ArduinoIoTCloudTimeService() { - static TimeService _timeService_instance; - return _timeService_instance; -} diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index af76df907..a0637a90e 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -119,21 +119,25 @@ defined (ARDUINO_NANO_RP2040_CONNECT) #define BOARD_HAS_ECCX08 #define HAS_TCP + #define HAS_RTC #endif #if defined(ARDUINO_NICLA_VISION) #define BOARD_HAS_SE050 #define HAS_TCP + #define HAS_RTC #endif #if defined(ARDUINO_AVR_UNO_WIFI_REV2) || \ defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) #define BOARD_HAS_OFFLOADED_ECCX08 #define HAS_TCP + #define HAS_RTC #endif #if defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) #define HAS_LORA + #define HAS_RTC #endif #if defined(ARDUINO_ESP8266_ESP12) || defined(ARDUINO_ARCH_ESP32) || defined(ESP8266) || defined(ESP32) diff --git a/src/ArduinoIoTCloud.cpp b/src/ArduinoIoTCloud.cpp index 0b2794ade..2475b5621 100644 --- a/src/ArduinoIoTCloud.cpp +++ b/src/ArduinoIoTCloud.cpp @@ -28,7 +28,7 @@ ArduinoIoTCloudClass::ArduinoIoTCloudClass() : _connection{nullptr} , _last_checked_property_index{0} -, _time_service(ArduinoIoTCloudTimeService()) +, _time_service(TimeService) , _tz_offset{0} , _tz_dst_until{0} , _thing_id{""} diff --git a/src/ArduinoIoTCloud.h b/src/ArduinoIoTCloud.h index 8be318b25..d09fca600 100644 --- a/src/ArduinoIoTCloud.h +++ b/src/ArduinoIoTCloud.h @@ -170,7 +170,7 @@ class ArduinoIoTCloudClass PropertyContainer _device_property_container; PropertyContainer _thing_property_container; unsigned int _last_checked_property_index; - TimeService & _time_service; + TimeServiceClass & _time_service; int _tz_offset; unsigned int _tz_dst_until; String _thing_id; diff --git a/src/property/types/CloudSchedule.h b/src/property/types/CloudSchedule.h index 47827c59c..3d2c598fd 100644 --- a/src/property/types/CloudSchedule.h +++ b/src/property/types/CloudSchedule.h @@ -115,7 +115,7 @@ class Schedule { bool isActive() { - ScheduleTimeType now = _schedule_time_service.getLocalTime(); + ScheduleTimeType now = TimeService.getLocalTime(); if(checkTimeValid(now)) { /* We have to wait RTC configuration and Timezone setting from the cloud */ @@ -201,7 +201,6 @@ class Schedule { return !(operator==(aSchedule)); } private: - TimeService & _schedule_time_service = ArduinoIoTCloudTimeService(); ScheduleUnit getScheduleUnit(ScheduleConfigurationType msk) { return static_cast((msk & SCHEDULE_UNIT_MASK) >> SCHEDULE_UNIT_SHIFT); diff --git a/src/utility/time/TimeService.cpp b/src/utility/time/TimeService.cpp index 112745d06..a10556f7c 100644 --- a/src/utility/time/TimeService.cpp +++ b/src/utility/time/TimeService.cpp @@ -38,6 +38,11 @@ RTCZero rtc; **************************************************************************************/ time_t cvt_time(char const * time); +#ifdef HAS_RTC +void rtc_begin(); +void rtc_set(unsigned long time); +unsigned long rtc_get(); +#endif /************************************************************************************** * CONSTANTS @@ -50,14 +55,16 @@ static time_t const EPOCH = 0; * CTOR/DTOR **************************************************************************************/ -TimeService::TimeService() +TimeServiceClass::TimeServiceClass() : _con_hdl(nullptr) -#if defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_MBED) +#ifdef HAS_RTC , _is_rtc_configured(false) #endif +#ifdef HAS_TCP , _is_tz_configured(false) , _timezone_offset(0) , _timezone_dst_until(0) +#endif { } @@ -66,69 +73,74 @@ TimeService::TimeService() * PUBLIC MEMBER FUNCTIONS **************************************************************************************/ -void TimeService::begin(ConnectionHandler * con_hdl) +void TimeServiceClass::begin(ConnectionHandler * con_hdl) { _con_hdl = con_hdl; -#ifdef ARDUINO_ARCH_SAMD - rtc.begin(); +#ifdef HAS_RTC + rtc_begin(); #endif } -unsigned long TimeService::getTime() +unsigned long TimeServiceClass::getTime() { -#ifdef ARDUINO_ARCH_SAMD - if(!_is_rtc_configured) - { - unsigned long utc = getRemoteTime(); - if(EPOCH_AT_COMPILE_TIME != utc) - { - rtc.setEpoch(utc); - _is_rtc_configured = true; - } - return utc; + /* If RTC is available try to get current time from + * there as first choice. RTC configuration is managed + * runtime by checking _is_rtc_configured flag. + */ +#ifdef HAS_RTC + unsigned long rtc_time = getRTC(); + if(isTimeValid(rtc_time)) { + return rtc_time; } - return rtc.getEpoch(); -#elif ARDUINO_ARCH_MBED - if(!_is_rtc_configured) - { - unsigned long utc = getRemoteTime(); - if(EPOCH_AT_COMPILE_TIME != utc) - { - set_time(utc); - _is_rtc_configured = true; - } - return utc; +#endif + + /* Without RTC support, but with TCP connection available + * try to get time from connection handler or NTP server. + */ +#ifdef HAS_TCP + unsigned long remote_time = getRemoteTime(); + if(isTimeValid(remote_time)) { + return remote_time; } - return time(NULL); -#else - return getRemoteTime(); #endif + + /* If none of the previous methods is supported or returns + * a valid time return the epoch at compile time as we do + * in TimeService::getRemoteTime(). + */ + return EPOCH_AT_COMPILE_TIME; } -void TimeService::setTimeZoneData(long offset, unsigned long dst_until) +void TimeServiceClass::setTimeZoneData(long offset, unsigned long dst_until) { - if(_timezone_offset != offset) - DEBUG_DEBUG("ArduinoIoTCloudTCP::%s tz_offset: [%d]", __FUNCTION__, offset); - _timezone_offset = offset; - - if(_timezone_dst_until != dst_until) - DEBUG_DEBUG("ArduinoIoTCloudTCP::%s tz_dst_unitl: [%ul]", __FUNCTION__, dst_until); - _timezone_dst_until = dst_until; - - _is_tz_configured = true; +#ifdef HAS_TCP + if(_timezone_offset != offset || _timezone_dst_until != dst_until) { + DEBUG_VERBOSE("TimeService::%s offset: %d dst_unitl %ul", __FUNCTION__, offset, dst_until); + _timezone_offset = offset; + _timezone_dst_until = dst_until; + _is_tz_configured = true; + } +#else + DEBUG_WARNING("TimeService::%s Timezone support not available without TCP connection"); +#endif } -unsigned long TimeService::getLocalTime() +unsigned long TimeServiceClass::getLocalTime() { +#ifdef HAS_TCP unsigned long utc = getTime(); if(_is_tz_configured) { return utc + _timezone_offset; } else { return EPOCH; } +#else + DEBUG_WARNING("TimeService::%s Timezone support not available without TCP connection"); + return EPOCH; +#endif } -unsigned long TimeService::getTimeFromString(const String& input) +unsigned long TimeServiceClass::getTimeFromString(const String& input) { struct tm t = { @@ -149,24 +161,22 @@ unsigned long TimeService::getTimeFromString(const String& input) static const int expected_length = 20; static const int expected_parameters = 6; - if(input == nullptr || input.length() != expected_length) - { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s invalid input length", __FUNCTION__); + if(input == nullptr || input.length() != expected_length) { + DEBUG_ERROR("TimeService::%s invalid input length", __FUNCTION__); return 0; } int scanned_parameters = sscanf(input.c_str(), "%d %s %d %d:%d:%d", &year, s_month, &day, &hour, &min, &sec); - if(scanned_parameters != expected_parameters) - { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s invalid input parameters number", __FUNCTION__); + if(scanned_parameters != expected_parameters) { + DEBUG_ERROR("TimeService::%s invalid input parameters number", __FUNCTION__); return 0; } char * s_month_position = strstr(month_names, s_month); if(s_month_position == nullptr || strlen(s_month) != 3) { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s invalid month name, use %s", __FUNCTION__, month_names); + DEBUG_ERROR("TimeService::%s invalid month name, use %s", __FUNCTION__, month_names); return 0; } @@ -174,7 +184,7 @@ unsigned long TimeService::getTimeFromString(const String& input) if(month < 0 || month > 11 || day < 1 || day > 31 || year < 1900 || hour < 0 || hour > 24 || min < 0 || min > 60 || sec < 0 || sec > 60) { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s invalid date values", __FUNCTION__); + DEBUG_ERROR("TimeService::%s invalid date values", __FUNCTION__); return 0; } @@ -192,34 +202,38 @@ unsigned long TimeService::getTimeFromString(const String& input) * PRIVATE MEMBER FUNCTIONS **************************************************************************************/ -unsigned long TimeService::getRemoteTime() +#ifdef HAS_TCP +bool TimeServiceClass::connected() { -#include "../../AIoTC_Config.h" -#ifndef HAS_LORA - - if(_con_hdl == nullptr) - return EPOCH_AT_COMPILE_TIME; - - /* At first try to see if a valid time can be obtained - * using the network time available via the connection - * handler. - */ - unsigned long const connection_time = _con_hdl->getTime(); - if(isTimeValid(connection_time)) { - return connection_time; + if(_con_hdl == nullptr) { + return false; + } else { + return _con_hdl->getStatus() == NetworkConnectionState::CONNECTED; } +} + +unsigned long TimeServiceClass::getRemoteTime() +{ + if(connected()) { + /* At first try to see if a valid time can be obtained + * using the network time available via the connection + * handler. + */ + unsigned long const connection_time = _con_hdl->getTime(); + if(isTimeValid(connection_time)) { + return connection_time; + } #ifndef __AVR__ - /* If no valid network time is available try to obtain the - * time via NTP next. - */ - unsigned long const ntp_time = NTPUtils::getTime(_con_hdl->getUDP()); - if(isTimeValid(ntp_time)) { - return ntp_time; - } + /* If no valid network time is available try to obtain the + * time via NTP next. + */ + unsigned long const ntp_time = NTPUtils::getTime(_con_hdl->getUDP()); + if(isTimeValid(ntp_time)) { + return ntp_time; + } #endif - -#endif /* ifndef HAS_LORA */ + } /* Return the epoch timestamp at compile time as a last * line of defense. Otherwise the certificate expiration @@ -228,10 +242,45 @@ unsigned long TimeService::getRemoteTime() */ return EPOCH_AT_COMPILE_TIME; } +#endif /* HAS_TCP */ + +#ifdef HAS_RTC +unsigned long TimeServiceClass::getRTC() +{ + if(!_is_rtc_configured) { + /* If RTC is not yet configured try to get a valid time value + * from the network. + */ + configureRTC(); + } + return rtc_get(); +} + +void TimeServiceClass::configureRTC() +{ +#ifdef HAS_TCP + /* For devices with a TCP connection we can try to get a valid + * time value from connection handler or from NTP server. + */ + unsigned long remote_time = getRemoteTime(); + if(isTimeValid(remote_time)) { + rtc_set(remote_time); + _is_rtc_configured = true; + } +#else + /* For devices without a TCP connection (LoRa) we cannot + * retrieve current time, so we store the epoch at compile time + * inside the RTC. + */ + rtc_set(EPOCH_AT_COMPILE_TIME); + _is_rtc_configured = true; +#endif +} +#endif /* HAS_RTC */ -bool TimeService::isTimeValid(unsigned long const time) +bool TimeServiceClass::isTimeValid(unsigned long const time) { - return (time >= EPOCH_AT_COMPILE_TIME); + return (time > EPOCH_AT_COMPILE_TIME); } /************************************************************************************** @@ -268,7 +317,40 @@ time_t cvt_time(char const * time) return mktime(&t); } -TimeService & ArduinoIoTCloudTimeService() { - static TimeService _timeService_instance; - return _timeService_instance; +#ifdef HAS_RTC +void rtc_begin() { +#ifdef ARDUINO_ARCH_SAMD + rtc.begin(); +#elif ARDUINO_ARCH_MBED + +#else + +#endif +} + +void rtc_set(unsigned long time) { +#ifdef ARDUINO_ARCH_SAMD + rtc.setEpoch(time); +#elif ARDUINO_ARCH_MBED + set_time(time); +#else + +#endif } + +unsigned long rtc_get() { +#ifdef ARDUINO_ARCH_SAMD + return rtc.getEpoch(); +#elif ARDUINO_ARCH_MBED + return time(NULL); +#else + return EPOCH; +#endif +} +#endif /* HAS_RTC */ + +/****************************************************************************** + * EXTERN DEFINITION + ******************************************************************************/ + +TimeServiceClass TimeService; diff --git a/src/utility/time/TimeService.h b/src/utility/time/TimeService.h index fbe81986d..7f3787739 100644 --- a/src/utility/time/TimeService.h +++ b/src/utility/time/TimeService.h @@ -24,6 +24,9 @@ #include +#include "../../AIoTC_Config.h" + +#ifdef HAS_RTC #ifdef ARDUINO_ARCH_SAMD #include #endif @@ -31,18 +34,19 @@ #ifdef ARDUINO_ARCH_MBED #include #endif +#endif /* HAS_RTC */ /************************************************************************************** * CLASS DECLARATION **************************************************************************************/ -class TimeService +class TimeServiceClass { public: - TimeService(); - + TimeServiceClass(); + virtual ~TimeServiceClass() { } void begin (ConnectionHandler * con_hdl); unsigned long getTime(); @@ -56,18 +60,32 @@ class TimeService private: ConnectionHandler * _con_hdl; -#if defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_MBED) +#ifdef HAS_RTC bool _is_rtc_configured; #endif + +#ifdef HAS_TCP bool _is_tz_configured; long _timezone_offset; unsigned long _timezone_dst_until; unsigned long getRemoteTime(); + bool connected(); +#endif + +#ifdef HAS_RTC + void configureRTC(); + unsigned long getRTC(); +#endif + static bool isTimeValid(unsigned long const time); }; -TimeService & ArduinoIoTCloudTimeService(); +/****************************************************************************** + * EXTERN DECLARATION + ******************************************************************************/ + +extern TimeServiceClass TimeService; #endif /* ARDUINO_IOT_CLOUD_TIME_SERVICE_H_ */