commit 25797f1f869ff3ae7b97725dac79ed155347d56c Author: Fabrice Weinberg Date: Tue Dec 8 17:21:16 2015 +0100 Initial commit diff --git a/NTPClient.cpp b/NTPClient.cpp new file mode 100644 index 0000000..3edc86a --- /dev/null +++ b/NTPClient.cpp @@ -0,0 +1,145 @@ +/** + * 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(int timeOffset) { + this->_timeOffset = timeOffset; +} + +NTPClient::NTPClient(const char* poolServerName) { + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(const char* poolServerName, int timeOffset) { + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(const char* poolServerName, int timeOffset, int updateInterval) { + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; + this->_updateInterval = updateInterval; +} + +void NTPClient::begin() { + #ifdef DEBUG_NTPClient + Serial.println("Begin NTPClient"); + Serial.print("Start udp connection on port: "); + Serial.println(this->_port); + #endif + this->_udp.begin(this->_port); + this->forceUpdate(); +} + +void NTPClient::forceUpdate() { + #ifdef DEBUG_NTPClient + Serial.println("Update from NTP Server"); + #endif + + IPAddress address; + WiFi.hostByName(this->_poolServerName, address); + + this->sendNTPPacket(address); + + // Wait till data is there or timeout... + byte timeout = 0; + int cb = 0; + do { + delay ( 10 ); + cb = this->_udp.parsePacket(); + if (timeout > 100) return; // 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; +} + +void NTPClient::update() { + unsigned long runtime = millis(); + if (runtime - this->_lastUpdate >= this->_updateInterval && this->_updateInterval != 0) { + this->forceUpdate(); + } +} + +unsigned long NTPClient::getRawTime() { + return this->_timeOffset + // User offset + this->_currentEpoc + // Epoc returned by the NTP server + ((millis() - this->_lastUpdate) / 1000); // Time since last update +} + +String NTPClient::getHours() { + return String((this->getRawTime() % 86400L) / 3600); +} +String NTPClient::getMinutes() { + return String((this->getRawTime() % 3600) / 60); +} + +String NTPClient::getSeconds() { + return String(this->getRawTime() % 60); +} + +String NTPClient::getFormattedTime() { + unsigned long rawTime = this->getRawTime(); + 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::sendNTPPacket(IPAddress ip) { + // set all bytes in the buffer to 0 + memset(this->_packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + 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: + this->_udp.beginPacket(ip, 123); //NTP requests are to port 123 + this->_udp.write(this->_packetBuffer, NTP_PACKET_SIZE); + this->_udp.endPacket(); +} + diff --git a/NTPClient.h b/NTPClient.h new file mode 100644 index 0000000..a0469c0 --- /dev/null +++ b/NTPClient.h @@ -0,0 +1,65 @@ +#pragma once + +#include "Arduino.h" + +#include +#include + +#define SEVENZYYEARS 2208988800UL +#define NTP_PACKET_SIZE 48 + +class NTPClient { + private: + WiFiUDP _udp; + + const char* _poolServerName = "time.nist.gov"; // Default time server + int _port = 1337; + int _timeOffset; + + unsigned int _updateInterval = 60000; // In ms + + unsigned long _currentEpoc; // In s + unsigned long _lastUpdate = 0; // In ms + + byte _packetBuffer[NTP_PACKET_SIZE]; + + void sendNTPPacket(IPAddress _timeServerIP); + + public: + NTPClient(int timeOffset); + NTPClient(const char* poolServerName); + NTPClient(const char* poolServerName, int timeOffset); + NTPClient(const char* poolServerName, int timeOffset, int updateInterval); + + /** + * Starts the NTPClient + * This will create the UDP Socket and get the first update from the NTP server. This must be called after + * a WiFi connection is established. + */ + void begin(); + + /** + * 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. + */ + void update(); + + /** + * This will force the update from the NTP Server. + */ + void forceUpdate(); + + String getHours(); + String getMinutes(); + String getSeconds(); + + /** + * @return time formatted like `hh:mm:ss` + */ + String getFormattedTime(); + + /** + * @return time as raw seconds + */ + unsigned long getRawTime(); +}; diff --git a/README.md b/README.md new file mode 100644 index 0000000..04cd9a7 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# NTPClient + +Connect to a NTP server, here is how: + +```cpp +#include +#include + +const char *ssid = ""; +const char *password = ""; + +// By default 'time.nist.gov' is used. +NTPClient timeClient(); + +// You can specify the time server pool and the offset, (in seconds) +// additionaly you can specify the update interval (in milliseconds). +// NTPClient timeClient("europe.pool.ntp.org", 3600, 60000); + +void setup(){ + Serial.begin(11520); + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + // Start the NTPClient after an WiFi connection is established + timeClient.begin(); + +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} +``` \ No newline at end of file diff --git a/examples/Advanced/Advanced.ino b/examples/Advanced/Advanced.ino new file mode 100644 index 0000000..92e8284 --- /dev/null +++ b/examples/Advanced/Advanced.ino @@ -0,0 +1,32 @@ +#include +#include + +const char *ssid = ""; +const char *password = ""; + +// You can specify the time server pool and the offset, (in seconds) +// additionaly you can specify the update interval (in milliseconds). +NTPClient timeClient("europe.pool.ntp.org", 3600, 60000); + +void setup(){ + Serial.begin(115200); + + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + // Start the NTPClient after an WiFi connection is established + timeClient.begin(); + +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} diff --git a/examples/Basic/Basic.ino b/examples/Basic/Basic.ino new file mode 100644 index 0000000..0fc9442 --- /dev/null +++ b/examples/Basic/Basic.ino @@ -0,0 +1,32 @@ +#include +#include + +const char *ssid = ""; +const char *password = ""; + + + +NTPClient timeClient(); + +void setup(){ + Serial.begin(115200); + + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + // Start the NTPClient after an WiFi connection is established + timeClient.begin(); + +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..a76fe7b --- /dev/null +++ b/keywords.txt @@ -0,0 +1,18 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +NTPClient KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +update KEYWORD2 +forceUpdate KEYWORD2 +getHours KEYWORD2 +getMinutes KEYWORD2 +getSeconds KEYWORD2 +getFormattedTime KEYWORD2 +getRawTime KEYWORD2 \ No newline at end of file diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..0b4389b --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=NTPClient +version=1.0.0 +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/FWeinb/NTPClient +architectures=esp8266 \ No newline at end of file