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

Make NTP request more robust. #7

Open
wants to merge 1 commit 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
45 changes: 25 additions & 20 deletions src/EasyNTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,17 @@ void EasyNTPClient::setTimeOffset (int offset) {

unsigned long EasyNTPClient::getServerTime () {
static int udpInited = this->mUdp->begin(123); // open socket on arbitrary port
// Only the first four bytes of an outgoing NTP packet need to be set
// appropriately, the rest can be whatever.
const long ntpFirstFourBytes = 0xEC0600E3; // NTP request header
// Only the first four bytes of an NTP request have to be set. The rest
// of the packet has to be 0, to avoid the server getting confused with the
// timestamps.
// see https://labs.apnic.net/?p=462 for details about the NTP packet
// structure
byte packetBuffer[NTP_PACKET_SIZE] = {0};

packetBuffer[0] = NTP_HEADER_LI|NTP_HEADER_VN|NTP_HEADER_MODE;
// packetBuffer[1] (stratum) skipped because it is already 0
packetBuffer[2] = 6; // polling interval in log2 seconds
packetBuffer[3] = 0xEC; // precision

// Fail if WiFiUdp.begin() could not init a socket
if (! udpInited)
Expand All @@ -54,7 +62,7 @@ unsigned long EasyNTPClient::getServerTime () {

// Send an NTP request
if (! (this->mUdp->beginPacket(this->mServerPool, 123) // 123 is the NTP port
&& this->mUdp->write((byte *)&ntpFirstFourBytes, 48) == 48
&& this->mUdp->write(packetBuffer, NTP_PACKET_SIZE) == NTP_PACKET_SIZE
&& this->mUdp->endPacket()))
return 0; // sending request failed

Expand All @@ -63,31 +71,28 @@ unsigned long EasyNTPClient::getServerTime () {
const byte maxPoll = 15; // poll up to this many times
int pktLen; // received packet length
for (byte i=0; i<maxPoll; i++) {
if ((pktLen = this->mUdp->parsePacket()) == 48)
break;
delay(pollIntv);
if ((pktLen = this->mUdp->parsePacket()) == NTP_PACKET_SIZE)
break;
delay(pollIntv);
}
if (pktLen != 48)
return 0; // no correct packet received
if (pktLen != NTP_PACKET_SIZE)
return 0; // no correct packet received

// Read and discard the first useless bytes
// Set useless to 32 for speed; set to 40 for accuracy.
const byte useless = 40;
for (byte i = 0; i < useless; ++i)
this->mUdp->read();
this->mUdp->read(packetBuffer, NTP_PACKET_SIZE);

// Read the integer part of sending time
unsigned long time = this->mUdp->read(); // NTP time
for (byte i = 1; i < 4; i++)
time = time << 8 | this->mUdp->read();
// Read the integer part (32 bits) of the Transmit Timestamp (64 bits total)
unsigned long time = packetBuffer[NTP_TX_TIMESTAMP_OFFSET];
time = time << 8 | packetBuffer[NTP_TX_TIMESTAMP_OFFSET + 1];
time = time << 8 | packetBuffer[NTP_TX_TIMESTAMP_OFFSET + 2];
time = time << 8 | packetBuffer[NTP_TX_TIMESTAMP_OFFSET + 3];

// Round to the nearest second if we want accuracy
// The fractionary part is the next byte divided by 256: if it is
// The fractionary part is the last byte divided by 256: if it is
// greater than 500ms we round to the next second; we also account
// for an assumed network delay of 50ms, and (0.5-0.05)*256=115;
// additionally, we account for how much we delayed reading the packet
// since its arrival, which we assume on average to be pollIntv/2.
time += (this->mUdp->read() > 115 - pollIntv/8);
time += (packetBuffer[NTP_TX_TIMESTAMP_OFFSET + 4] > 115 - pollIntv/8);

// Discard the rest of the packet
this->mUdp->flush();
Expand Down
12 changes: 11 additions & 1 deletion src/EasyNTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
#include "Arduino.h"
#include <Udp.h>

/*
* NTP packet-related macros.
* see https://labs.apnic.net/?p=462 for details about the NTP packet structure.
*/
#define NTP_PACKET_SIZE 48 // Size of an NTP packet
#define NTP_HEADER_LI 0b11000000 // leap indicator = 3 (unsynchronized)
#define NTP_HEADER_VN 0b00100000 // version number = 4
#define NTP_HEADER_MODE 0b00000011 // mode = 3 (client)
#define NTP_TX_TIMESTAMP_OFFSET 40 // offset within the packet for the TX time

class EasyNTPClient
{
public:
Expand All @@ -37,4 +47,4 @@ class EasyNTPClient
unsigned long getServerTime();
};

#endif
#endif