NTP clock made with ESP-12 (ESP8266) and 16-char TM1640 (JY-LM1640)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NTPClientLib.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*
  2. Copyright 2016 German Martin (gmag11@gmail.com). All rights reserved.
  3. Redistribution and use in source and binary forms, with or without modification, are
  4. permitted provided that the following conditions are met :
  5. 1. Redistributions of source code must retain the above copyright notice, this list of
  6. conditions and the following disclaimer.
  7. 2. Redistributions in binary form must reproduce the above copyright notice, this list
  8. of conditions and the following disclaimer in the documentation and / or other materials
  9. provided with the distribution.
  10. THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR IMPLIED
  11. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  12. FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
  13. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  14. CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  15. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  16. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING
  17. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  18. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  19. The views and conclusions contained in the software and documentation are those of the
  20. authors and should not be interpreted as representing official policies, either expressed
  21. or implied, of German Martin
  22. */
  23. //
  24. //
  25. //
  26. #include "NtpClientLib.h"
  27. #define DBG_PORT Serial
  28. #ifdef DEBUG_NTPCLIENT
  29. #define DEBUGLOG(...) DBG_PORT.printf(__VA_ARGS__)
  30. #else
  31. #define DEBUGLOG(...)
  32. #endif
  33. NTPClient::NTPClient () {
  34. }
  35. bool NTPClient::setNtpServerName (String ntpServerName) {
  36. char * name = (char *)malloc ((ntpServerName.length () + 1) * sizeof (char));
  37. if (!name)
  38. return false;
  39. ntpServerName.toCharArray (name, ntpServerName.length () + 1);
  40. DEBUGLOG ("NTP server set to %s\n", name);
  41. free (_ntpServerName);
  42. _ntpServerName = name;
  43. return true;
  44. }
  45. bool NTPClient::setNtpServerName (char* ntpServerName) {
  46. char *name = ntpServerName;
  47. if (name == NULL)
  48. return false;
  49. DEBUGLOG ("NTP server set to %s\n", name);
  50. free (_ntpServerName);
  51. _ntpServerName = name;
  52. return true;
  53. }
  54. String NTPClient::getNtpServerName () {
  55. return String (_ntpServerName);
  56. }
  57. char* NTPClient::getNtpServerNamePtr () {
  58. return _ntpServerName;
  59. }
  60. bool NTPClient::setTimeZone (int8_t timeZone, int8_t minutes) {
  61. if ((timeZone >= -12) && (timeZone <= 14) && (minutes >= -59) && (minutes <= 59)) {
  62. // Temporarily set time to new time zone, before trying to synchronize
  63. int8_t timeDiff = timeZone - _timeZone;
  64. _timeZone = timeZone;
  65. _minutesOffset = minutes;
  66. setTime (now () + timeDiff * SECS_PER_HOUR + minutes * SECS_PER_MIN);
  67. if (udp && (timeStatus () != timeNotSet)) {
  68. setTime (getTime ());
  69. }
  70. DEBUGLOG ("NTP time zone set to: %d\r\n", timeZone);
  71. return true;
  72. }
  73. return false;
  74. }
  75. boolean sendNTPpacket (const char* address, UDP *udp) {
  76. uint8_t ntpPacketBuffer[NTP_PACKET_SIZE]; //Buffer to store request message
  77. // set all bytes in the buffer to 0
  78. memset (ntpPacketBuffer, 0, NTP_PACKET_SIZE);
  79. // Initialize values needed to form NTP request
  80. // (see URL above for details on the packets)
  81. ntpPacketBuffer[0] = 0b11100011; // LI, Version, Mode
  82. ntpPacketBuffer[1] = 0; // Stratum, or type of clock
  83. ntpPacketBuffer[2] = 6; // Polling Interval
  84. ntpPacketBuffer[3] = 0xEC; // Peer Clock Precision
  85. // 8 bytes of zero for Root Delay & Root Dispersion
  86. ntpPacketBuffer[12] = 49;
  87. ntpPacketBuffer[13] = 0x4E;
  88. ntpPacketBuffer[14] = 49;
  89. ntpPacketBuffer[15] = 52;
  90. // all NTP fields have been given values, now
  91. // you can send a packet requesting a timestamp:
  92. udp->beginPacket (address, DEFAULT_NTP_PORT); //NTP requests are to port 123
  93. udp->write (ntpPacketBuffer, NTP_PACKET_SIZE);
  94. udp->endPacket ();
  95. return true;
  96. }
  97. time_t NTPClient::getTime () {
  98. //DNSClient dns;
  99. //WiFiUDP *udpClient = new WiFiUDP(*udp);
  100. IPAddress timeServerIP; //NTP server IP address
  101. char ntpPacketBuffer[NTP_PACKET_SIZE]; //Buffer to store response message
  102. DEBUGLOG ("Starting UDP\n");
  103. udp->begin (DEFAULT_NTP_PORT);
  104. //DEBUGLOG ("UDP port: %d\n",udp->localPort());
  105. while (udp->parsePacket () > 0); // discard any previously received packets
  106. /*dns.begin(WiFi.dnsServerIP());
  107. uint8_t dnsResult = dns.getHostByName(NTP.getNtpServerName().c_str(), timeServerIP);
  108. DEBUGLOG(F("NTP Server hostname: "));
  109. DEBUGLOGCR(NTP.getNtpServerName());
  110. DEBUGLOG(F("NTP Server IP address: "));
  111. DEBUGLOGCR(timeServerIP);
  112. DEBUGLOG(F("Result code: "));
  113. DEBUGLOG(dnsResult);
  114. DEBUGLOG(" ");
  115. DEBUGLOGCR(F("-- IP Connected. Waiting for sync"));
  116. DEBUGLOGCR(F("-- Transmit NTP Request"));*/
  117. //if (dnsResult == 1) { //If DNS lookup resulted ok
  118. sendNTPpacket (getNtpServerName ().c_str (), udp);
  119. uint32_t beginWait = millis ();
  120. while (millis () - beginWait < NTP_TIMEOUT) {
  121. int size = udp->parsePacket ();
  122. if (size >= NTP_PACKET_SIZE) {
  123. DEBUGLOG ("-- Receive NTP Response\n");
  124. udp->read (ntpPacketBuffer, NTP_PACKET_SIZE); // read packet into the buffer
  125. time_t timeValue = decodeNtpMessage (ntpPacketBuffer);
  126. setSyncInterval (getLongInterval ());
  127. if (!_firstSync) {
  128. // if (timeStatus () == timeSet)
  129. _firstSync = timeValue;
  130. }
  131. //getFirstSync (); // Set firstSync value if not set before
  132. DEBUGLOG ("Sync frequency set low\n");
  133. udp->stop ();
  134. setLastNTPSync (timeValue);
  135. DEBUGLOG ("Successful NTP sync at %s", getTimeDateString (getLastNTPSync ()).c_str ());
  136. if (onSyncEvent)
  137. onSyncEvent (timeSyncd);
  138. return timeValue;
  139. }
  140. #ifdef ARDUINO_ARCH_ESP8266
  141. ESP.wdtFeed ();
  142. #endif
  143. }
  144. DEBUGLOG ("-- No NTP Response :-(\n");
  145. udp->stop ();
  146. setSyncInterval (getShortInterval ()); // Retry connection more often
  147. if (onSyncEvent)
  148. onSyncEvent (noResponse);
  149. return 0; // return 0 if unable to get the time
  150. }
  151. int8_t NTPClient::getTimeZone () {
  152. return _timeZone;
  153. }
  154. int8_t NTPClient::getTimeZoneMinutes () {
  155. return _minutesOffset;
  156. }
  157. /*void NTPClient::setLastNTPSync(time_t moment) {
  158. _lastSyncd = moment;
  159. }*/
  160. time_t NTPClient::s_getTime () {
  161. return NTP.getTime ();
  162. }
  163. #if NETWORK_TYPE == NETWORK_W5100
  164. bool NTPClient::begin (String ntpServerName, int8_t timeZone, bool daylight, int8_t minutes, EthernetUDP* udp_conn) {
  165. #elif NETWORK_TYPE == NETWORK_ESP8266 || NETWORK_TYPE == NETWORK_WIFI101 || NETWORK_TYPE == NETWORK_ESP32
  166. bool NTPClient::begin (String ntpServerName, int8_t timeZone, bool daylight, int8_t minutes, WiFiUDP* udp_conn) {
  167. #endif
  168. if (!setNtpServerName (ntpServerName)) {
  169. DEBUGLOG ("Time sync not started\r\n");
  170. return false;
  171. }
  172. if (!setTimeZone (timeZone, minutes)) {
  173. DEBUGLOG ("Time sync not started\r\n");
  174. return false;
  175. }
  176. if (udp_conn)
  177. udp = udp_conn;
  178. else
  179. #if NETWORK_TYPE == NETWORK_W5100
  180. udp = new EthernetUDP ();
  181. #else
  182. udp = new WiFiUDP ();
  183. #endif
  184. //_timeZone = timeZone;
  185. setDayLight (daylight);
  186. _lastSyncd = 0;
  187. if (!setInterval (DEFAULT_NTP_SHORTINTERVAL, DEFAULT_NTP_INTERVAL)) {
  188. DEBUGLOG ("Time sync not started\r\n");
  189. return false;
  190. }
  191. DEBUGLOG ("Time sync started\r\n");
  192. setSyncInterval (getShortInterval ());
  193. setSyncProvider (s_getTime);
  194. return true;
  195. }
  196. bool NTPClient::stop () {
  197. setSyncProvider (NULL);
  198. DEBUGLOG ("Time sync disabled\n");
  199. return true;
  200. }
  201. bool NTPClient::setInterval (int interval) {
  202. if (interval >= 10) {
  203. if (_longInterval != interval) {
  204. _longInterval = interval;
  205. DEBUGLOG ("Sync interval set to %d\n", interval);
  206. if (timeStatus () == timeSet)
  207. setSyncInterval (interval);
  208. }
  209. return true;
  210. } else
  211. return false;
  212. }
  213. bool NTPClient::setInterval (int shortInterval, int longInterval) {
  214. if (shortInterval >= 10 && longInterval >= 10) {
  215. _shortInterval = shortInterval;
  216. _longInterval = longInterval;
  217. if (timeStatus () != timeSet) {
  218. setSyncInterval (shortInterval);
  219. } else {
  220. setSyncInterval (longInterval);
  221. }
  222. DEBUGLOG ("Short sync interval set to %d\n", shortInterval);
  223. DEBUGLOG ("Long sync interval set to %d\n", longInterval);
  224. return true;
  225. } else
  226. return false;
  227. }
  228. int NTPClient::getInterval () {
  229. return _longInterval;
  230. }
  231. int NTPClient::getShortInterval () {
  232. return _shortInterval;
  233. }
  234. void NTPClient::setDayLight (bool daylight) {
  235. _daylight = daylight;
  236. DEBUGLOG ("--Set daylight saving %s\n", daylight ? "ON" : "OFF");
  237. setTime (getTime ());
  238. }
  239. bool NTPClient::getDayLight () {
  240. return _daylight;
  241. }
  242. String NTPClient::getTimeStr (time_t moment) {
  243. char timeStr[10];
  244. sprintf (timeStr, "%02d:%02d:%02d", hour (moment), minute (moment), second (moment));
  245. return timeStr;
  246. }
  247. String NTPClient::getDateStr (time_t moment) {
  248. char dateStr[12];
  249. sprintf (dateStr, "/ %02d-%02d", day (moment), month (moment), year (moment));
  250. return dateStr;
  251. }
  252. String NTPClient::getTimeDateString (time_t moment) {
  253. return getTimeStr (moment) + " " + getDateStr (moment);
  254. }
  255. time_t NTPClient::getLastNTPSync () {
  256. return _lastSyncd;
  257. }
  258. void NTPClient::onNTPSyncEvent (onSyncEvent_t handler) {
  259. onSyncEvent = handler;
  260. }
  261. time_t NTPClient::getUptime () {
  262. _uptime = _uptime + (millis () - _uptime);
  263. return _uptime / 1000;
  264. }
  265. String NTPClient::getUptimeString () {
  266. uint16_t days;
  267. uint8_t hours;
  268. uint8_t minutes;
  269. uint8_t seconds;
  270. time_t uptime = getUptime ();
  271. seconds = uptime % SECS_PER_MIN;
  272. uptime -= seconds;
  273. minutes = (uptime % SECS_PER_HOUR) / SECS_PER_MIN;
  274. uptime -= minutes * SECS_PER_MIN;
  275. hours = (uptime % SECS_PER_DAY) / SECS_PER_HOUR;
  276. uptime -= hours * SECS_PER_HOUR;
  277. days = uptime / SECS_PER_DAY;
  278. char uptimeStr[20];
  279. sprintf (uptimeStr, "%4u days %02d:%02d:%02d", days, hours, minutes, seconds);
  280. return uptimeStr;
  281. }
  282. time_t NTPClient::getLastBootTime () {
  283. if (timeStatus () == timeSet) {
  284. return (now () - getUptime ());
  285. }
  286. return 0;
  287. }
  288. time_t NTPClient::getFirstSync () {
  289. /*if (!_firstSync) {
  290. if (timeStatus () == timeSet) {
  291. _firstSync = now () - getUptime ();
  292. }
  293. }*/
  294. return _firstSync;
  295. }
  296. bool NTPClient::summertime (int year, byte month, byte day, byte hour, byte tzHours)
  297. // input parameters: "normal time" for year, month, day, hour and tzHours (0=UTC, 1=MEZ)
  298. {
  299. if ((month < 3) || (month > 10)) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez
  300. if ((month > 3) && (month < 10)) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep
  301. if ((month == 3 && (hour + 24 * day) >= (1 + tzHours + 24 * (31 - (5 * year / 4 + 4) % 7))) || (month == 10 && (hour + 24 * day) < (1 + tzHours + 24 * (31 - (5 * year / 4 + 1) % 7))))
  302. return true;
  303. else
  304. return false;
  305. }
  306. boolean NTPClient::isSummerTimePeriod (time_t moment) {
  307. return summertime (year (), month (), day (), hour (), getTimeZone ());
  308. }
  309. void NTPClient::setLastNTPSync (time_t moment) {
  310. _lastSyncd = moment;
  311. }
  312. time_t NTPClient::decodeNtpMessage (char *messageBuffer) {
  313. unsigned long secsSince1900;
  314. // convert four bytes starting at location 40 to a long integer
  315. secsSince1900 = (unsigned long)messageBuffer[40] << 24;
  316. secsSince1900 |= (unsigned long)messageBuffer[41] << 16;
  317. secsSince1900 |= (unsigned long)messageBuffer[42] << 8;
  318. secsSince1900 |= (unsigned long)messageBuffer[43];
  319. #define SEVENTY_YEARS 2208988800UL
  320. time_t timeTemp = secsSince1900 - SEVENTY_YEARS + _timeZone * SECS_PER_HOUR + _minutesOffset * SECS_PER_MIN;
  321. if (_daylight) {
  322. if (summertime (year (timeTemp), month (timeTemp), day (timeTemp), hour (timeTemp), _timeZone)) {
  323. timeTemp += SECS_PER_HOUR;
  324. DEBUGLOG ("Summer Time\n");
  325. } else {
  326. DEBUGLOG ("Winter Time\n");
  327. }
  328. } else {
  329. DEBUGLOG ("No daylight\n");
  330. }
  331. return timeTemp;
  332. }
  333. NTPClient NTP;