Skip to content

Make update() non-blocking. #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
97 changes: 77 additions & 20 deletions NTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,34 @@ void NTPClient::begin(int port) {
this->_udpSetup = true;
}

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]);
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;

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;
}
}

bool NTPClient::forceUpdate() {
#ifdef DEBUG_NTPClient
Serial.println("Update from NTP Server");
Expand All @@ -94,42 +122,60 @@ 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 ( ((_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

this->_currentEpoc = secsSince1900 - SEVENZYYEARS;
// setup the UDP client if needed
if (!this->_udpSetup) {
this->begin();
}

return true; // return true after successful update
}
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) {
updated = checkResponse();
}
return false; // return false if update does not occur

return updated;
}

bool NTPClient::updated() {
return (_currentEpoc != 0);
}

unsigned long NTPClient::getEpochTime() const {
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() const {
Expand Down Expand Up @@ -173,6 +219,14 @@ void NTPClient::setUpdateInterval(unsigned long updateInterval) {
this->_updateInterval = updateInterval;
}

void NTPClient::setRetryInterval(int retryInterval) {
_retryInterval = retryInterval;
}

void NTPClient::setUpdateCallback(NTPUpdateCallbackFunction f) {
_updateCallback = f;
}

void NTPClient::setPoolServerName(const char* poolServerName) {
this->_poolServerName = poolServerName;
}
Expand Down Expand Up @@ -201,4 +255,7 @@ void NTPClient::sendNTPPacket() {
}
this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE);
this->_udp->endPacket();

this->_lastRequest = millis();

}
35 changes: 34 additions & 1 deletion NTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
#include <Udp.h>

#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;
Expand All @@ -18,14 +23,20 @@ class NTPClient {
int _port = NTP_DEFAULT_LOCAL_PORT;
long _timeOffset = 0;

unsigned int _retryInterval = 1000; // In ms
unsigned long _updateInterval = 60000; // 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 = NULL;

void sendNTPPacket();
bool checkResponse();

public:
NTPClient(UDP& udp);
Expand Down Expand Up @@ -63,8 +74,20 @@ class NTPClient {
bool update();

/**
* This will force the update from the NTP Server.
* Has the time ever been sucessfully updated
*
*/
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
* @return true on success, false on failure
*/
bool forceUpdate();
Expand All @@ -85,6 +108,11 @@ class NTPClient {
*/
void setUpdateInterval(unsigned long updateInterval);

/**
* Set the retry interval to another frequency in ms
*/
void setRetryInterval(int retryInterval);

/**
* @return time formatted like `hh:mm:ss`
*/
Expand All @@ -95,6 +123,11 @@ class NTPClient {
*/
unsigned long getEpochTime() const;

/**
* @return time in milliseconds since Jan. 1, 1970
*/
unsigned long long getEpochMillis();

/**
* Stops the underlying UDP client
*/
Expand Down