NTPClient.cpp

NTPClient begin isValid forceUpdate
update getEpochtime getEpoch getDay
getHours getMinutes getSeconds getFormattedTime
getFormattedDate setTimeOffset setUpdateInterval sendNTPPacket
setEpochTime


/*H********************************************************
* 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"

typedef unsigned long ulng;



/*F******************************************************************** * NTPClient Constructors **********************************************************************/ NTPClient:: NTPClient( UDP &udp ) { this->_udp = &udp; } NTPClient:: NTPClient( UDP &udp, int timeOffset ) { this->_udp = &udp; this->_timeOffset = timeOffset; } NTPClient:: NTPClient( UDP &udp, const char *poolServerName ) { this->_udp = &udp; this->_poolServerName = poolServerName; } NTPClient:: NTPClient( UDP &udp, const char *poolServerName, int timeOffset ) { this->_udp = &udp; this->_timeOffset = timeOffset; this->_poolServerName = poolServerName; } NTPClient:: NTPClient( UDP &udp, const char *poolServerName, int timeOffset, unsigned long updateInterval ) { this->_udp = &udp; this->_timeOffset = timeOffset; this->_poolServerName = poolServerName; this->_updateInterval = updateInterval; }
/*F******************************************************************** * **********************************************************************/ void NTPClient:: begin() { this->begin( NTP_DEFAULT_LOCAL_PORT); } void NTPClient:: begin( int port ) { this->_port = port; this->_udp->begin( this->_port); this->_udpSetup = true; }
/*F******************************************************************** * **********************************************************************/ bool NTPClient:: isValid( byte *ntpPacket ) { // Perform a few validity checks on the packet if( (ntpPacket[0] & 0b11000000) == 0b11000000) //Check for LI=UNSYNC return false; if( (ntpPacket[0] & 0b00111000) >> 3 < 0b100) //Check Ver >= 4 return false; if( (ntpPacket[0] & 0b00000111) != 0b100) //Check for Mode == Server return false; if( (ntpPacket[1] < 1) || (ntpPacket[1] > 15))//Check for valid Stratum return false; if( ntpPacket[16] == 0 && ntpPacket[17] == 0 && ntpPacket[18] == 0 && ntpPacket[19] == 0 && ntpPacket[20] == 0 && ntpPacket[21] == 0 && ntpPacket[22] == 0 && ntpPacket[22] == 0)//Check for ReferenceTimestamp != 0 return false; return true; }
/*F******************************************************************** ETS CURREPOCH = SECS SINCE 1900 **********************************************************************/ bool NTPClient:: forceUpdate() { #ifdef DEBUG_NTPClient Serial.println( "Update from NTP Server" ); #endif while( this->_udp->parsePacket() != 0)// FLUSH ANY EXISTING PACKETS 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( cb > 0 ) { this->_udp->read( this->_packetBuffer, NTP_PACKET_SIZE ); if( !this->isValid( this->_packetBuffer ) ) cb = 0; } if( timeout > 100 ) return( false ); timeout++; // TIMEOUT AFTER 1000 MS }while( cb == 0 ); // WAIT FOR RESPONSE NTP PACKET this->_lastUpdate = millis() -( 10 *(timeout + 1));// FIX DLY IN RD TIM ulong highWord = word( this->_packetBuffer[40] , this->_packetBuffer[41]); ulong lowWord = word( this->_packetBuffer[42] , this->_packetBuffer[43]); // COMBINE FOUR BYTES (TWO WORDS) INTO LONG INT, NTP TIME (SCS SINCE Jan 1 1970): ulong secsSince1900 = highWord << 16 | lowWord; this->_currentEpoc = secsSince1900 - SEVENZYYEARS; // BACK TO UTC return( true ); }
/*F******************************************************************** * **********************************************************************/ bool NTPClient:: update() { // UPDATE AFTER _updateInterval || this->_lastUpdate == 0) if( (millis() - this->_lastUpdate >= this->_updateInterval ) { // UPDATE IF THERE WAS NO UPDATE YET if( !this->_udpSetup ) this->begin(); // SETUP UDP CLIENT IF NEEDED return this->forceUpdate(); } return( true ); }
/*F******************************************************************** * 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. **********************************************************************/ unsigned long NTPClient:: getEpochTime() { return( this->_timeOffset // USER OFFSET + this->_currentEpoc // EPOC RETURNED BY NTP SERVER + ((millis() // GET CURRENT TIME IN MS - this->_lastUpdate) / 1000)); // TIME SINCE LAST UPDATE }; }
/*F******************************************************************** Modified getEpochTime: * getEpoch returns NTP UTC epoch, which are econds 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. **********************************************************************/ unsigned long NTPClient:: getEpoch() { return( + this->_currentEpoc ); // EPOCH RETURNED BY NTP SERVER }
/*F******************************************************************** * **********************************************************************/ int NTPClient:: getDay() { return( ((this->getEpochTime() / 86400L) + 4 ) % 7); // 0 is Sunday }
/*F******************************************************************** * **********************************************************************/ int NTPClient:: getHours() { return( (this->getEpochTime() % 86400L) / 3600); }
/*F******************************************************************** * **********************************************************************/ int NTPClient:: getMinutes() { return( (this->getEpochTime() % 3600) / 60); }
/*F******************************************************************** * **********************************************************************/ int NTPClient:: getSeconds() { return (this->getEpochTime() % 60); }
/*F******************************************************************** * **********************************************************************/ String NTPClient:: getFormattedTime( unsigned long secs) { unsigned long rawTime = secs ? secs : 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 ); }
/*F******************************************************************** * Based on https://github.com/PaulStoffregen/Time/blob/master/Time.cpp currently assumes UTC timezone, instead of using this->_timeOffset **********************************************************************/ String NTPClient:: getFormattedDate( unsigned long secs) { unsigned long rawTime = (secs ? secs : this->getEpochTime()) / 86400L; // in days unsigned long days = 0, year = 1970; uint8_t month; static const uint8_t monthDays[]={31,28, 31,30,31,30,31,31,30,31, 30,31 } while( (days += (LEAP_YEAR(year) ? 366 : 365)) <= rawTime) year++; rawTime -= days - (LEAP_YEAR(year) ? 366 : 365); // now it is days in this year, starting at 0 days= 0; for( month=0; month<12; month++) { uint8_t monthLength; if( month ==1 ) { // february monthLength = LEAP_YEAR( year ) ? 29 : 28; } else { monthLength = monthDays[month]; } if( rawTime < monthLength) break; rawTime -= monthLength; } String monthStr = ++month < 10 ? "0" + String(month) : String(month); // jan is month 1 String dayStr = ++rawTime < 10 ? "0" + String(rawTime) : String(rawTime); // day of month return String(year) + "-" + monthStr + "- " + dayStr + "T" + this->getFormattedTime(secs ? secs : 0) + "Z"; }
/*F******************************************************************** * **********************************************************************/ void NTPClient:: end() { this->_udp->stop(); this->_udpSetup = false; }
/*F******************************************************************** * Sets time offset (in seconds) **********************************************************************/ void NTPClient:: setTimeOffset( int timeOffset ) { this->_timeOffset = timeOffset; }
/*F******************************************************************** * **********************************************************************/ void NTPClient:: setUpdateInterval( unsigned long updateInterval) { this->_updateInterval = updateInterval; }
/*F******************************************************************** * **********************************************************************/ 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 // (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] = 0x49; this->_packetBuffer[13] = 0x4E; this->_packetBuffer[14] = 0x49; this->_packetBuffer[15] = 0x52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: this->_udp->beginPacket( this->_poolServerName, 123); //NTP requests are to port 123 this->_udp->write( this->_packetBuffer, NTP_PACKET_SIZE); this->_udp->endPacket(); }
/*F******************************************************************** * **********************************************************************/ void NTPClient:: setEpochTime( unsigned long secs) { this->_currentEpoc = secs; }