From 3cbbd2fe569cb48bfe19b6f084d520add7f630b3 Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Fri, 13 Feb 2026 14:47:52 -0800 Subject: [PATCH] snapshot --- libraries/NTPClient/CHANGELOG | 15 ++ libraries/NTPClient/NTPClient.cpp | 212 ++++++++++++++++++ libraries/NTPClient/NTPClient.h | 114 ++++++++++ libraries/NTPClient/README.md | 52 +++++ .../NTPClient/examples/Advanced/Advanced.ino | 37 +++ libraries/NTPClient/examples/Basic/Basic.ino | 33 +++ .../examples/IsTimeSet/IsTimeSet.ino | 53 +++++ libraries/NTPClient/keywords.txt | 24 ++ libraries/NTPClient/library.properties | 9 + 9 files changed, 549 insertions(+) create mode 100644 libraries/NTPClient/CHANGELOG create mode 100755 libraries/NTPClient/NTPClient.cpp create mode 100755 libraries/NTPClient/NTPClient.h create mode 100644 libraries/NTPClient/README.md create mode 100644 libraries/NTPClient/examples/Advanced/Advanced.ino create mode 100644 libraries/NTPClient/examples/Basic/Basic.ino create mode 100644 libraries/NTPClient/examples/IsTimeSet/IsTimeSet.ino create mode 100644 libraries/NTPClient/keywords.txt create mode 100644 libraries/NTPClient/library.properties diff --git a/libraries/NTPClient/CHANGELOG b/libraries/NTPClient/CHANGELOG new file mode 100644 index 0000000..6a082d5 --- /dev/null +++ b/libraries/NTPClient/CHANGELOG @@ -0,0 +1,15 @@ +NTPClient 3.1.0 - 2016.05.31 + +* Added functions for changing the timeOffset and updateInterval later. Thanks @SirUli + +NTPClient 3.0.0 - 2016.04.19 + +* Constructors now require UDP instance argument, to add support for non-ESP8266 boards +* Added optional begin API to override default local port +* Added end API to close UDP socket +* Changed return type of update and forceUpdate APIs to bool, and return success or failure +* Change return type of getDay, getHours, getMinutes, and getSeconds to int + +Older + +* Changes not recorded diff --git a/libraries/NTPClient/NTPClient.cpp b/libraries/NTPClient/NTPClient.cpp new file mode 100755 index 0000000..b435855 --- /dev/null +++ b/libraries/NTPClient/NTPClient.cpp @@ -0,0 +1,212 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2015 by Fabrice Weinberg + * + * 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 "NTPClient.h" + +NTPClient::NTPClient(UDP& udp) { + this->_udp = &udp; +} + +NTPClient::NTPClient(UDP& udp, long timeOffset) { + this->_udp = &udp; + this->_timeOffset = timeOffset; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName) { + this->_udp = &udp; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP) { + this->_udp = &udp; + this->_poolServerIP = poolServerIP; + this->_poolServerName = NULL; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset) { + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset){ + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerIP = poolServerIP; + this->_poolServerName = NULL; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval) { + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; + this->_updateInterval = updateInterval; +} + +NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset, unsigned long updateInterval) { + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerIP = poolServerIP; + this->_poolServerName = NULL; + this->_updateInterval = updateInterval; +} + +void NTPClient::begin() { + this->begin(NTP_DEFAULT_LOCAL_PORT); +} + +void NTPClient::begin(unsigned int port) { + this->_port = port; + + this->_udp->begin(this->_port); + + this->_udpSetup = true; +} + +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(); + + this->sendNTPPacket(); + + // Wait till data is there or timeout... + byte timeout = 0; + int cb = 0; + do { + delay ( 10 ); + cb = this->_udp->parsePacket(); + if (timeout > 100) return false; // timeout after 1000 ms + timeout++; + } while (cb == 0); + + this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time + + 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]); + // 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; + + this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + + 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->_port != NTP_DEFAULT_LOCAL_PORT) this->begin(this->_port); // setup the UDP client if needed + return this->forceUpdate(); + } + return false; // return false if update does not occur +} + +bool NTPClient::isTimeSet() const { + return (this->_lastUpdate != 0); // returns true if the time has been set, else false +} + +unsigned long NTPClient::getEpochTime() const { + return this->_timeOffset + // User offset + this->_currentEpoc + // Epoch returned by the NTP server + ((millis() - this->_lastUpdate) / 1000); // Time since last update +} + +int NTPClient::getDay() const { + return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday +} +int NTPClient::getHours() const { + return ((this->getEpochTime() % 86400L) / 3600); +} +int NTPClient::getMinutes() const { + return ((this->getEpochTime() % 3600) / 60); +} +int NTPClient::getSeconds() const { + return (this->getEpochTime() % 60); +} + +String NTPClient::getFormattedTime() const { + unsigned long rawTime = this->getEpochTime(); + unsigned long hours = (rawTime % 86400L) / 3600; + String hoursStr = hours < 10 ? "0" + String(hours) : String(hours); + + unsigned long minutes = (rawTime % 3600) / 60; + String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes); + + unsigned long seconds = rawTime % 60; + String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds); + + return hoursStr + ":" + minuteStr + ":" + secondStr; +} + +void NTPClient::end() { + this->_udp->stop(); + + this->_udpSetup = false; +} + +void NTPClient::setTimeOffset(int timeOffset) { + this->_timeOffset = timeOffset; +} + +void NTPClient::setUpdateInterval(unsigned long updateInterval) { + this->_updateInterval = updateInterval; +} + +void NTPClient::setPoolServerName(const char* poolServerName) { + this->_poolServerName = poolServerName; +} + +void NTPClient::sendNTPPacket() { + // set all bytes in the buffer to 0 + memset(this->_packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + this->_packetBuffer[0] = 0b11100011; // LI, Version, Mode + this->_packetBuffer[1] = 0; // Stratum, or type of clock + this->_packetBuffer[2] = 6; // Polling Interval + this->_packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + this->_packetBuffer[12] = 49; + this->_packetBuffer[13] = 0x4E; + this->_packetBuffer[14] = 49; + this->_packetBuffer[15] = 52; + + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + if (this->_poolServerName) { + this->_udp->beginPacket(this->_poolServerName, 123); + } else { + this->_udp->beginPacket(this->_poolServerIP, 123); + } + this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE); + this->_udp->endPacket(); +} + +void NTPClient::setRandomPort(unsigned int minValue, unsigned int maxValue) { + randomSeed(analogRead(0)); + this->_port = random(minValue, maxValue); +} diff --git a/libraries/NTPClient/NTPClient.h b/libraries/NTPClient/NTPClient.h new file mode 100755 index 0000000..a31d32f --- /dev/null +++ b/libraries/NTPClient/NTPClient.h @@ -0,0 +1,114 @@ +#pragma once + +#include "Arduino.h" + +#include + +#define SEVENZYYEARS 2208988800UL +#define NTP_PACKET_SIZE 48 +#define NTP_DEFAULT_LOCAL_PORT 1337 + +class NTPClient { + private: + UDP* _udp; + bool _udpSetup = false; + + const char* _poolServerName = "pool.ntp.org"; // Default time server + IPAddress _poolServerIP; + unsigned int _port = NTP_DEFAULT_LOCAL_PORT; + long _timeOffset = 0; + + unsigned long _updateInterval = 60000; // In ms + + unsigned long _currentEpoc = 0; // In s + unsigned long _lastUpdate = 0; // In ms + + byte _packetBuffer[NTP_PACKET_SIZE]; + + void sendNTPPacket(); + + public: + NTPClient(UDP& udp); + NTPClient(UDP& udp, long timeOffset); + NTPClient(UDP& udp, const char* poolServerName); + NTPClient(UDP& udp, const char* poolServerName, long timeOffset); + NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval); + NTPClient(UDP& udp, IPAddress poolServerIP); + NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset); + NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset, unsigned long updateInterval); + + /** + * Set time server name + * + * @param poolServerName + */ + void setPoolServerName(const char* poolServerName); + + /** + * Set random local port + */ + void setRandomPort(unsigned int minValue = 49152, unsigned int maxValue = 65535); + + /** + * Starts the underlying UDP client with the default local port + */ + void begin(); + + /** + * Starts the underlying UDP client with the specified local port + */ + void begin(unsigned int port); + + /** + * 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 + */ + bool update(); + + /** + * This will force the update from the NTP Server. + * + * @return true on success, false on failure + */ + bool forceUpdate(); + + /** + * This allows to check if the NTPClient successfully received a NTP packet and set the time. + * + * @return true if time has been set, else false + */ + bool isTimeSet() const; + + int getDay() const; + int getHours() const; + int getMinutes() const; + int getSeconds() const; + + /** + * Changes the time offset. Useful for changing timezones dynamically + */ + void setTimeOffset(int timeOffset); + + /** + * Set the update interval to another frequency. E.g. useful when the + * timeOffset should not be set in the constructor + */ + void setUpdateInterval(unsigned long updateInterval); + + /** + * @return time formatted like `hh:mm:ss` + */ + String getFormattedTime() const; + + /** + * @return time in seconds since Jan. 1, 1970 + */ + unsigned long getEpochTime() const; + + /** + * Stops the underlying UDP client + */ + void end(); +}; diff --git a/libraries/NTPClient/README.md b/libraries/NTPClient/README.md new file mode 100644 index 0000000..f83882c --- /dev/null +++ b/libraries/NTPClient/README.md @@ -0,0 +1,52 @@ +# NTPClient + +[![Check Arduino status](https://github.com/arduino-libraries/NTPClient/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/check-arduino.yml) +[![Compile Examples status](https://github.com/arduino-libraries/NTPClient/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/compile-examples.yml) +[![Spell Check status](https://github.com/arduino-libraries/NTPClient/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/spell-check.yml) + +Connect to a NTP server, here is how: + +```cpp +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; + +// By default 'pool.ntp.org' is used with 60 seconds update interval and +// no offset +NTPClient timeClient(ntpUDP); + +// You can specify the time server pool and the offset, (in seconds) +// additionally you can specify the update interval (in milliseconds). +// NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); + +void setup(){ + Serial.begin(115200); + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + timeClient.begin(); +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} +``` + +## Function documentation +`getEpochTime` returns the Unix epoch, which are the seconds elapsed since 00:00:00 UTC on 1 January 1970 (leap seconds are ignored, every day is treated as having 86400 seconds). **Attention**: If you have set a time offset this time offset will be added to your epoch timestamp. diff --git a/libraries/NTPClient/examples/Advanced/Advanced.ino b/libraries/NTPClient/examples/Advanced/Advanced.ino new file mode 100644 index 0000000..18a6a97 --- /dev/null +++ b/libraries/NTPClient/examples/Advanced/Advanced.ino @@ -0,0 +1,37 @@ +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; + +// You can specify the time server pool and the offset (in seconds, can be +// changed later with setTimeOffset() ). Additionally you can specify the +// update interval (in milliseconds, can be changed using setUpdateInterval() ). +NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); + +void setup(){ + Serial.begin(115200); + + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + timeClient.begin(); +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} diff --git a/libraries/NTPClient/examples/Basic/Basic.ino b/libraries/NTPClient/examples/Basic/Basic.ino new file mode 100644 index 0000000..f0a2a7c --- /dev/null +++ b/libraries/NTPClient/examples/Basic/Basic.ino @@ -0,0 +1,33 @@ +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; +NTPClient timeClient(ntpUDP); + +void setup(){ + Serial.begin(115200); + + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + timeClient.begin(); +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} diff --git a/libraries/NTPClient/examples/IsTimeSet/IsTimeSet.ino b/libraries/NTPClient/examples/IsTimeSet/IsTimeSet.ino new file mode 100644 index 0000000..619bfde --- /dev/null +++ b/libraries/NTPClient/examples/IsTimeSet/IsTimeSet.ino @@ -0,0 +1,53 @@ +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; +// initialized to a time offset of 10 hours +NTPClient timeClient(ntpUDP,"pool.ntp.org", 36000, 60000); +// HH:MM:SS +// timeClient initializes to 10:00:00 if it does not receive an NTP packet +// before the 100ms timeout. +// without isTimeSet() the LED would be switched on, although the time +// was not yet set correctly. + +// blue LED on ESP-12F +const int led = 2; +const int hour = 10; +const int minute = 0; + +void setup(){ + Serial.begin(115200); + + pinMode(led, OUTPUT); + // led is off when pin is high + digitalWrite(led, 1); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay (500); + Serial.print ("."); + } + + timeClient.begin(); +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + if(timeClient.isTimeSet()) { + if (hour == timeClient.getHours() && minute == timeClient.getMinutes()) { + digitalWrite(led, 0); + } + } + + delay(1000); +} diff --git a/libraries/NTPClient/keywords.txt b/libraries/NTPClient/keywords.txt new file mode 100644 index 0000000..edce989 --- /dev/null +++ b/libraries/NTPClient/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +NTPClient KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 +update KEYWORD2 +forceUpdate KEYWORD2 +isTimeSet KEYWORD2 +getDay KEYWORD2 +getHours KEYWORD2 +getMinutes KEYWORD2 +getSeconds KEYWORD2 +getFormattedTime KEYWORD2 +getEpochTime KEYWORD2 +setTimeOffset KEYWORD2 +setUpdateInterval KEYWORD2 +setPoolServerName KEYWORD2 diff --git a/libraries/NTPClient/library.properties b/libraries/NTPClient/library.properties new file mode 100644 index 0000000..abdd80d --- /dev/null +++ b/libraries/NTPClient/library.properties @@ -0,0 +1,9 @@ +name=NTPClient +version=3.2.1 +author=Fabrice Weinberg +maintainer=Fabrice Weinberg +sentence=An NTPClient to connect to a time server +paragraph=Get time from a NTP server and keep it in sync. +category=Timing +url=https://github.com/arduino-libraries/NTPClient +architectures=*