snapshot
This commit is contained in:
288
libraries/NTP/NTP.cpp
Executable file
288
libraries/NTP/NTP.cpp
Executable file
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* NTP library for Arduino framework
|
||||
* The MIT License (MIT)
|
||||
* (c) 2022 sstaub
|
||||
*
|
||||
* 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 "NTP.h"
|
||||
|
||||
NTP::NTP(UDP& udp) {
|
||||
this->udp = &udp;
|
||||
}
|
||||
|
||||
NTP::~NTP() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void NTP::begin(const char* server) {
|
||||
this->server = server;
|
||||
init();
|
||||
}
|
||||
|
||||
void NTP::begin(IPAddress serverIP) {
|
||||
this->serverIP = serverIP;
|
||||
init();
|
||||
}
|
||||
|
||||
void NTP::init() {
|
||||
memset(ntpRequest, 0, NTP_PACKET_SIZE);
|
||||
ntpRequest[0] = 0b11100011; // LI, Version, Mode
|
||||
ntpRequest[1] = 0; // Stratum, or type of clock
|
||||
ntpRequest[2] = 6; // Polling Interval
|
||||
ntpRequest[3] = 0xEC; // Peer Clock Precision
|
||||
// 8 bytes of zero for Root Delay & Root Dispersion
|
||||
ntpRequest[12] = 49;
|
||||
ntpRequest[13] = 0x4E;
|
||||
ntpRequest[14] = 49;
|
||||
ntpRequest[15] = 52;
|
||||
udp->begin(NTP_PORT);
|
||||
ntpUpdate();
|
||||
if (dstZone) {
|
||||
timezoneOffset = dstEnd.tzOffset * SECS_PER_MINUTES;
|
||||
dstOffset = (dstStart.tzOffset - dstEnd.tzOffset) * SECS_PER_MINUTES;
|
||||
currentTime();
|
||||
beginDST();
|
||||
}
|
||||
}
|
||||
|
||||
void NTP::stop() {
|
||||
udp->stop();
|
||||
}
|
||||
|
||||
bool NTP::update() {
|
||||
if ((millis() - lastUpdate >= interval) || lastUpdate == 0) {
|
||||
return ntpUpdate();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NTP::ntpUpdate() {
|
||||
if (server == nullptr) udp->beginPacket(serverIP, NTP_PORT);
|
||||
else udp->beginPacket(server, NTP_PORT);
|
||||
udp->write(ntpRequest, NTP_PACKET_SIZE);
|
||||
udp->endPacket();
|
||||
uint8_t timeout = 0;
|
||||
uint8_t size = 0;
|
||||
do {
|
||||
delay (10);
|
||||
size = udp->parsePacket();
|
||||
if (timeout > 100) return false;
|
||||
timeout++;
|
||||
} while (size != 48);
|
||||
lastUpdate = millis() - (10 * (timeout + 1));
|
||||
udp->read(ntpQuery, NTP_PACKET_SIZE);
|
||||
#ifdef __AVR__
|
||||
unsigned long highWord = word(ntpQuery[40], ntpQuery[41]);
|
||||
unsigned long lowWord = word(ntpQuery[42], ntpQuery[43]);
|
||||
timestamp = highWord << 16 | lowWord;
|
||||
if (timestamp != 0) {
|
||||
ntpTime = timestamp;
|
||||
utcTime = ntpTime - NTP_OFFSET;
|
||||
}
|
||||
else return false;
|
||||
#else
|
||||
timestamp = ntpQuery[40] << 24 | ntpQuery[41] << 16 | ntpQuery[42] << 8 | ntpQuery[43];
|
||||
if (timestamp != 0) {
|
||||
ntpTime = timestamp;
|
||||
utcTime = ntpTime - SEVENTYYEARS;
|
||||
}
|
||||
else return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTP::updateInterval(uint32_t interval) {
|
||||
this->interval = interval;
|
||||
}
|
||||
|
||||
void NTP::ruleDST(const char* tzName, int8_t week, int8_t wday, int8_t month, int8_t hour, int tzOffset) {
|
||||
strcpy(dstStart.tzName, tzName);
|
||||
dstStart.week = week;
|
||||
dstStart.wday = wday;
|
||||
dstStart.month = month;
|
||||
dstStart.hour = hour;
|
||||
dstStart.tzOffset = tzOffset;
|
||||
}
|
||||
|
||||
const char* NTP::ruleDST() {
|
||||
if(dstZone) {
|
||||
return ctime(&dstTime);
|
||||
}
|
||||
else return RULE_DST_MESSAGE;
|
||||
}
|
||||
|
||||
void NTP::ruleSTD(const char* tzName, int8_t week, int8_t wday, int8_t month, int8_t hour, int tzOffset) {
|
||||
strcpy(dstEnd.tzName, tzName);
|
||||
dstEnd.week = week;
|
||||
dstEnd.wday = wday;
|
||||
dstEnd.month = month;
|
||||
dstEnd.hour = hour;
|
||||
dstEnd.tzOffset = tzOffset;
|
||||
}
|
||||
|
||||
const char* NTP::ruleSTD() {
|
||||
if(dstZone) {
|
||||
return ctime(&stdTime);
|
||||
}
|
||||
else return RULE_STD_MESSAGE;
|
||||
}
|
||||
|
||||
const char* NTP::tzName() {
|
||||
if (dstZone) {
|
||||
if (summerTime()) return dstStart.tzName;
|
||||
else return dstEnd.tzName;
|
||||
}
|
||||
return GMT_MESSAGE;
|
||||
}
|
||||
|
||||
void NTP::timeZone(int8_t tzHours, int8_t tzMinutes) {
|
||||
this->tzHours = tzHours;
|
||||
this->tzMinutes = tzMinutes;
|
||||
timezoneOffset = tzHours * 3600;
|
||||
if (tzHours < 0) {
|
||||
timezoneOffset -= tzMinutes * 60;
|
||||
}
|
||||
else {
|
||||
timezoneOffset += tzMinutes * 60;
|
||||
}
|
||||
}
|
||||
|
||||
void NTP::isDST(bool dstZone) {
|
||||
this->dstZone = dstZone;
|
||||
}
|
||||
|
||||
bool NTP::isDST() {
|
||||
return summerTime();
|
||||
}
|
||||
|
||||
time_t NTP::epoch() {
|
||||
currentTime();
|
||||
return utcCurrent;
|
||||
}
|
||||
|
||||
void NTP::currentTime() {
|
||||
utcCurrent = utcTime + ((millis() - lastUpdate) / 1000);
|
||||
if (dstZone) {
|
||||
if (summerTime()) {
|
||||
local = utcCurrent + dstOffset + timezoneOffset;
|
||||
current = gmtime(&local);
|
||||
}
|
||||
else {
|
||||
local = utcCurrent + timezoneOffset;
|
||||
current = gmtime(&local);
|
||||
}
|
||||
if ((current->tm_year + 1900) > yearDST) beginDST();
|
||||
}
|
||||
else {
|
||||
local = utcCurrent + timezoneOffset;
|
||||
current = gmtime(&local);
|
||||
}
|
||||
}
|
||||
|
||||
int16_t NTP::year() {
|
||||
currentTime();
|
||||
return current->tm_year + 1900;
|
||||
}
|
||||
|
||||
int8_t NTP::month() {
|
||||
currentTime();
|
||||
return current->tm_mon + 1;
|
||||
}
|
||||
|
||||
int8_t NTP::day() {
|
||||
currentTime();
|
||||
return current->tm_mday;
|
||||
}
|
||||
|
||||
int8_t NTP::weekDay() {
|
||||
currentTime();
|
||||
return current->tm_wday;
|
||||
}
|
||||
|
||||
int8_t NTP::hours() {
|
||||
currentTime();
|
||||
return current->tm_hour;
|
||||
}
|
||||
|
||||
int8_t NTP::minutes() {
|
||||
currentTime();
|
||||
return current->tm_min;
|
||||
}
|
||||
|
||||
int8_t NTP::seconds() {
|
||||
currentTime();
|
||||
return current->tm_sec;
|
||||
}
|
||||
|
||||
const char* NTP::formattedTime(const char *format) {
|
||||
currentTime();
|
||||
memset(timeString, 0, sizeof(timeString));
|
||||
strftime(timeString, sizeof(timeString), format, current);
|
||||
return timeString;
|
||||
}
|
||||
|
||||
void NTP::beginDST() {
|
||||
dstTime = calcDateDST(dstStart, current->tm_year + 1900);
|
||||
utcDST = dstTime - (dstEnd.tzOffset * SECS_PER_MINUTES);
|
||||
stdTime = calcDateDST(dstEnd, current->tm_year + 1900);
|
||||
utcSTD = stdTime - (dstStart.tzOffset * SECS_PER_MINUTES);
|
||||
yearDST = current->tm_year + 1900;
|
||||
}
|
||||
|
||||
time_t NTP::calcDateDST(struct ruleDST rule, int year) {
|
||||
uint8_t month = rule.month;
|
||||
uint8_t week = rule.week;
|
||||
if (week == 0) {
|
||||
if (month++ > 11) {
|
||||
month = 0;
|
||||
year++;
|
||||
}
|
||||
week = 1;
|
||||
}
|
||||
|
||||
struct tm tm;
|
||||
tm.tm_hour = rule.hour;
|
||||
tm.tm_min = 0;
|
||||
tm.tm_sec = 0;
|
||||
tm.tm_mday = 1;
|
||||
tm.tm_mon = month;
|
||||
tm.tm_year = year - 1900;
|
||||
time_t t = mktime(&tm);
|
||||
|
||||
t += ((rule.wday - tm.tm_wday + 7) % 7 + (week - 1) * 7 ) * SECS_PER_DAY;
|
||||
if (rule.week == 0) t -= 7 * SECS_PER_DAY;
|
||||
return t;
|
||||
}
|
||||
|
||||
bool NTP::summerTime() {
|
||||
if ((utcCurrent > utcDST) && (utcCurrent <= utcSTD)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t NTP::ntp() {
|
||||
return ntpTime;
|
||||
}
|
||||
|
||||
uint32_t NTP::utc() {
|
||||
return utcTime;
|
||||
}
|
||||
Reference in New Issue
Block a user