├── .gitattributes ├── .gitignore ├── NTPtimeESP.cpp ├── NTPtimeESP.h ├── README.md └── examples └── NTPtimeESP └── NTPtimeESP.ino /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /NTPtimeESP.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | NTP 3 | This routine gets the unixtime from a NTP server and adjusts it to the time zone and the 4 | Middle European summer time if requested 5 | 6 | Author: Andreas Spiess V1.0 2016-5-28 7 | 8 | Based on work from John Lassen: http://www.john-lassen.de/index.php/projects/esp-8266-arduino-ide-webconfig 9 | 10 | */ 11 | 12 | #include 13 | #include "NTPtimeESP.h" 14 | 15 | #define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) 16 | 17 | #define SEC_TO_MS 1000 18 | #define RECV_TIMEOUT_DEFAULT 1 // 1 second 19 | #define SEND_INTRVL_DEFAULT 1 // 1 second 20 | #define MAX_SEND_INTERVAL 60 // 60 seconds 21 | #define MAC_RECV_TIMEOUT 60 // 60 seconds 22 | 23 | const int NTP_PACKET_SIZE = 48; 24 | byte _packetBuffer[ NTP_PACKET_SIZE]; 25 | static const uint8_t _monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 26 | 27 | float _timeZone=0.0; 28 | String _NTPserver=""; 29 | 30 | // NTPserver is the name of the NTPserver 31 | 32 | bool NTPtime::setSendInterval(unsigned long _sendInterval_) { 33 | bool retVal = false; 34 | if(_sendInterval_ <= MAX_SEND_INTERVAL) { 35 | _sendInterval = _sendInterval_ * SEC_TO_MS; 36 | retVal = true; 37 | } 38 | 39 | return retVal; 40 | } 41 | 42 | bool NTPtime::setRecvTimeout(unsigned long _recvTimeout_) { 43 | bool retVal = false; 44 | if(_recvTimeout_ <= MAC_RECV_TIMEOUT) { 45 | _recvTimeout = _recvTimeout_ * SEC_TO_MS; 46 | retVal = true; 47 | } 48 | 49 | return retVal; 50 | } 51 | 52 | NTPtime::NTPtime(String NTPserver) { 53 | _NTPserver = NTPserver; 54 | _sendPhase = true; 55 | _sentTime = 0; 56 | _sendInterval = SEND_INTRVL_DEFAULT * SEC_TO_MS; 57 | _recvTimeout = RECV_TIMEOUT_DEFAULT * SEC_TO_MS; 58 | } 59 | 60 | void NTPtime::printDateTime(strDateTime _dateTime) { 61 | if (_dateTime.valid) { 62 | Serial.print(_dateTime.year); 63 | Serial.print( "-"); 64 | Serial.print(_dateTime.month); 65 | Serial.print( "-"); 66 | Serial.print(_dateTime.day); 67 | Serial.print( "-"); 68 | Serial.print(_dateTime.dayofWeek); 69 | Serial.print( " "); 70 | 71 | Serial.print(_dateTime.hour); 72 | Serial.print( "H "); 73 | Serial.print(_dateTime.minute); 74 | Serial.print( "M "); 75 | Serial.print(_dateTime.second); 76 | Serial.print( "S "); 77 | Serial.println(); 78 | } else { 79 | #ifdef DEBUG_ON 80 | Serial.println("Invalid time !!!"); 81 | Serial.println(""); 82 | #endif 83 | } 84 | } 85 | 86 | // Converts a unix time stamp to a strDateTime structure 87 | strDateTime NTPtime::ConvertUnixTimestamp( unsigned long _tempTimeStamp) { 88 | strDateTime _tempDateTime; 89 | uint8_t _year, _month, _monthLength; 90 | uint32_t _time; 91 | unsigned long _days; 92 | 93 | _tempDateTime.epochTime = _tempTimeStamp; 94 | 95 | _time = (uint32_t)_tempTimeStamp; 96 | _tempDateTime.second = _time % 60; 97 | _time /= 60; // now it is minutes 98 | _tempDateTime.minute = _time % 60; 99 | _time /= 60; // now it is hours 100 | _tempDateTime.hour = _time % 24; 101 | _time /= 24; // now it is _days 102 | _tempDateTime.dayofWeek = ((_time + 4) % 7) + 1; // Sunday is day 1 103 | 104 | _year = 0; 105 | _days = 0; 106 | while ((unsigned)(_days += (LEAP_YEAR(_year) ? 366 : 365)) <= _time) { 107 | _year++; 108 | } 109 | _tempDateTime.year = _year; // year is offset from 1970 110 | 111 | _days -= LEAP_YEAR(_year) ? 366 : 365; 112 | _time -= _days; // now it is days in this year, starting at 0 113 | 114 | _days = 0; 115 | _month = 0; 116 | _monthLength = 0; 117 | for (_month = 0; _month < 12; _month++) { 118 | if (_month == 1) { // february 119 | if (LEAP_YEAR(_year)) { 120 | _monthLength = 29; 121 | } else { 122 | _monthLength = 28; 123 | } 124 | } else { 125 | _monthLength = _monthDays[_month]; 126 | } 127 | 128 | if (_time >= _monthLength) { 129 | _time -= _monthLength; 130 | } else { 131 | break; 132 | } 133 | } 134 | _tempDateTime.month = _month + 1; // jan is month 1 135 | _tempDateTime.day = _time + 1; // day of month 136 | _tempDateTime.year += 1970; 137 | 138 | return _tempDateTime; 139 | } 140 | 141 | 142 | // 143 | // Summertime calculates the daylight saving time for middle Europe. Input: Unixtime in UTC 144 | // 145 | boolean NTPtime::summerTime(unsigned long _timeStamp ) { 146 | 147 | strDateTime _tempDateTime; 148 | _tempDateTime = ConvertUnixTimestamp(_timeStamp); 149 | // printTime("Innerhalb ", _tempDateTime); 150 | 151 | if (_tempDateTime.month < 3 || _tempDateTime.month > 10) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez 152 | if (_tempDateTime.month > 3 && _tempDateTime.month < 10) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep 153 | if (_tempDateTime.month == 3 && (_tempDateTime.hour + 24 * _tempDateTime.day) >= (1 + 24 * (31 - (5 * _tempDateTime.year / 4 + 4) % 7)) || _tempDateTime.month == 10 && (_tempDateTime.hour + 24 * _tempDateTime.day) < (1 + 24 * (31 - (5 * _tempDateTime.year / 4 + 1) % 7))) 154 | return true; 155 | else 156 | return false; 157 | } 158 | 159 | boolean NTPtime::daylightSavingTime(unsigned long _timeStamp) { 160 | 161 | strDateTime _tempDateTime; 162 | _tempDateTime = ConvertUnixTimestamp(_timeStamp); 163 | 164 | // here the US code 165 | //return false; 166 | // see http://stackoverflow.com/questions/5590429/calculating-daylight-saving-time-from-only-date 167 | // since 2007 DST begins on second Sunday of March and ends on first Sunday of November. 168 | // Time change occurs at 2AM locally 169 | if (_tempDateTime.month < 3 || _tempDateTime.month > 11) return false; //January, february, and december are out. 170 | if (_tempDateTime.month > 3 && _tempDateTime.month < 11) return true; //April to October are in 171 | int previousSunday = _tempDateTime.day - (_tempDateTime.dayofWeek - 1); // dow Sunday input was 1, 172 | // need it to be Sunday = 0. If 1st of month = Sunday, previousSunday=1-0=1 173 | //int previousSunday = day - (dow-1); 174 | // -------------------- March --------------------------------------- 175 | //In march, we are DST if our previous Sunday was = to or after the 8th. 176 | if (_tempDateTime.month == 3 ) { // in march, if previous Sunday is after the 8th, is DST 177 | // unless Sunday and hour < 2am 178 | if ( previousSunday >= 8 ) { // Sunday = 1 179 | // return true if day > 14 or (dow == 1 and hour >= 2) 180 | return ((_tempDateTime.day > 14) || 181 | ((_tempDateTime.dayofWeek == 1 && _tempDateTime.hour >= 2) || _tempDateTime.dayofWeek > 1)); 182 | } // end if ( previousSunday >= 8 && _dateTime.dayofWeek > 0 ) 183 | else 184 | { 185 | // previousSunday has to be < 8 to get here 186 | //return (previousSunday < 8 && (_tempDateTime.dayofWeek - 1) = 0 && _tempDateTime.hour >= 2) 187 | return false; 188 | } // end else 189 | } // end if (_tempDateTime.month == 3 ) 190 | 191 | // ------------------------------- November ------------------------------- 192 | 193 | // gets here only if month = November 194 | //In november we must be before the first Sunday to be dst. 195 | //That means the previous Sunday must be before the 2nd. 196 | if (previousSunday < 1) 197 | { 198 | // is not true for Sunday after 2am or any day after 1st Sunday any time 199 | return ((_tempDateTime.dayofWeek == 1 && _tempDateTime.hour < 1) || (_tempDateTime.dayofWeek > 1)); 200 | //return true; 201 | } // end if (previousSunday < 1) 202 | else 203 | { 204 | // return false unless after first wk and dow = Sunday and hour < 2 205 | return (_tempDateTime.day <8 && _tempDateTime.dayofWeek == 1 && _tempDateTime.hour < 1); 206 | } // end else 207 | } // end boolean NTPtime::daylightSavingTime(unsigned long _timeStamp) 208 | 209 | 210 | unsigned long NTPtime::adjustTimeZone(unsigned long _timeStamp, float _timeZone, int _DayLightSaving) { 211 | 212 | if (_DayLightSaving ==1 && summerTime(_timeStamp)) _timeStamp += 3600; // European Summer time 213 | _timeStamp += (unsigned long)(_timeZone * 3600.0); // adjust timezone 214 | if (_DayLightSaving ==2 && daylightSavingTime(_timeStamp)) _timeStamp += 3600; // US daylight time 215 | return _timeStamp; 216 | } 217 | 218 | // time zone is the difference to UTC in hours 219 | // if _isDayLightSaving is true, time will be adjusted accordingly 220 | // Use returned time only after checking "ret.valid" flag 221 | 222 | strDateTime NTPtime::getNTPtime(float _timeZone, int _DayLightSaving) { 223 | int cb; 224 | strDateTime _dateTime; 225 | unsigned long _unixTime = 0; 226 | _dateTime.valid = false; 227 | unsigned long _currentTimeStamp; 228 | 229 | if (_sendPhase) { 230 | if (_sentTime && ((millis() - _sentTime) < _sendInterval)) { 231 | return _dateTime; 232 | } 233 | 234 | _sendPhase = false; 235 | UDPNTPClient.begin(1337); // Port for NTP receive 236 | 237 | #ifdef DEBUG_ON 238 | IPAddress _timeServerIP; 239 | WiFi.hostByName(_NTPserver.c_str(), _timeServerIP); 240 | Serial.println(); 241 | Serial.println(_timeServerIP); 242 | Serial.println("Sending NTP packet"); 243 | #endif 244 | 245 | memset(_packetBuffer, 0, NTP_PACKET_SIZE); 246 | _packetBuffer[0] = 0b11100011; // LI, Version, Mode 247 | _packetBuffer[1] = 0; // Stratum, or type of clock 248 | _packetBuffer[2] = 6; // Polling Interval 249 | _packetBuffer[3] = 0xEC; // Peer Clock Precision 250 | _packetBuffer[12] = 49; 251 | _packetBuffer[13] = 0x4E; 252 | _packetBuffer[14] = 49; 253 | _packetBuffer[15] = 52; 254 | UDPNTPClient.beginPacket(_NTPserver.c_str(), 123); 255 | UDPNTPClient.write(_packetBuffer, NTP_PACKET_SIZE); 256 | UDPNTPClient.endPacket(); 257 | 258 | _sentTime = millis(); 259 | } else { 260 | cb = UDPNTPClient.parsePacket(); 261 | if (cb == 0) { 262 | if ((millis() - _sentTime) > _recvTimeout) { 263 | _sendPhase = true; 264 | _sentTime = 0; 265 | } 266 | } else { 267 | #ifdef DEBUG_ON 268 | Serial.print("NTP packet received, length="); 269 | Serial.println(cb); 270 | #endif 271 | 272 | UDPNTPClient.read(_packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer 273 | unsigned long highWord = word(_packetBuffer[40], _packetBuffer[41]); 274 | unsigned long lowWord = word(_packetBuffer[42], _packetBuffer[43]); 275 | unsigned long secsSince1900 = highWord << 16 | lowWord; 276 | const unsigned long seventyYears = 2208988800UL; 277 | _unixTime = secsSince1900 - seventyYears; 278 | if (secsSince1900 > 0) { 279 | _currentTimeStamp = adjustTimeZone(_unixTime, _timeZone, _DayLightSaving); 280 | _dateTime = ConvertUnixTimestamp(_currentTimeStamp); 281 | _dateTime.valid = true; 282 | } else 283 | _dateTime.valid = false; 284 | 285 | _sendPhase = true; 286 | } 287 | } 288 | 289 | return _dateTime; 290 | } 291 | -------------------------------------------------------------------------------- /NTPtimeESP.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | NTPtime for ESP8266/ESP32 4 | This routine gets the unixtime from a NTP server and adjusts it to the time zone and the 5 | Middle European summer time if requested 6 | 7 | Author: Andreas Spiess V1.0 2016-6-28 8 | 9 | Based on work from John Lassen: http://www.john-lassen.de/index.php/projects/esp-8266-arduino-ide-webconfig 10 | 11 | */ 12 | #ifndef NTPtime_h 13 | #define NTPtime_h 14 | 15 | #include 16 | #ifdef ESP32 17 | #include 18 | #else 19 | #include 20 | #endif 21 | #include 22 | 23 | struct strDateTime 24 | { 25 | byte hour; 26 | byte minute; 27 | byte second; 28 | int year; 29 | byte month; 30 | byte day; 31 | byte dayofWeek; 32 | unsigned long epochTime; 33 | boolean valid; 34 | }; 35 | 36 | class NTPtime { 37 | public: 38 | NTPtime(String NTPtime); 39 | strDateTime getNTPtime(float _timeZone, int _DayLightSaving); 40 | void printDateTime(strDateTime _dateTime); 41 | bool setSendInterval(unsigned long _sendInterval); // in seconds 42 | bool setRecvTimeout(unsigned long _recvTimeout); // in seconds 43 | 44 | private: 45 | bool _sendPhase; 46 | unsigned long _sentTime; 47 | unsigned long _sendInterval; 48 | unsigned long _recvTimeout; 49 | 50 | strDateTime ConvertUnixTimestamp( unsigned long _tempTimeStamp); 51 | boolean summerTime(unsigned long _timeStamp ); 52 | boolean daylightSavingTime(unsigned long _timeStamp); 53 | unsigned long adjustTimeZone(unsigned long _timeStamp, float _timeZone, int _DayLightSavingSaving); 54 | WiFiUDP UDPNTPClient; 55 | }; 56 | #endif 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NTPtimeESP 2 | 3 | This library returns the queries the NTP time service and returns the actual time in a structure: 4 | 5 | ``` 6 | struct strDateTime 7 | { 8 | 9 | byte hour; 10 | byte minute; 11 | byte second; 12 | int year; 13 | byte month; 14 | byte day; 15 | byte dayofWeek; 16 | boolean valid; 17 | 18 | }; 19 | ``` 20 | 21 | The time can be automatically adjusted by time zone and European summer time. It runs on ESP8266 and ESP32 and needs an internet connection. 22 | -------------------------------------------------------------------------------- /examples/NTPtimeESP/NTPtimeESP.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch shows an example of sending a reading to data.sparkfun.com once per day. 3 | 4 | It uses the Sparkfun testing stream so the only customizing required is the WiFi SSID and password. 5 | 6 | The Harringay Maker Space 7 | License: Apache License v2 8 | */ 9 | #include 10 | 11 | #define DEBUG_ON 12 | 13 | 14 | NTPtime NTPch("ch.pool.ntp.org"); // Choose server pool as required 15 | char *ssid = ""; // Set you WiFi SSID 16 | char *password = ""; // Set you WiFi password 17 | 18 | /* 19 | * The structure contains following fields: 20 | * struct strDateTime 21 | { 22 | byte hour; 23 | byte minute; 24 | byte second; 25 | int year; 26 | byte month; 27 | byte day; 28 | byte dayofWeek; 29 | boolean valid; 30 | }; 31 | */ 32 | strDateTime dateTime; 33 | 34 | void setup() { 35 | Serial.begin(115200); 36 | Serial.println(); 37 | Serial.println("Booted"); 38 | Serial.println("Connecting to Wi-Fi"); 39 | 40 | WiFi.mode(WIFI_STA); 41 | WiFi.begin (ssid, password); 42 | while (WiFi.status() != WL_CONNECTED) { 43 | Serial.print("."); 44 | delay(500); 45 | } 46 | Serial.println("WiFi connected"); 47 | } 48 | 49 | void loop() { 50 | 51 | // first parameter: Time zone in floating point (for India); second parameter: 1 for European summer time; 2 for US daylight saving time; 0 for no DST adjustment; (contributed by viewwer, not tested by me) 52 | dateTime = NTPch.getNTPtime(1.0, 1); 53 | 54 | // check dateTime.valid before using the returned time 55 | // Use "setSendInterval" or "setRecvTimeout" if required 56 | if(dateTime.valid){ 57 | NTPch.printDateTime(dateTime); 58 | 59 | byte actualHour = dateTime.hour; 60 | byte actualMinute = dateTime.minute; 61 | byte actualsecond = dateTime.second; 62 | int actualyear = dateTime.year; 63 | byte actualMonth = dateTime.month; 64 | byte actualday =dateTime.day; 65 | byte actualdayofWeek = dateTime.dayofWeek; 66 | } 67 | } 68 | --------------------------------------------------------------------------------