diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 5dc618ef1..a558873be 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -18,16 +18,14 @@ #ifdef HAS_TCP #include +#include "utility/time/TimeService.h" #ifdef BOARD_HAS_ECCX08 #include "utility/ECCX08Cert.h" #include "utility/BearSSLTrustAnchor.h" #include #endif -#ifdef ARDUINO_ARCH_SAMD - #include - RTCZero rtc; -#endif +TimeService time_service; #ifdef BOARD_HAS_ECCX08 const static int keySlot = 0; @@ -41,20 +39,7 @@ const static int CONNECT_FAILURE = 0; const static int CONNECT_FAILURE_SUBSCRIBE = -1; static unsigned long getTime() { - if (!ArduinoCloud.getConnection()) { - return 0; - } - TcpIpConnectionHandler * connection = ArduinoCloud.getConnection(); - unsigned long time = connection->getTime(); - Debug.print(DBG_DEBUG, "NTP time: %lu", time); - if (!NTPUtils::isTimeValid(time)) { - Debug.print(DBG_ERROR, "Bogus NTP time from API, fallback to UDP method"); - time = NTPUtils(connection->getUDP()).getTime(); - } - #ifdef ARDUINO_ARCH_SAMD - rtc.setEpoch(time); - #endif - return time; + return time_service.getTime(); } ArduinoIoTCloudTCP::ArduinoIoTCloudTCP(): @@ -92,9 +77,7 @@ int ArduinoIoTCloudTCP::begin(TcpIpConnectionHandler & connection, String broker _connection = &connection; _brokerAddress = brokerAddress; _brokerPort = brokerPort; - #ifdef ARDUINO_ARCH_SAMD - rtc.begin(); - #endif + time_service.begin(&connection); return begin(_connection->getClient(), _brokerAddress, _brokerPort); } diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index 0cfb9f3b6..3e33749c8 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -30,8 +30,6 @@ #include -#include "utility/NTPUtils.h" - static char const DEFAULT_BROKER_ADDRESS_SECURE_AUTH[] = "mqtts-sa.iot.arduino.cc"; static uint16_t const DEFAULT_BROKER_PORT_SECURE_AUTH = 8883; diff --git a/src/utility/NTPUtils.cpp b/src/utility/NTPUtils.cpp deleted file mode 100644 index c3b9fe43d..000000000 --- a/src/utility/NTPUtils.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "NTPUtils.h" -#include "Arduino.h" -/* - This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient - For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol -*/ - - -// could be a constexpr in C++14 -static time_t cvt_TIME(char const *time) { - char s_month[5]; - int month, day, year; - struct tm t = {0 /* tm_sec */, - 0 /* tm_min */, - 0 /* tm_hour */, - 0 /* tm_mday */, - 0 /* tm_mon */, - 0 /* tm_year */, - 0 /* tm_wday */, - 0 /* tm_yday */, - 0 /* tm_isdst */ - }; - static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; - - sscanf(time, "%s %d %d", s_month, &day, &year); - - month = (strstr(month_names, s_month) - month_names) / 3; - - t.tm_mon = month; - t.tm_mday = day; - t.tm_year = year - 1900; - t.tm_isdst = -1; - - return mktime(&t); -} - - -NTPUtils::NTPUtils(UDP& Udp) : Udp(Udp) {} - -bool NTPUtils::isTimeValid(unsigned long time) { - return (time > static_cast(cvt_TIME(__DATE__))); -} - -void NTPUtils::sendNTPpacket(uint8_t* packetBuffer) { - memset(packetBuffer, 0, NTP_PACKET_SIZE); - packetBuffer[0] = 0b11100011; - packetBuffer[1] = 0; - packetBuffer[2] = 6; - packetBuffer[3] = 0xEC; - packetBuffer[12] = 49; - packetBuffer[13] = 0x4E; - packetBuffer[14] = 49; - packetBuffer[15] = 52; - Udp.beginPacket(timeServer, 123); - Udp.write(packetBuffer, NTP_PACKET_SIZE); - Udp.endPacket(); -} - -unsigned long NTPUtils::getTime() { - - unsigned int localPort = 8888; - uint8_t packetBuffer[NTP_PACKET_SIZE]; - - Udp.begin(localPort); - sendNTPpacket(packetBuffer); - long start = millis(); - while (!Udp.parsePacket() && (millis() - start < 10000)) {} - if (millis() - start >= 1000) { - //timeout reached - Udp.stop(); - return 0; - } - Udp.read(packetBuffer, NTP_PACKET_SIZE); - - unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); - unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); - unsigned long secsSince1900 = highWord << 16 | lowWord; - const unsigned long seventyYears = 2208988800UL; - unsigned long epoch = secsSince1900 - seventyYears; - - Udp.stop(); - return epoch; -} diff --git a/src/utility/NTPUtils.h b/src/utility/NTPUtils.h deleted file mode 100644 index 15c7a6585..000000000 --- a/src/utility/NTPUtils.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __NTP_UTILS__ -#define __NTP_UTILS__ -/* - This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient - For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol -*/ - -#include "Udp.h" -#include - -class NTPUtils { - public: - NTPUtils(UDP& Udp); - void sendNTPpacket(uint8_t* packetBuffer); - unsigned long getTime(); - static bool isTimeValid(unsigned long time); - private: - const char* timeServer = "time.arduino.cc"; - const int NTP_PACKET_SIZE = 48; - UDP& Udp; -}; - -#endif diff --git a/src/utility/time/NTPUtils.cpp b/src/utility/time/NTPUtils.cpp new file mode 100644 index 000000000..d815111b3 --- /dev/null +++ b/src/utility/time/NTPUtils.cpp @@ -0,0 +1,86 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2020 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "../../ArduinoIoTCloud_Defines.h" +#ifndef HAS_LORA + +#include "NTPUtils.h" + +#include + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +unsigned long NTPUtils::getTime(UDP & udp) +{ + udp.begin(NTP_LOCAL_PORT); + + sendNTPpacket(udp); + + bool is_timeout = false; + unsigned long const start = millis(); + do + { + is_timeout = (millis() - start) >= NTP_TIMEOUT_MS; + } while(!is_timeout && !udp.parsePacket()); + + if(is_timeout) { + udp.stop(); + return 0; + } + + uint8_t ntp_packet_buf[NTP_PACKET_SIZE]; + udp.read(ntp_packet_buf, NTP_PACKET_SIZE); + udp.stop(); + + unsigned long const highWord = word(ntp_packet_buf[40], ntp_packet_buf[41]); + unsigned long const lowWord = word(ntp_packet_buf[42], ntp_packet_buf[43]); + unsigned long const secsSince1900 = highWord << 16 | lowWord; + unsigned long const seventyYears = 2208988800UL; + unsigned long const epoch = secsSince1900 - seventyYears; + + return epoch; +} + +/************************************************************************************** + * PRIVATE MEMBER FUNCTIONS + **************************************************************************************/ + +void NTPUtils::sendNTPpacket(UDP & udp) +{ + uint8_t ntp_packet_buf[NTP_PACKET_SIZE] = {0}; + + ntp_packet_buf[0] = 0b11100011; + ntp_packet_buf[1] = 0; + ntp_packet_buf[2] = 6; + ntp_packet_buf[3] = 0xEC; + ntp_packet_buf[12] = 49; + ntp_packet_buf[13] = 0x4E; + ntp_packet_buf[14] = 49; + ntp_packet_buf[15] = 52; + + udp.beginPacket(NTP_TIME_SERVER, NTP_TIME_SERVER_PORT); + udp.write(ntp_packet_buf, NTP_PACKET_SIZE); + udp.endPacket(); +} + +#endif /* #ifndef HAS_LORA */ diff --git a/src/utility/time/NTPUtils.h b/src/utility/time/NTPUtils.h new file mode 100644 index 000000000..91ccb128d --- /dev/null +++ b/src/utility/time/NTPUtils.h @@ -0,0 +1,59 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2020 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +#ifndef __NTP_UTILS__ +#define __NTP_UTILS__ + +#include "../../ArduinoIoTCloud_Defines.h" +#ifndef HAS_LORA + +/* + This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient + For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol +*/ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +class NTPUtils +{ +public: + + static unsigned long getTime(UDP & udp); + +private: + + static size_t const NTP_PACKET_SIZE = 48; + static int const NTP_TIME_SERVER_PORT = 123; + static int const NTP_LOCAL_PORT = 8888; + static unsigned long const NTP_TIMEOUT_MS = 1000; + static char constexpr * NTP_TIME_SERVER = "time.arduino.cc"; + + static void sendNTPpacket(UDP & udp); + +}; + +#endif /* #ifndef HAS_LORA */ + +#endif diff --git a/src/utility/time/TimeService.cpp b/src/utility/time/TimeService.cpp new file mode 100644 index 000000000..ed2436938 --- /dev/null +++ b/src/utility/time/TimeService.cpp @@ -0,0 +1,157 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2020 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "../../ArduinoIoTCloud_Defines.h" +#ifndef HAS_LORA + +#include "TimeService.h" + +#include + +#include "NTPUtils.h" + +/************************************************************************************** + * GLOBAL VARIABLES + **************************************************************************************/ + +#ifdef ARDUINO_ARCH_SAMD +RTCZero rtc; +#endif + +/************************************************************************************** + * INTERNAL FUNCTION DECLARATION + **************************************************************************************/ + +time_t cvt_time(char const * time); + +/************************************************************************************** + * CONSTANTS + **************************************************************************************/ + +static time_t const EPOCH_AT_COMPILE_TIME = cvt_time(__DATE__); + +/************************************************************************************** + * CTOR/DTOR + **************************************************************************************/ + +TimeService::TimeService() +: _con_hdl(nullptr) +#ifdef ARDUINO_ARCH_SAMD +, _is_rtc_configured(false) +#endif +{ + +} + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +void TimeService::begin(ConnectionHandler * con_hdl) +{ + _con_hdl = con_hdl; +#ifdef ARDUINO_ARCH_SAMD + rtc.begin(); +#endif +} + +unsigned long TimeService::getTime() +{ +#ifdef ARDUINO_ARCH_SAMD + if(!_is_rtc_configured) + { + rtc.setEpoch(getRemoteTime()); + _is_rtc_configured = true; + } + return rtc.getEpoch(); +#else + return getRemoteTime(); +#endif +} + +/************************************************************************************** + * PRIVATE MEMBER FUNCTIONS + **************************************************************************************/ + +unsigned long TimeService::getRemoteTime() +{ + if(_con_hdl == nullptr) return 0; + + /* 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 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; + } + + return 0; +} + +bool TimeService::isTimeValid(unsigned long const time) +{ + return (time >= EPOCH_AT_COMPILE_TIME); +} + +/************************************************************************************** + * INTERNAL FUNCTION DEFINITION + **************************************************************************************/ + +time_t cvt_time(char const * time) +{ + char s_month[5]; + int month, day, year; + struct tm t = + { + 0 /* tm_sec */, + 0 /* tm_min */, + 0 /* tm_hour */, + 0 /* tm_mday */, + 0 /* tm_mon */, + 0 /* tm_year */, + 0 /* tm_wday */, + 0 /* tm_yday */, + 0 /* tm_isdst */ + }; + static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + + sscanf(time, "%s %d %d", s_month, &day, &year); + + month = (strstr(month_names, s_month) - month_names) / 3; + + t.tm_mon = month; + t.tm_mday = day; + t.tm_year = year - 1900; + t.tm_isdst = -1; + + return mktime(&t); +} + +#endif /* #ifndef HAS_LORA */ diff --git a/src/utility/time/TimeService.h b/src/utility/time/TimeService.h new file mode 100644 index 000000000..5ca8c73d9 --- /dev/null +++ b/src/utility/time/TimeService.h @@ -0,0 +1,63 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2020 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +#ifndef ARDUINO_IOT_CLOUD_TIME_SERVICE_H_ +#define ARDUINO_IOT_CLOUD_TIME_SERVICE_H_ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "../../ArduinoIoTCloud_Defines.h" +#ifndef HAS_LORA + +#include + +#ifdef ARDUINO_ARCH_SAMD + #include +#endif + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +class TimeService +{ + +public: + + TimeService(); + + + void begin (ConnectionHandler * con_hdl); + unsigned long getTime(); + +private: + + ConnectionHandler * _con_hdl; +#ifdef ARDUINO_ARCH_SAMD + bool _is_rtc_configured; +#endif + + unsigned long getRemoteTime(); + static bool isTimeValid(unsigned long const time); + +}; + +#endif /* #ifndef HAS_LORA */ + +#endif /* ARDUINO_IOT_CLOUD_TIME_SERVICE_H_ */