diff --git a/NTPClient.cpp b/NTPClient.cpp index fffe105..e7afd4d 100755 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -82,54 +82,124 @@ void NTPClient::begin(int port) { } bool NTPClient::forceUpdate() { - #ifdef DEBUG_NTPClient - Serial.println("Update from NTP Server"); - #endif - // flush any existing packets while(this->_udp->parsePacket() != 0) this->_udp->flush(); + uint32_t tik,tok; //tik,tok to record wait time, replace timeout this->sendNTPPacket(); + tik=millis(); + #ifdef DEBUG_NTPClient + Serial.println("sent ntp packet"); + #endif // Wait till data is there or timeout... - byte timeout = 0; - int cb = 0; + uint16_t cb = 0; do { - delay ( 10 ); + delay (1); //poll more frequently to get high accuarcy cb = this->_udp->parsePacket(); - if (timeout > 100) return false; // timeout after 1000 ms - timeout++; + if ((millis()-tik)>this->_ntp_timeout){ + this->_last_fail=millis(); + return false; + } } while (cb == 0); - - this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time + tok=millis(); + #ifdef DEBUG_NTPClient + Serial.println("got ntp packet."); + Serial.print("tik, tok, (tok-tik)/2: "); + Serial.print(tik);Serial.print(", "); + Serial.print(tok);Serial.print(", "); + Serial.println((tok-tik)/2.0); + #endif 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]); + uint32_t high_word,low_word; + uint32_t receive_int,transmit_int; //integer part // 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; + high_word=word(this->_packetBuffer[32],this->_packetBuffer[33]); + low_word=word(this->_packetBuffer[34],this->_packetBuffer[35]); + receive_int=(high_word<<16) | low_word; + high_word=word(this->_packetBuffer[40],this->_packetBuffer[41]); + low_word=word(this->_packetBuffer[42],this->_packetBuffer[43]); + transmit_int=(high_word<<16) | low_word; + #ifdef DEBUG_NTPClient + Serial.print("receive_int, transmit_int: "); + Serial.print(receive_int);Serial.print(", "); + Serial.println(transmit_int); + #endif + + float receive_dec=0,transmit_dec=0; //decimal part + high_word=word(this->_packetBuffer[36],this->_packetBuffer[37]); + receive_dec=high_word/65536.0; + #ifdef DEBUG_NTPClient + Serial.print("receive_dec, transmit_dec: "); + Serial.print(high_word,HEX);Serial.print(", "); + Serial.print(receive_dec,6);Serial.print(", "); + #endif + high_word=word(this->_packetBuffer[44],this->_packetBuffer[45]); + transmit_dec=high_word/65536.0; + #ifdef DEBUG_NTPClient + Serial.print(high_word,HEX);Serial.print(", "); + Serial.println(transmit_dec,6); + #endif + + float ping_delay; + ping_delay=(tok-tik)/1000.0-(transmit_int-receive_int)-(transmit_dec-receive_dec); + ping_delay/=2.0; + if(ping_delay<=0){ + Serial.println("ERROR: ping_delay < 0.0!"); + } + + this->_lastUpdate=tok; + this->_currentEpoc=transmit_int - SEVENZYYEARS ; + this->_current_epoc_dec=ping_delay+transmit_dec; + if(this->_current_epoc_dec>1){ + this->_currentEpoc+=(int)this->_current_epoc_dec; + this->_current_epoc_dec-=(int)this->_current_epoc_dec; + } - this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + #ifdef DEBUG_NTPClient + Serial.print("current Epoc: "); + Serial.print(this->_currentEpoc);Serial.print(" "); + Serial.println(this->_current_epoc_dec,6); + #endif return true; // return true after successful update } -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(); +int8_t NTPClient::update() { + uint32_t now=millis(); + if(now>=this->_lastUpdate){ //if not overflow + if(now-this->_lastUpdate>=this->_updateInterval){ + if(now-this->_last_fail >= 1500){ + return this->forceUpdate(); + }else{ + return 3; //return 3 if last failed was just happen + } + } + }else{ //if overflowed + if(now+0xffffffff-this->_lastUpdate >= this->_updateInterval){ + if(now+0xffffffff-this->_last_fail >= 1500){ + return this->forceUpdate(); + }else{ + return 3; + } + } } - return false; // return false if update does not occur + return 2; // return 2 if update does not occur } 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 + this->_current_epoc_dec*1000)/1000.0); // Time since last update +} + +float NTPClient::get_millis() const{ + float ms = millis() - this->_lastUpdate + this->_current_epoc_dec*1000.0; + ms-=(int)(ms/1000)*1000; + return ms; } int NTPClient::getDay() const { @@ -174,7 +244,16 @@ void NTPClient::setUpdateInterval(unsigned long updateInterval) { } void NTPClient::setPoolServerName(const char* poolServerName) { - this->_poolServerName = poolServerName; + this->_poolServerName = poolServerName; +} + +void NTPClient::setPoolServerIP(IPAddress server_ip){ + this->_poolServerIP = server_ip; + this->_poolServerName = NULL; +} + +void NTPClient::setTimeout(uint16_t t_ms){ + this->_ntp_timeout=t_ms; } void NTPClient::sendNTPPacket() { diff --git a/NTPClient.h b/NTPClient.h index 20ec43b..5c86942 100755 --- a/NTPClient.h +++ b/NTPClient.h @@ -7,6 +7,7 @@ #define SEVENZYYEARS 2208988800UL #define NTP_PACKET_SIZE 48 #define NTP_DEFAULT_LOCAL_PORT 1337 +#define DEBUG_NTPClient class NTPClient { private: @@ -21,7 +22,11 @@ class NTPClient { unsigned long _updateInterval = 60000; // In ms unsigned long _currentEpoc = 0; // In s - unsigned long _lastUpdate = 0; // In ms + float _current_epoc_dec = 0; // In s, decimal part of current epoc + uint32_t _lastUpdate = 0xff000000;// In ms + + uint16_t _ntp_timeout = 1000; // In ms + uint32_t _last_fail = 0xffff0000; // In ms byte _packetBuffer[NTP_PACKET_SIZE]; @@ -44,6 +49,20 @@ class NTPClient { */ void setPoolServerName(const char* poolServerName); + /** + * clear time server and set ip + * + * @param ServerIP + */ + void setPoolServerIP(IPAddress server_ip); + + /** + * Set ntp timeout, recommand not above 1000ms + * + * @param t_ms + */ + void setTimeout(uint16_t t_ms); + /** * Starts the underlying UDP client with the default local port */ @@ -58,9 +77,12 @@ class NTPClient { * This should be called in the main loop of your application. By default an update from the NTP Server is only * made every 60 seconds. This can be configured in the NTPClient constructor. * - * @return true on success, false on failure + * @return 1(true) on updated and success + * 0(false) on updated and failure + * 2 on not time to update + * 3 on it's time to update but last failed was just happen, so it decided to wait more */ - bool update(); + int8_t update(); /** * This will force the update from the NTP Server. @@ -95,6 +117,11 @@ class NTPClient { */ unsigned long getEpochTime() const; + /** + * @return ms of this second, in ms + */ + float get_millis() const; + /** * Stops the underlying UDP client */ diff --git a/examples/ntp_demo.ino b/examples/ntp_demo.ino new file mode 100644 index 0000000..08568a6 --- /dev/null +++ b/examples/ntp_demo.ino @@ -0,0 +1,94 @@ +// change next line to use with another board/shield +#include +//#include +//#include +//#include +#include +#include "NTPClient.h" + +WiFiUDP ntpUDP; +NTPClient my_time_client(ntpUDP); + +void connect_wifi(char * ssid,char * password){ + Serial.print("wifi connecting..."); + WiFi.begin(ssid,password); + while(WiFi.status()!= WL_CONNECTED){ + delay(1000); + Serial.print("."); + } + + Serial.println("\nwifi connected!"); + Serial.print("ip: "); + Serial.println(WiFi.localIP()); + Serial.print("netmask: "); + Serial.println(WiFi.subnetMask()); + Serial.print("gateway: "); + Serial.println(WiFi.gatewayIP()); + Serial.print("channel: "); + Serial.println(WiFi.channel()); + Serial.print("auto-reconnect: "); + Serial.println(WiFi.getAutoReconnect()); +} + +inline void sync_time(){ + my_time_client.begin(); + my_time_client.setTimeOffset(28800); + my_time_client.setUpdateInterval(20000); + //my_time_client.setPoolServerName("0.ubuntu.pool.ntp.org"); + my_time_client.setPoolServerName("192.168.1.200"); + //smaller timeout will give you more accuracy + //but also larger possibility to fail + my_time_client.setTimeout(800); + Serial.println("syncing..."); + + while(my_time_client.update()!=1){ + delay(2000); + my_time_client.forceUpdate(); + } + + Serial.print("success: "); + Serial.println(my_time_client.getFormattedTime()); +} + +void setup(){ + Serial.begin(230400); + Serial.println("serial inited"); + + connect_wifi((char *)"ssid",(char *)"pwd"); + sync_time(); + + Serial.println("ready!"); +} + +String s_last_time="s_last_time"; + +void loop(){ + String s_time=my_time_client.getFormattedTime(); + if(s_time!=s_last_time){ + Serial.print("a second passed "); + Serial.print(s_time);Serial.print(" "); + Serial.print(my_time_client.get_millis(),3); + Serial.println("ms"); + s_last_time=s_time; + + //please do not update too frequently + int8_t re=my_time_client.update(); + if(re==0){ + Serial.println("0: sync but failed"); + delay(500); + }else if(re==1){ + Serial.println("1: sync and suc"); + }else if(re==2){ + ;//Serial.println("2: not time to sync"); + }else if(re==3){ + Serial.println("3: last failed was just happen"); + }else{ + Serial.print("return value error: "); + Serial.println(re); + } + } + + //float ms=my_time_client.get_millis(); + + delay(1); +} \ No newline at end of file