An Arduino-targetted NTP Server class that supports both basic NTP requests, and a limited subset of control requests. I built this library to also serve up timezone information via NTP management requests.
Please note that the hardware used in this document is: NodeMCU-32S, ATGM336H-5N.
Although ATGM336H-5N can be powered by 5V, it is found that the GPS clock is about 6s slower than time.gov when using 5V, and it is reduced to less than 1s when using 3.3V power supply.
Compatible with Arduino ESP32 version 3.x
To initialize the NTP Server, you need to know what stratum and time reference you are using. In my case, I was obtaining my time source from a GPS receiver. This puts me in stratum 1 as a GPS reference:
WiFiNTPServer myServer("GPS", L_NTP_STRAT_PRIMARY); // Note: L_NTP_STRAT_PRIMARY=1
Now we can initialize the library with the begin method:
void setup() {
myServer.begin();
}
The next step is to add a call to update in your main loop:
void loop() {
myServer.update();
}
Lastly, the server will need to know the current time, which brings us to:
This method sets the reference time from an external source. The two parameters involved are as follows:
refTime: The reference time (i.e. the time as parsed from the GPS receiver)refTimeMicros: The processor time (i.e. a snapshot ofesp_timer_get_time()) that the reference time was taken at.
The more often the reference time is set from an external source, the more accurate the server will be. In the case of a GPS time server, you should capture refTimeMicros at the rising edge of the PPS signal, then call setReferenceTime once the serial time data has been decoded.
Sets the reference time, taking the current value of esp_timer_get_time() for convenience.
Sets the current stratum of the server. Valid values are 1 through 16. The stratum is reported back to clients with every request.
Sets the maximum poll interval that will be reported to clients. Note that the actual interval that will be locked in will be to the closest power of 2, as the NTP protocol definition of poll interval is 2 to the power of the interval.
Sets the reported server precision, in seconds.
Sets the root delay of the time source.
Sets the root dispersion of the time source.
Sets the NTP reference ID, in character format. Note that this function will fail if the reference ID is more than 4 bytes (exclusing null terminator).
Returns the number of milliseconds since the last time sync was performed via setReferenceTime
Returns the current local time, which is the last reference time plus the number of milliseconds since the last clock synchronization. This will only be as accurate as your processor's clock.
Returns true if the server's clock is in a synchonized state. The clock may desynchronize if we have not had a recent setReferenceTime call. This "maximum synchronization interval" is currently hard-coded, but soon will be configurable.
A note on clock synchronization: if the server determines that the clock is not in a synchonized state, all NTP requests will have the warning set that the clock is no longer synchonized (LI will be set to 3 and stratum will be set to 16).
Calling this function will force the clock to a desynchronized state. It is intended for the user to use this in extreme cases when the clock can no longer be trusted.
Returns the number of successful NTP requests serviced since the last inquiry. If resetCounter is set, the internal server counter will be set back to zero.
Returns the number of malformed/rejected NTP requests serviced since the last inquiry. If resetCounter is set, the internal server counter will be set back to zero.
To enable variable reading through NTP control packets, you need to hook into the onReadVariable callback. Until the callback is hooked into, any control requests against the server will fail. To set the callback, do the following:
int myCallbackFunction(const char *var, char *lpBuffer, int cbBuffer)
{
int result = L_NTP_R_ERROR;
if (!strncasecmp(var, "TZ", strlen(var)))
{
// Note: Take care not to exceed the buffer space as defined by cbBuffer
// Report back the current timezone setting (POSIX format)
strcpy(lpBuffer, "EST+5EDT,M3.2.0/2,M11.1.0/2");
result = L_NTP_R_SUCCESS; // Inform server class that we have a valid response
}
return result;
}
myServer.onReadVariable(myCallbackFunction);