Skip to content
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

Increase time accuracy #102

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 103 additions & 24 deletions NTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down
33 changes: 30 additions & 3 deletions NTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define SEVENZYYEARS 2208988800UL
#define NTP_PACKET_SIZE 48
#define NTP_DEFAULT_LOCAL_PORT 1337
#define DEBUG_NTPClient

class NTPClient {
private:
Expand All @@ -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];

Expand All @@ -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
*/
Expand All @@ -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.
Expand Down Expand Up @@ -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
*/
Expand Down
94 changes: 94 additions & 0 deletions examples/ntp_demo.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// change next line to use with another board/shield
#include <WiFi.h>
//#include <ESP8266WiFi.h>
//#include <WiFi.h>
//#include <WiFi101.h>
#include <WiFiUdp.h>
#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);
}