From b68746a87aa831b1be9d98f71aec070987169d8f Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Tue, 11 Oct 2016 14:37:32 -0700 Subject: [PATCH 1/6] Make update() non-blocking. --- NTPClient.cpp | 62 +++++++++++++++++++++++++++++++++++---------------- NTPClient.h | 5 ++++- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/NTPClient.cpp b/NTPClient.cpp index 7b1a4e2..46121af 100644 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -60,6 +60,25 @@ void NTPClient::begin(int port) { this->_udpSetup = true; } +bool NTPClient::checkResponse() { + + if (this->_udp->parsePacket()) { + this->_lastUpdate = millis(); + this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE); + + unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); + unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + return true; + } else { + return false; + } +} + bool NTPClient::forceUpdate() { #ifdef DEBUG_NTPClient Serial.println("Update from NTP Server"); @@ -69,36 +88,38 @@ bool NTPClient::forceUpdate() { // Wait till data is there or timeout... byte timeout = 0; - int cb = 0; + bool cb = 0; do { delay ( 10 ); - cb = this->_udp->parsePacket(); + cb = this->checkResponse(); if (timeout > 100) return false; // timeout after 1000 ms timeout++; - } while (cb == 0); + } while (cb == false); - this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time + return true; +} - this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE); +bool NTPClient::update() { + bool updated = false; + unsigned long now = millis(); - unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); - unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]); - // combine the four bytes (two words) into a long integer - // this is NTP time (seconds since Jan 1 1900): - unsigned long secsSince1900 = highWord << 16 | lowWord; + if ((now - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval + || this->_lastUpdate == 0 + || now - _lastRequest > _retryInterval) { // Update if there was no response to the request - this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + // setup the UDP client if needed + if (!this->_udpSetup) { + this->begin(); + } - return true; -} + this->sendNTPPacket(); + } -bool NTPClient::update() { - if ((millis() - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval - || this->_lastUpdate == 0) { // Update if there was no update yet. - if (!this->_udpSetup) this->begin(); // setup the UDP client if needed - return this->forceUpdate(); + if (_lastRequest > _lastUpdate) { + updated = checkResponse(); } - return true; + + return updated; } unsigned long NTPClient::getEpochTime() { @@ -168,4 +189,7 @@ void NTPClient::sendNTPPacket() { this->_udp->beginPacket(this->_poolServerName, 123); //NTP requests are to port 123 this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE); this->_udp->endPacket(); + + this->_lastRequest = millis(); + } diff --git a/NTPClient.h b/NTPClient.h index 4d5630d..b582fd0 100644 --- a/NTPClient.h +++ b/NTPClient.h @@ -18,13 +18,16 @@ class NTPClient { int _timeOffset = 0; unsigned int _updateInterval = 60000; // In ms + unsigned int _retryInterval = 1000; // In ms unsigned long _currentEpoc = 0; // In s unsigned long _lastUpdate = 0; // In ms + unsigned long _lastRequest = 0; // IN ms byte _packetBuffer[NTP_PACKET_SIZE]; void sendNTPPacket(); + bool checkResponse(); public: NTPClient(UDP& udp); @@ -53,7 +56,7 @@ class NTPClient { /** * This will force the update from the NTP Server. - * + * This can block for a full second * @return true on success, false on failure */ bool forceUpdate(); From 5ef4d5400ff400dd3749cba821a3395b0ae5af31 Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Fri, 9 Dec 2016 20:35:20 -0800 Subject: [PATCH 2/6] Added updated() method to see if the time has ever been updated. --- NTPClient.cpp | 4 ++++ NTPClient.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/NTPClient.cpp b/NTPClient.cpp index 46121af..6e62861 100644 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -122,6 +122,10 @@ bool NTPClient::update() { return updated; } +bool NTPClient::updated() { + return (_currentEpoc != 0); +} + unsigned long NTPClient::getEpochTime() { return this->_timeOffset + // User offset this->_currentEpoc + // Epoc returned by the NTP server diff --git a/NTPClient.h b/NTPClient.h index b582fd0..50b9617 100644 --- a/NTPClient.h +++ b/NTPClient.h @@ -54,6 +54,12 @@ class NTPClient { */ bool update(); + /** + * Has the time ever been sucessfully updated + * + */ + bool updated(); + /** * This will force the update from the NTP Server. * This can block for a full second From a76f5957bf041bb55f6e9bf0312cd5177ae4881e Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Mon, 9 Jan 2017 07:49:57 -0800 Subject: [PATCH 3/6] Added a callback facility to notify user of updated time. Accuracy is improved by using the fractional portion of seconds. Make millisecond accurate time available via new getEpochMillis() function. --- NTPClient.cpp | 26 +++++++++++++++++++++++++- NTPClient.h | 19 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/NTPClient.cpp b/NTPClient.cpp index 6e62861..463c5d3 100644 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -73,6 +73,14 @@ bool NTPClient::checkResponse() { unsigned long secsSince1900 = highWord << 16 | lowWord; this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + + highWord = word(this->_packetBuffer[44], this->_packetBuffer[45]); + lowWord = word(this->_packetBuffer[46], this->_packetBuffer[47]); + this->_currentFraction = highWord << 16 | lowWord; + + // if the user has set a callback function for when the time is updated, call it + if (_updateCallback) { _updateCallback(this); } + return true; } else { return false; @@ -129,7 +137,19 @@ bool NTPClient::updated() { unsigned long NTPClient::getEpochTime() { return this->_timeOffset + // User offset this->_currentEpoc + // Epoc returned by the NTP server - ((millis() - this->_lastUpdate) / 1000); // Time since last update + ((millis() - this->_lastUpdate + (_currentFraction / FRACTIONSPERMILLI)) / 1000); // Time since last update +} + +unsigned long long NTPClient::getEpochMillis() { + unsigned long long epoch; + + epoch = this->_timeOffset; // user offset + epoch += _currentEpoc; // last time returned via server + epoch *= 1000; // convert to millis + epoch += _currentFraction / FRACTIONSPERMILLI; // add the fraction from the server + epoch += millis() - this->_lastUpdate; // add the millis that have passed since the last update + + return epoch; } int NTPClient::getDay() { @@ -173,6 +193,10 @@ void NTPClient::setUpdateInterval(int updateInterval) { this->_updateInterval = updateInterval; } +void NTPClient::setUpdateCallback(NTPUpdateCallbackFunction f) { + _updateCallback = f; +} + void NTPClient::sendNTPPacket() { // set all bytes in the buffer to 0 memset(this->_packetBuffer, 0, NTP_PACKET_SIZE); diff --git a/NTPClient.h b/NTPClient.h index 50b9617..4b48960 100644 --- a/NTPClient.h +++ b/NTPClient.h @@ -5,9 +5,14 @@ #include #define SEVENZYYEARS 2208988800UL +#define FRACTIONSPERMILLI (4294967UL) #define NTP_PACKET_SIZE 48 #define NTP_DEFAULT_LOCAL_PORT 1337 +class NTPClient; + +typedef void (*NTPUpdateCallbackFunction)(NTPClient* c); + class NTPClient { private: UDP* _udp; @@ -21,11 +26,14 @@ class NTPClient { unsigned int _retryInterval = 1000; // In ms unsigned long _currentEpoc = 0; // In s + unsigned long _currentFraction = 0; // In 1/(2^32) s unsigned long _lastUpdate = 0; // In ms unsigned long _lastRequest = 0; // IN ms byte _packetBuffer[NTP_PACKET_SIZE]; + NTPUpdateCallbackFunction _updateCallback; + void sendNTPPacket(); bool checkResponse(); @@ -60,6 +68,12 @@ class NTPClient { */ bool updated(); + /** + * Register a callback function for when the time gets updated + * + */ + void setUpdateCallback(NTPUpdateCallbackFunction f); + /** * This will force the update from the NTP Server. * This can block for a full second @@ -93,6 +107,11 @@ class NTPClient { */ unsigned long getEpochTime(); + /** + * @return time in milliseconds since Jan. 1, 1970 + */ + unsigned long long getEpochMillis(); + /** * Stops the underlying UDP client */ From 182d69ddb91a9f1a0823518588f39885989db67c Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Tue, 10 Jan 2017 09:29:15 -0800 Subject: [PATCH 4/6] Fix handling of last packet request timing --- NTPClient.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/NTPClient.cpp b/NTPClient.cpp index 463c5d3..d925c0b 100644 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -64,6 +64,7 @@ bool NTPClient::checkResponse() { if (this->_udp->parsePacket()) { this->_lastUpdate = millis(); + this->_lastRequest = 0; // no outstanding request this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE); unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); @@ -111,9 +112,9 @@ bool NTPClient::update() { bool updated = false; unsigned long now = millis(); - if ((now - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval - || this->_lastUpdate == 0 - || now - _lastRequest > _retryInterval) { // Update if there was no response to the request + if ( ((_lastRequest == 0) && (_lastUpdate == 0)) // Never requested or updated + || ((_lastRequest == 0) && ((now - _lastUpdate) >= _updateInterval)) // Update after _updateInterval + || ((_lastRequest != 0) && ((now - _lastRequest) > _retryInterval)) ) { // Update if there was no response to the request // setup the UDP client if needed if (!this->_udpSetup) { @@ -123,7 +124,7 @@ bool NTPClient::update() { this->sendNTPPacket(); } - if (_lastRequest > _lastUpdate) { + if (_lastRequest) { updated = checkResponse(); } From ab157e8a77804421e093d28b7fb5a36058a22b49 Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Tue, 16 Jan 2018 14:37:22 -0800 Subject: [PATCH 5/6] Initialize _updateCallback to a null ptr. --- NTPClient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NTPClient.h b/NTPClient.h index 4b48960..459d38b 100644 --- a/NTPClient.h +++ b/NTPClient.h @@ -32,7 +32,7 @@ class NTPClient { byte _packetBuffer[NTP_PACKET_SIZE]; - NTPUpdateCallbackFunction _updateCallback; + NTPUpdateCallbackFunction _updateCallback = NULL; void sendNTPPacket(); bool checkResponse(); From 63f122946c9dfbe4ad23a73c74885a3c171e745d Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Sat, 20 Oct 2018 10:41:27 -0700 Subject: [PATCH 6/6] Add setRetryInterval() method --- NTPClient.cpp | 4 ++++ NTPClient.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/NTPClient.cpp b/NTPClient.cpp index d925c0b..15dc9e4 100644 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -194,6 +194,10 @@ void NTPClient::setUpdateInterval(int updateInterval) { this->_updateInterval = updateInterval; } +void NTPClient::setRetryInterval(int retryInterval) { + _retryInterval = retryInterval; +} + void NTPClient::setUpdateCallback(NTPUpdateCallbackFunction f) { _updateCallback = f; } diff --git a/NTPClient.h b/NTPClient.h index 459d38b..8163ba1 100644 --- a/NTPClient.h +++ b/NTPClient.h @@ -97,6 +97,11 @@ class NTPClient { */ void setUpdateInterval(int updateInterval); + /** + * Set the retry interval to another frequency in ms + */ + void setRetryInterval(int retryInterval); + /** * @return time formatted like `hh:mm:ss` */