I have been trying to make redundant NTP system, a valid input to my further system for time syncronization.
Sometimes due to connection slow / randomly single ntp server gives wrong values.
Below code to test the redundacy but problem happens.
i have tried many ntp servers like time.google.com, time.cloudflare.com etc. but result is 1 ntp is always right and 2nd will have another value or viceversa.
OUTPUT PROBLEM:
Epoch time from server 1: 20151
Epoch time from server 2: 1729945407
Epoch time from server 1: 20153
Epoch time from server 2: 1729945409
Epoch time from server 1: 20155
Epoch time from server 2: 1729945411
Epoch time from server 1: 20157
Epoch time from server 2: 1729945413
#include <WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
// Replace with your network credentials
const char* ssid = "XXXXX";
const char* password = "XXXXX";
// NTP Client Setup
WiFiUDP ntpUDP1, ntpUDP2; // Two separate UDP instances
NTPClient timeClient1(ntpUDP1, "pool.ntp.org", 19800, 60000); // NTP Server 1
NTPClient timeClient2(ntpUDP2, "time.nist.gov", 19800, 60000); // NTP Server 2
void setup() {
Serial.begin(115200); // Start Serial communication at 115200 baud
// Connect to WiFi
Serial.print("Connecting to WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected.");
// Start the NTP clients
timeClient1.begin();
timeClient2.begin();
}
void loop() {
// Update the NTP clients
timeClient1.update();
timeClient2.update();
// Print the current epoch time from both servers
Serial.print("Epoch time from server 1: ");
Serial.println(timeClient1.getEpochTime());
Serial.print("Epoch time from server 2: ");
Serial.println(timeClient2.getEpochTime());
// Wait for a while before updating again
delay(1000);
}
it could easily be modified to go through a list of NTP servers
it could look something like this
click to see the code
/*
# ============================================
# code is placed under the MIT license
# Copyright (c) 2024 J-M-L
# For the Arduino Forum : https://forum.arduino.cc/u/j-m-l
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# ===============================================
*/
#include <WiFi.h>
#include <esp_sntp.h>
const char *ssid = "Wokwi-GUEST";
const char *wifiPassword = "";
// Array of NTP servers
const char *ntpServers[] = {
"time.apple.com", // Apple
"time.google.com", // Google
"time.windows.com", // Microsoft
"pool.ntp.org", // NTP Pool Project
"time.nist.gov", // NIST
"time.cloudflare.com", // Cloudflare
"ntp.ubuntu.com", // Ubuntu
};
const size_t numNtpServers = sizeof ntpServers / sizeof *ntpServers;
bool syncCompleted = false;
bool wifiConnected = false;
size_t currentServerIndex = 0;
unsigned long syncStartTime = 0;
const unsigned long maxSyncWait = 10000; // Max time to wait for each server in ms
void onNTPTimeSync(struct timeval *tv) {
syncCompleted = true;
time_t lastNtpSync = time(NULL);
struct tm *pTime = localtime(&lastNtpSync);
Serial.print("NTP synchronized with ");
Serial.println(ntpServers[currentServerIndex]);
Serial.println(pTime, "%d/%m/%Y %H:%M:%S");
}
void onWiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
Serial.println("...Network connection established");
}
void syncWithCurrentServer() {
Serial.print("Trying NTP server: ");
Serial.println(ntpServers[currentServerIndex]);
// Configure NTP server and timezone (here configigured for France)
configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServers[currentServerIndex]);
// Set the NTP time sync callback
sntp_set_time_sync_notification_cb(onNTPTimeSync);
syncCompleted = false;
syncStartTime = millis();
}
void onWiFiStationGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
Serial.print("Obtained IP address = ");
Serial.println(WiFi.localIP());
wifiConnected = true;
syncWithCurrentServer(); // Start syncing with the first server
}
void onWiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
wifiConnected = false;
Serial.print("Network disconnected. (reason: ");
Serial.println(info.wifi_sta_disconnected.reason);
Serial.println(")\nAttempting to reconnect.");
WiFi.begin(ssid, wifiPassword, 6); // use 6 for wokwi, not needed on physical hardware
}
void requestFromNextServer() {
if (++currentServerIndex >= numNtpServers) {
Serial.println("All NTP servers have been tried. Cycling through.");
currentServerIndex = 0;
}
syncWithCurrentServer();
}
void setup() {
Serial.begin(115200);
Serial.println("NTP time management demonstration");
Serial.println("Connecting to WiFi network");
WiFi.onEvent(onWiFiStationConnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED);
WiFi.onEvent(onWiFiStationGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFi.onEvent(onWiFiStationDisconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
WiFi.begin(ssid, wifiPassword, 6);
}
void loop() {
if (wifiConnected) {
if (syncCompleted) {
delay(1000); // Wait for 1 second before trying the next server
requestFromNextServer();
}
} else {
// Check if we have timed out waiting for sync
if (millis() - syncStartTime >= maxSyncWait) {
Serial.print("Failed to sync with ");
Serial.println(ntpServers[currentServerIndex]);
requestFromNextServer();
}
}
}
another question arise in mind, is this equivalent to using pool.ntp.org ? as it automatically selects the available ntp at that time. Reducing load on mcu ?
Might or might not be related to your problem. I wanted an accurate NTP based clock and was having problems with drift. I started a topic about the problems I was having, which you can read here:
I never really got to the point of having the reliable clock I wanted, but I did learn a lot about NTP.
Interesting is why the results are so radically different:
I guess the two instances of NTPClient interfere with each other.
Since the transaction involves sending a request to an NTP server and waiting for the result, it is probably not possible to do this without a pause to allow the first to finish, at least not using the same port which is defined as 1337 #define NTP_DEFAULT_LOCAL_PORT 1337
EDIT
I once developed an radio clock which obtained its time signal from the DCF77 transmitter in Germany.
The time signal, when available, updated an RTC module. Sometimes the time signal was corrupted even though the check digit matched the payload. The solution there was to not to trust the time signal to update the RTC unless 2 near consecutive time signals were received with the same time difference to the RTC.
If you can't get a timestamp simultaneously from two different NTP servers, you may have to do something similar.
not sure how much drift is occurring, but NTP will never be quite correct due to transit times through the network
[Precision Time Protocol](Precision Time Protocol - Wikipedia attempts to measure the transit time in order to make a correction to the received time packet
due to multiple issues for checking valid time in ntp.
i have thought of simple idea as below, pls validate the same is it okay to use such method any issues?
because when ever my ntp gave me garbage value it had shown 19812. cross check from arduino serial monitor.
so implemented logic to ignore rtc.adjust only when value is greater than 5 digit. a little confusion should i keep that value to 9. as valid response of ntp is 1729958540.
timeClient.begin();
timeClient.update();
Serial.print("Epoch time from server: ");
Serial.println(timeClient.getEpochTime());
// Check if the epoch time is unexpectedly low (likely invalid)
if (timeClient.getEpochTime() < 100000) { // Any 5-digit time indicates an error in this case
Serial.println("Invalid epoch time received. Restarting ESP...");
delay(1000); // Short delay before restart
ESP.restart(); // Restart ESP to reattempt connection and time sync
} else{
rtc.adjust(timeClient.getEpochTime());
delay(100);
}
I think you should first address the basic problems with your method of obtaining a result from an NTP server. The core of the method used by @J-M-L above (basically configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServers[currentServerIndex]); uses only a few lines of code and compensates automatically for daylight saving time changes and is to be prefered over explicit use of low level protocols in an attempt to obtain time data. The example shown is, of course, more than a few lines but most of that is debug information.
I suppose it is theoretically possible that you contact a bad/rogue NTP server but if two yield mismatching results you still have to decide which to trust.
Incidentally, I have learned something new about Wokwi for testing IOT applications:
Epoch time from server 1: 1729967227
Epoch time from server 2: 1729967227
Epoch time from server 1: 1729967228
Epoch time from server 2: 1729967228
Epoch time from server 1: 1729967229
Epoch time from server 2: 1729967229
Epoch time from server 1: 1729967230
Epoch time from server 2: 1729967230
Indeed. I've just looked in a bit more detail here and found it: GitHub - arduino-libraries/NTPClient: Connect to a NTP server
That should solve the OP's original problem although I would still recommend the built-in SNTP time handling in the ESP32 / ESP8266 core because of the simplicity of use and the time zone handling.