Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
SNTPClient.cpp
00001 /* 00002 * SNTPClient.cpp 00003 * SNTP (Simple NTP) client 00004 * Written by iva2k 00005 * 00006 * Wrapper around LWIP/sntp for MBED, with DST rules. 00007 * This implementation relies on: 00008 * 1. LWIP (http://www.sics.se/~adam/lwip/) adopted for MBED 00009 * http://mbed.org/projects/cookbook/svn/EMAC/lwip/trunk 00010 * 2. LWIP's contributed SNTP client (sntp.c and sntp.h) from 00011 * http://cvs.savannah.gnu.org/viewvc/contrib/apps/sntp/?root=lwip 00012 * (used version 1.8) 00013 * 00014 * Changes needed in LWIP/sntp: 00015 * - pointer typecast (line 594) - fixes mbed compiler problem 00016 * - #include "sntp.h" moved after lwip/opt.h et.al - for re-defines. 00017 * - pbuf_free(p) in sntp_send_request() (line 602) - fixes memory leak BUG. 00018 * - changing sntp_dns_found() to delayed sntp_try_next_server() - to unblock if network is disconnected. 00019 * 00020 * Changes in MBED's LWIP: 00021 * - modified lwipopts.h (this file is automatically included into sntp.c) 00022 * 00023 * Requirements: 00024 * + direct RTC update from receiving the NTP time packet 00025 * + optionally support more than one NTP server 00026 * + the IP address of the NTP server(s) stored in a local file 00027 * + timeout error recovery should the NTP server(s) not be available. No RTC update should the NTP access fail 00028 * + allow for a UTC offset, also stored in the local file 00029 * + DST correction 00030 * + use DNS for NTP servers IP address resolution 00031 * + periodic updates at specified time intervals 00032 * 00033 * TODO: 00034 * . DST correction 00035 * - record metrics (count of updates, failed tries?, correction more than epsilon, etc.?) 00036 */ 00037 00038 #include "mbed.h" 00039 #include "lwip/opt.h" 00040 #include "SNTPClient.h" 00041 00042 #define SNTP_EPSILON 10 // time difference noticed as big update (in seconds). 00043 00044 //#define SNTP_DST_TESTS // Define this to run DST algorithms tests 00045 00046 typedef struct DST_ZONE_DESCR { 00047 tDST_ZONE zone; 00048 const char *name; 00049 int gmt; 00050 int dst; 00051 int hr1; 00052 int wk1; 00053 int wday1; 00054 int mon1; 00055 int hr2; 00056 int wk2; 00057 int wday2; 00058 int mon2; 00059 pFncDstCalc fnc; 00060 } tDST_ZONE_DESR; 00061 00062 static tDST_ZONE_DESR gSntpDstZones[] = { 00063 #define _(z, fnc, gmt, dst, hr1,wk1,wday1,mon1, hr2,wk2,wday2,mon2) \ 00064 { z, #z, gmt, dst, hr1,wk1,wday1,mon1, hr2,wk2,wday2,mon2, fnc }, 00065 #include "DstZones.h" 00066 }; 00067 #define DST_ZONE_DESCR_CNT (sizeof(gSntpDstZones)/sizeof(gSntpDstZones[0])) 00068 00069 const char *SNTPDstZoneName(tDST_ZONE zone) { 00070 if (zone >= DST_ZONE_DESCR_CNT) 00071 return ""; 00072 return gSntpDstZones[zone].name; 00073 } 00074 00075 #ifdef __cplusplus 00076 extern "C" { 00077 #endif 00078 tDST_ZONE gSntpDstZone = DST_NONE; // DST zone - rule selector 00079 unsigned int gSntpRecvTimeout_s = 3; // 3 sec; SNTP_RECV_TIMEOUT 00080 unsigned int gSntpUpdateDelay_s = 3600; // 1 hour; SNTP_UPDATE_DELAY 00081 signed int gSntpTimezone = 0*3600; // seconds from UTC to local time 00082 signed int gSntpDST = 0*3600; // seconds from UTC to local time 00083 bool gSntpRtcUtc = false; // true to keep RTC in UTC, false to keep in local time 00084 unsigned int gSntpUpdates = 0; // Track number of all clock NTP updates 00085 unsigned int gSntpUpdatesBig = 0; // Track number of significant clock NTP updates 00086 bool gSntpRunning = false; // true if SNTP service is running 00087 Timeout gSntpDelay; // For async calls. 00088 Ticker gSntpTicker; // There is no RTC interrupt on MBED (yet). Use (more wastefull) timer. 00089 time_t gSntpRtcTCR = 0; // Timer Capture Register equivalent (in RTC time - either local or UTC depending on gSntpRtcUtc) 00090 signed int gSntpRtcTCRDST = 0; // New DST value for when RTC crosses gSntpRtcTCR 00091 00092 static void SNTPSetDSTEx(unsigned int dst, bool adjust_clock) { 00093 if (adjust_clock && !gSntpRtcUtc && gSntpDST != dst) { 00094 time_t seconds = time(NULL); 00095 seconds -= gSntpDST; // Convert from old local time 00096 seconds += dst; // Convert to new local time 00097 set_time(seconds); 00098 if (gSntpRtcTCR) { 00099 // Adjust our alarm clock 00100 gSntpRtcTCR -= gSntpDST; 00101 gSntpRtcTCR += dst; 00102 } 00103 } 00104 gSntpDST = dst; 00105 } 00106 00107 #if 0 00108 // Custom DST zone function example 00109 // USA (since 2007) 00110 // Calculate start or stop DST point for given year based on rules 00111 // tz - Timezone 00112 // year - current year. DST changes well away from year end, so does not matter what timezone. 00113 // start - DST_START for start, DST_STOP for stop 00114 // Returns DST point (in local time). DST_STOP time is in DST 00115 static tDstPoint _sntp_dst_calc_custom(int tz, int year, tDST_START start) { 00116 tDstPoint ret; 00117 struct tm t; 00118 00119 // USA: 3600; 2, Second SUN, March; 2, First SUN, November 00120 int dst = 3600; // DST shift (seconds) 00121 int hour; // Hour of the change 00122 int N; // Week in the month 00123 int wday; // Day of the week 00124 int month; 00125 if (start == DST_START) { 00126 hour = 2; // (0-23) Hour of the change 00127 N = 2-1; // 2nd in the month 00128 wday = 0; // Sunday 00129 month = 2; // March 00130 } else { // DST_STOP 00131 hour = 2; // (0-23) Hour of the change 00132 N = 1-1; // 1st in the month 00133 wday = 0; // Sunday 00134 month = 10; // November 00135 } 00136 00137 t.tm_sec = 0; // 0-59 00138 t.tm_min = 0; // 0-59 00139 t.tm_hour = hour; // 0-23 00140 t.tm_year = year - 1900; // 00141 t.tm_mon = month; // 0-11 00142 t.tm_mday = 1+N*7; // 1-31, first possible date for Nth given weekday 00143 mktime(&t); // Calculate tm_wday 00144 t.tm_mday += (wday-t.tm_wday+14)%7; // Shift to wday 00145 ret.t = mktime(&t); 00146 ret.dst = (start == DST_START) ? dst : 0; 00147 ret.dstshift = dst; 00148 return ret; 00149 } 00150 #endif 00151 00152 // Calculate start or stop DST point for given year based on rules for the DST zone 00153 // tz - Timezone 00154 // zone - DST zone 00155 // year - current year. DST changes well away from year end, so does not matter timezone. 00156 // start - DST_START for start, DST_STOP for stop 00157 // Returns DST point (in standard local time). DST_STOP time is in NOT IN DST 00158 static tDstPoint _sntp_dst_calc(int tz, tDST_ZONE zone, int year, tDST_START start) { 00159 tDstPoint ret; 00160 00161 ret.t = 0; 00162 ret.dst = 0; 00163 ret.dstshift = 0; 00164 if (zone == DST_NONE || zone >= DST_ZONE_DESCR_CNT) 00165 return ret; 00166 00167 tDST_ZONE_DESR *descr = &gSntpDstZones[zone]; 00168 if (descr->fnc) { 00169 // Use custom function 00170 ret = descr->fnc(tz, year, start); 00171 } else { 00172 // Use rules 00173 struct tm t; 00174 t.tm_sec = 0; // 0-59 00175 t.tm_min = 0; // 0-59 00176 t.tm_hour = (start == DST_START) ? descr->hr1 : descr->hr2; 00177 t.tm_year = year - 1900; // 00178 t.tm_mon = (start == DST_START) ? descr->mon1 : descr->mon2; // 0-11 00179 int wk =((start == DST_START) ? descr->wk1 : descr->wk2) -1; 00180 int wday = (start == DST_START) ? descr->wday1: descr->wday2; 00181 if (wk < 0) { 00182 // For "Last in the month" - we go to next month, then move back by one week 00183 t.tm_mon += 1; // 0-11 00184 if (t.tm_mon > 11) { 00185 t.tm_mon -= 12; 00186 t.tm_year += 1; 00187 } 00188 t.tm_mday = 1+0*7; // 1-31, first possible date for Nth given weekday 00189 } else { 00190 t.tm_mday = 1+wk*7; // 1-31, first possible date for Nth given weekday 00191 } 00192 mktime(&t); // Calculate tm_wday 00193 t.tm_mday += (wday-t.tm_wday+14)%7; // Shift to wday 00194 ret.t = mktime(&t); 00195 if (wk < 0) { 00196 ret.t -= 7 * 24 * 3600; 00197 } 00198 if (descr->gmt) { 00199 ret.t += tz; 00200 } 00201 ret.dst = (start == DST_START) ? descr->dst : 0; 00202 ret.dstshift = descr->dst; 00203 } 00204 if (start == DST_STOP) { 00205 ret.t -= ret.dstshift; 00206 // this correction is due to the fact that rules are given in local time with DST, 00207 // but calculations are made in standard local time (without DST adjustment) 00208 } 00209 return ret; 00210 } 00211 00212 // Calculate desired DST point relative to now based on rules for the DST zone 00213 // tz - timezone 00214 // zone - DST zone 00215 // now - current time (standard local time = excluding DST). 00216 // index - 0 for immediate next, +1 for second next, -1 for immediate previous 00217 // Returns DST point (in standard local time) 00218 static tDstPoint _sntp_dst_point(int tz, tDST_ZONE zone, time_t now, int index) { 00219 tDstPoint ret; 00220 int year; 00221 tDST_START type; 00222 struct tm *pt = localtime(&now); 00223 00224 ret.t = 0; 00225 ret.dst = 0; 00226 ret.dstshift = 0; 00227 if (zone == DST_NONE || zone >= DST_ZONE_DESCR_CNT) 00228 return ret; 00229 00230 // Algorithm 00231 // 1. Determine where now is in respect to current year DST points (find for index=0) 00232 // 2. Use index to shift year and type 00233 // 3. return the point 00234 // This algorithm relies on DST start date being before DST stop date in the year. 00235 00236 year = pt->tm_year + 1900; 00237 type = DST_START; 00238 ret = _sntp_dst_calc(tz, zone, year, type); 00239 if (now > ret.t) { 00240 type = DST_STOP; 00241 ret = _sntp_dst_calc(tz, zone, year, type); 00242 if (now > ret.t) { 00243 // It is next year's start point 00244 type = DST_START; 00245 year += 1; 00246 } 00247 } 00248 // Now year and type are right for index=0 00249 00250 // Calculate where index points to - shift year and type 00251 int norm = (index > 0) ? (int)(index/2) : (int)((index-1)/2); 00252 year += norm; 00253 index -= norm*2; // Now index is (0,1) 00254 if (index) { 00255 // Flip the type 00256 type = (type == DST_START) ? DST_STOP : DST_START; 00257 } 00258 00259 ret = _sntp_dst_calc(tz, zone, year, type); 00260 return ret; 00261 00262 // struct tm t; 00263 // t.tm_sec = 0; // 0-59 00264 // t.tm_min = 0; // 0-59 00265 // t.tm_hour = 0; // 0-23 00266 // t.tm_mday = 0; // 1-31 00267 // t.tm_mon = 0; // 0-11 00268 // t.tm_year = 2005-1900; // year since 1900 00269 // t.tm_wday = 0; // 0-6 Day of week (Sunday = 0) 00270 // t.tm_yday = 0; // Day of year (0 - 365) 00271 // t.tm_isdst = -1; // Nonzero = Daylight saving time 00272 } 00273 00274 #if defined(LWIP_DEBUG) 00275 // Print DST dates report 00276 static void _sntp_dst_dates(int tz, tDST_ZONE zone, int year) { 00277 char _buffer[64]; 00278 tDstPoint r; 00279 printf("\r\nDST DATES for zone %d/%s:\r\n", (int)zone, SNTPDstZoneName(zone)); 00280 00281 r = _sntp_dst_calc(tz, zone, year, DST_START); 00282 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00283 printf(" %d DST BEGINS (+%04d sec) %s\r\n", year, r.dst, _buffer); 00284 00285 r = _sntp_dst_calc(tz, zone, year, DST_STOP); 00286 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00287 printf(" %d DST ENDS (+%04d sec) %s\r\n", year, r.dst, _buffer); 00288 } 00289 #endif 00290 00291 #ifdef SNTP_DST_TESTS 00292 static void test_sntp_dst_calc(int tz, tDST_ZONE zone) { 00293 char _buffer[64]; 00294 tDstPoint r; 00295 int year; 00296 00297 printf("\r\nTEST: _sntp_dst_calc(tz=%d, zone=%d/%s)\r\n", tz, (int)zone, SNTPDstZoneName(zone)); 00298 00299 for (year = 1999; year < 2016; year++) { 00300 r = _sntp_dst_calc(tz, zone, year, DST_START); 00301 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00302 printf(" (0,%d,start) %s %d\r\n", year, _buffer, r.dst); 00303 00304 r = _sntp_dst_calc(tz, zone, year, DST_STOP); 00305 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00306 printf(" (0,%d,stop ) %s %d\r\n", year, _buffer, r.dst); 00307 } 00308 } 00309 00310 static void test_sntp_dst_point(int index) { 00311 char _buffer[64]; 00312 struct tm t; 00313 tDstPoint r; 00314 int tz=gSntpTimezone; 00315 tDST_ZONE zone=gSntpDstZone; 00316 int year = 2009, day, month; 00317 00318 int norm = (index > 0) ? (int)(index/2) : (int)((index-1)/2); 00319 printf("\r\nTEST: _sntp_dst_point(%d) norm=%d\r\n", index, norm); 00320 00321 t.tm_sec = 0; // 0-59 00322 t.tm_min = 0; // 0-59 00323 t.tm_hour = 9; // 0-23 00324 t.tm_year = year-1900; 00325 00326 day = 1; 00327 for (month = 0; month < 2; month++) { 00328 t.tm_mon = month; 00329 t.tm_mday = day; 00330 t.tm_year = year-1900; 00331 r = _sntp_dst_point(tz, zone, mktime(&t), index); 00332 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00333 printf(" (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst); 00334 } 00335 00336 for (day = 1; day < 32; day++) { 00337 t.tm_mon = month; 00338 t.tm_mday = day; 00339 t.tm_year = year-1900; 00340 r = _sntp_dst_point(tz, zone, mktime(&t), index); 00341 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00342 printf(" (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst); 00343 } 00344 00345 day = 30; 00346 for (; month < 10; month++) { 00347 t.tm_mon = month; 00348 t.tm_mday = day; 00349 t.tm_year = year-1900; 00350 r = _sntp_dst_point(tz, zone, mktime(&t), index); 00351 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00352 printf(" (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst); 00353 } 00354 00355 for (day = 1; day < 32; day++) { 00356 t.tm_mon = month; 00357 t.tm_mday = day; 00358 t.tm_year = year-1900; 00359 r = _sntp_dst_point(tz, zone, mktime(&t), index); 00360 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t)); 00361 printf(" (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst); 00362 } 00363 00364 } 00365 #endif // SNTP_DST_TESTS 00366 00367 // Set current DST 00368 static void _sntp_dst_now(void) { 00369 time_t now = time(NULL); 00370 // Convert to standart local time (no DST) 00371 now = gSntpRtcUtc ? (now + gSntpTimezone) : (now - gSntpDST); 00372 00373 // Check DST setting for now 00374 tDstPoint dst = _sntp_dst_point(gSntpTimezone, gSntpDstZone, now, -1); 00375 00376 #ifdef LWIP_DEBUG 00377 char _buffer[64]; 00378 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&dst.t)); 00379 LWIP_DEBUGF(SNTP_DEBUG_STATE, ( 00380 "DEBUG: _sntp_dst_now(%d) based on last DST change on (%d) %s to (+%04d sec)\r\n", 00381 now, dst.t, _buffer, dst.dst)); 00382 #endif 00383 00384 // Change RTC 00385 SNTPSetDSTEx(dst.dst, true); 00386 } 00387 00388 // Plan for next DST change 00389 static void _sntp_dst_schedule(void) { 00390 time_t now = time(NULL); 00391 // Convert to standart local time (no DST) 00392 now = gSntpRtcUtc ? (now + gSntpTimezone) : (now - gSntpDST); 00393 00394 // Check next DST change point 00395 tDstPoint dst = _sntp_dst_point(gSntpTimezone, gSntpDstZone, now, 0); 00396 00397 if (dst.t) { 00398 00399 #ifdef LWIP_DEBUG 00400 char _buffer[64]; 00401 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&dst.t)); 00402 #endif 00403 00404 // Set our alarm clock 00405 // Convert from standard local to UTC time or local time 00406 dst.t = gSntpRtcUtc ? (dst.t - gSntpTimezone) : (dst.t + gSntpDST); 00407 00408 #ifdef LWIP_DEBUG 00409 if (time(NULL) > dst.t) { 00410 LWIP_DEBUGF(SNTP_DEBUG_STATE, ( 00411 "DEBUG: _sntp_dst_schedule() ASSERTION FAILED !(%d<%d) trying to schedule for the past: %s (+%04d sec)\r\n", 00412 time(NULL), dst.t, _buffer, dst.dst)); 00413 } else { 00414 LWIP_DEBUGF(SNTP_DEBUG_STATE, ( 00415 "DEBUG: _sntp_dst_schedule() scheduled in %d seconds, set for %s (+%04d sec)\r\n", 00416 dst.t-time(NULL), _buffer, dst.dst)); 00417 } 00418 #endif 00419 } 00420 gSntpRtcTCR = dst.t; 00421 gSntpRtcTCRDST = dst.dst; 00422 } 00423 00424 // RTC ISR - called upon each RTC tick 00425 static void _sntp_isr(void) { 00426 time_t seconds = time(NULL); 00427 if (gSntpRtcTCR && seconds > gSntpRtcTCR ) { 00428 // DST change has arrived 00429 gSntpRtcTCR = 0; 00430 // Disable ISR and avoid extra calcs in SNTPSetDSTEx() 00431 00432 //if (gSntpRtcTCRDST != gSntpDST) { 00433 // Change to/from DST 00434 SNTPSetDSTEx(gSntpRtcTCRDST, true); 00435 gSntpRtcTCRDST = 0; 00436 00437 // Schedule callback to plan for next DST change (take it out of ISR context) 00438 gSntpDelay.attach(_sntp_dst_schedule, 1.0); 00439 00440 #ifdef LWIP_DEBUG 00441 char _buffer[64]; 00442 if (gSntpRtcUtc) { 00443 seconds += gSntpTimezone + gSntpDST; // Convert to local time 00444 } 00445 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&seconds)); 00446 LWIP_DEBUGF(SNTP_DEBUG_STATE, ( 00447 "DEBUG: _sntp_isr() changed DST. datetime=%s\r\n", _buffer)); 00448 #endif 00449 00450 //} 00451 00452 } 00453 } 00454 00455 // NTP Callback - timestamp from NTP server arrives here 00456 void SntpClientSet(time_t seconds) { 00457 time_t old_seconds = time(NULL); 00458 if (!gSntpRtcUtc) { 00459 seconds += gSntpTimezone + gSntpDST; // Convert to local time 00460 } 00461 set_time(seconds); 00462 00463 // Special tasks for the very first NTP updates 00464 if (!gSntpUpdates) { 00465 00466 #if defined(LWIP_DEBUG) 00467 // Report DST dates for the zone 00468 struct tm *pt = localtime(&seconds); 00469 _sntp_dst_dates(gSntpTimezone, gSntpDstZone, pt->tm_year+1900); 00470 #endif 00471 00472 // DST scheduler 00473 _sntp_dst_now(); 00474 _sntp_dst_schedule(); 00475 00476 // Enable RTC ISR 00477 gSntpTicker.attach(_sntp_isr, 1.0); 00478 } 00479 00480 if (gSntpUpdates && abs((int)(old_seconds - seconds)) > SNTP_EPSILON) { 00481 printf("SNTP settime() corrected big difference: (%d) seconds, more than %d. Big updates count=%d.\r\n", 00482 old_seconds-seconds, SNTP_EPSILON, gSntpUpdatesBig); 00483 gSntpUpdatesBig ++; 00484 } 00485 00486 gSntpUpdates++; 00487 00488 if (1) { 00489 char _buffer[64]; 00490 if (gSntpRtcUtc) { 00491 seconds += gSntpTimezone + gSntpDST; // Convert to local time 00492 } 00493 strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&seconds)); 00494 printf("SNTP settime() #%d seconds=%d TZ=%0.1f datetime=%s\r\n", 00495 gSntpUpdates, seconds, gSntpTimezone/3600.0, _buffer); 00496 } 00497 } 00498 #ifdef __cplusplus 00499 }; 00500 #endif 00501 00502 #ifdef __cplusplus 00503 extern "C" { 00504 #endif 00505 00506 #include "lwip/def.h" 00507 #include "lwip/pbuf.h" 00508 #include "lwip/sys.h" 00509 #include "lwip/stats.h" 00510 #include "netif/etharp.h" 00511 #include "string.h" 00512 00513 #ifdef __cplusplus 00514 }; 00515 #endif 00516 00517 // Accomodating sntp timeout functions 00518 #if NO_SYS 00519 #include "sntp.h" 00520 extern void sntp_request(void *arg); // this is dirty hack around "static" function. Some linkers may revolt! 00521 extern void sntp_try_next_server(void *arg); 00522 extern char* sntp_server_addresses[]; 00523 extern u8_t sntp_current_server; 00524 extern u8_t sntp_num_servers; 00525 static void (*sntp_addresses_free)(void*) = NULL; 00526 static Timeout _sntp_timer1; 00527 static Timeout _sntp_timer2; 00528 void sntp_sys_timeout(u32_t timeout_s, void (*func)(void *arg), void *arg) { 00529 // all we really need to track is only 2 functions: sntp_request, sntp_try_next_server 00530 Timeout *t = NULL; 00531 if (func == &sntp_request) { 00532 t = &_sntp_timer1; 00533 LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_timeout(), func=sntp_request\r\n")); 00534 } else if (func == &sntp_try_next_server) { 00535 t = &_sntp_timer2; 00536 LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_timeout(), func=sntp_try_next_server\r\n")); 00537 } 00538 if (t) { 00539 t->detach(); 00540 t->attach((void(*)(void))func, 1.0*timeout_s); 00541 // Another shortcut - we have no arg to pass, so just typecast the func. 00542 } 00543 } 00544 00545 void sntp_sys_untimeout(void (*func)(void *arg), void *arg) { 00546 Timeout *t = NULL; 00547 if (func == &sntp_request) { 00548 t = &_sntp_timer1; 00549 LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_untimeout(), func=sntp_request\r\n")); 00550 } else if (func == &sntp_try_next_server) { 00551 t = &_sntp_timer2; 00552 LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_untimeout(), func=sntp_try_next_server\r\n")); 00553 } 00554 if (t) { 00555 t->detach(); 00556 } 00557 } 00558 00559 #else // NO_SYS 00560 #error "I don't know how to compile LWIP/SNTP with NO_SYS=0" 00561 #endif // NO_SYS 00562 00563 // Can be called when already running 00564 void SNTPSetDstZone(tDST_ZONE zone) { 00565 if (zone >= DST_ZONE_DESCR_CNT) 00566 return; // ERR_INVALID_ARG 00567 00568 gSntpDstZone = zone; 00569 00570 if (gSntpRunning) { 00571 // DST scheduler 00572 _sntp_dst_now(); 00573 _sntp_dst_schedule(); 00574 } 00575 } 00576 00577 void SNTPSetRecvTimeout(unsigned int val_s) { gSntpRecvTimeout_s = val_s; } 00578 void SNTPSetUpdateDelay(unsigned int val_s) { gSntpUpdateDelay_s = val_s; } 00579 void SNTPSetTimezone(float hours_from_utc, bool adjust_clock) { 00580 if (adjust_clock && !gSntpRtcUtc) { 00581 time_t seconds = time(NULL); 00582 seconds -= gSntpTimezone; // Convert from old local time 00583 seconds += hours_from_utc * 3600; // Convert to new local time 00584 set_time(seconds); 00585 if (gSntpRtcTCR) { 00586 // Adjust our alarm clock 00587 gSntpRtcTCR -= gSntpTimezone; 00588 gSntpRtcTCR += hours_from_utc * 3600; 00589 } 00590 } 00591 gSntpTimezone = hours_from_utc * 3600; 00592 } 00593 void SNTPSetDST(float hours_from_utc, bool adjust_clock) { 00594 SNTPSetDSTEx(hours_from_utc * 3600, adjust_clock); 00595 } 00596 00597 static int sntp_num_servers_alloc = 0; 00598 static void _SNTPClrAddresses(void) { 00599 if (!sntp_num_servers_alloc) sntp_num_servers_alloc = sntp_num_servers; 00600 // Here we save the original size of the sntp_server_addresses[] array. 00601 00602 if (sntp_addresses_free) { 00603 for (int i=0; i<sntp_num_servers; i++) { 00604 sntp_addresses_free(sntp_server_addresses[i]); 00605 } 00606 } 00607 sntp_current_server = 0; 00608 sntp_num_servers = 0; 00609 sntp_addresses_free = NULL; 00610 } 00611 static int _SNTPAddAddress(const char* server_address) { 00612 if (sntp_num_servers+1 > sntp_num_servers_alloc) 00613 return -1; 00614 sntp_server_addresses[sntp_num_servers] = (char*)malloc(strlen(server_address)+1); 00615 if (sntp_server_addresses[sntp_num_servers] == NULL) return -1; // Out of memory 00616 strcpy(sntp_server_addresses[sntp_num_servers], server_address); 00617 sntp_num_servers++; 00618 return 0; 00619 } 00620 00621 // Override default servers list. 00622 // For no-copy, pass pointer to free() in p_free. 00623 // Returns: actual number of servers set (limited by allocated buffer size) 00624 // WARNING! There is no interlock to ensure that SNTP service does not read its data while we are updating it. 00625 // This function is intended to be called only before SNTPClientInit(). 00626 int SNTPSetAddresses(const char* server_addresses[], int count, void (*p_free)(void*)) { 00627 // In order to use sntp.c as-is, we hack into its static variables. 00628 // Not all compilers/linkers will support that. 00629 00630 _SNTPClrAddresses(); 00631 if (count > sntp_num_servers_alloc) 00632 count = sntp_num_servers_alloc; 00633 for (int i=0; i<count; i++) { 00634 if (p_free) { 00635 sntp_server_addresses[i] = (char *)server_addresses[i]; 00636 } else { 00637 _SNTPAddAddress(server_addresses[i]); 00638 } 00639 } 00640 sntp_num_servers = count; 00641 sntp_current_server = 0; 00642 sntp_addresses_free = p_free ? p_free : &free; 00643 return count; 00644 } 00645 00646 // Trim whitespace/CRLFs from both ends of a given string 00647 char * str_cleanup(char *in) { 00648 char * out = in; 00649 // Trim leading spaces and CR/LF 00650 while (*out == ' ' || *out == '\t' || *out == '\r' || *out == '\n') 00651 out ++; 00652 // Trim trailing spaces and CR/LF 00653 int len = strlen(out)-1; 00654 while (out[len] == ' ' || out[len] == '\t' || out[len] == '\r' || out[len] == '\n') { 00655 out[len] = '\0'; 00656 len--; 00657 } 00658 return out; 00659 } 00660 00661 #ifndef CRLF 00662 #define CRLF "\r\n" 00663 #endif 00664 void SNTPWriteIniFile(FILE * f) { 00665 fprintf(f, "# SNTP Configuration file" CRLF); 00666 fprintf(f, CRLF "[Servers]" CRLF); 00667 for (int i=0; i<sntp_num_servers; i++) { 00668 fprintf(f, "%s" CRLF, sntp_server_addresses[i]); 00669 } 00670 fprintf(f, CRLF "[Global]" CRLF); 00671 fprintf(f, "RtcUtc=%d" CRLF, (int)gSntpRtcUtc); 00672 fprintf(f, "Timezone=%0.1f" CRLF, gSntpTimezone / 3600.0); 00673 fprintf(f, "DstZone=%d" CRLF, gSntpDstZone); 00674 fprintf(f, "# %s" CRLF, SNTPDstZoneName(gSntpDstZone)); 00675 fprintf(f, "UpdateDelay=%d" CRLF, gSntpUpdateDelay_s); 00676 fprintf(f, "RecvTimeout=%d" CRLF, gSntpRecvTimeout_s); 00677 fprintf(f, CRLF "##END" CRLF); 00678 } 00679 00680 // Simple ini file parser for SNTP configuration (Case-sensitive!) 00681 // This function is intended to be called only before SNTPClientInit(). 00682 // Returns: 0 if OK, errno otherwise 00683 enum { 00684 SECT_NONE, 00685 SECT_SERVERS, 00686 SECT_GLOBAL, 00687 }; 00688 int SNTPReadIniFile(const char* filename) { 00689 FILE *f; 00690 char buf[512]; 00691 bool addresses_cleared = false; 00692 00693 f = fopen(filename, "r"); 00694 if (!f) 00695 return -1; // errno not used? 00696 00697 char *buf1, *buf2; 00698 int section=SECT_NONE; 00699 int line = 0; 00700 while (fgets(buf, sizeof(buf)/sizeof(buf[0]), f)) { 00701 line++; 00702 buf1 = str_cleanup(buf); 00703 if (*buf1 == '#' || *buf1 == '\0') 00704 continue; // Comment line or empty line - skip 00705 if (*buf1 == '[') { 00706 // New section 00707 if (0 == strncmp(buf1,"[Servers]", sizeof("[Servers]")-1)) { 00708 section=SECT_SERVERS; 00709 if (!addresses_cleared) { 00710 // Clear addresses only once. 00711 _SNTPClrAddresses(); 00712 addresses_cleared = true; 00713 } 00714 } else if (0 == strncmp(buf1,"[Global]", sizeof("[Global]")-1)) { 00715 section=SECT_GLOBAL; 00716 } else { 00717 section=SECT_NONE; 00718 fprintf(stderr, "File \"%s\", line %d - section \"%s\" is not understood.\r\n", filename, line, buf1); 00719 } 00720 } else { 00721 // Section values 00722 switch (section) { 00723 case SECT_SERVERS: 00724 if (_SNTPAddAddress(buf1)) { 00725 fprintf(stderr, "File \"%s\", line %d - cannot add server \"%s\" - exceeded allocated slots.\r\n", filename, line, buf1); 00726 } 00727 break; 00728 case SECT_GLOBAL: 00729 buf2 = strchr(buf1, '='); 00730 if (buf2) { 00731 *buf2++ = '\0'; // Now buf1 has variable name, buf2 has value 00732 buf2 = str_cleanup(buf2); 00733 if (0 == strncmp(buf1, "Timezone", sizeof("Timezone")-1)) { 00734 gSntpTimezone = strtod(buf2, &buf2) * 3600; 00735 } else if (0 == strncmp(buf1, "UpdateDelay", sizeof("UpdateDelay")-1)) { 00736 gSntpUpdateDelay_s = strtoul(buf2, &buf2, 10); 00737 } else if (0 == strncmp(buf1, "RecvTimeout", sizeof("RecvTimeout")-1)) { 00738 gSntpRecvTimeout_s = strtoul(buf2, &buf2, 10); 00739 } else if (0 == strncmp(buf1, "RtcUtc", sizeof("RtcUtc")-1)) { 00740 gSntpRtcUtc = (bool)strtol(buf2, &buf2, 10); 00741 } else if (0 == strncmp(buf1, "DstZone", sizeof("DstZone")-1)) { 00742 // FIXME: It would be nice to allow human-readable string here. 00743 gSntpDstZone = (tDST_ZONE)strtol(buf2, &buf2, 10); 00744 } else { 00745 fprintf(stderr, "File \"%s\", line %d - unrecognized variable \"%s\" in section [Global]\r\n", filename, line, buf1); 00746 } 00747 } else { 00748 fprintf(stderr, "File \"%s\", line %d - unrecognized statement in section [Global]: %s\r\n", filename, line, buf1); 00749 } 00750 break; 00751 default: 00752 fprintf(stderr, "File \"%s\", line %d - unrecognized statement / no section: %s\r\n", filename, line, buf1); 00753 } 00754 } 00755 } 00756 fclose(f); 00757 printf("SNTP configuration read from file \"%s\", %d lines.\r\n", filename, line); 00758 return 0; 00759 } 00760 00761 // Start the SNTP client 00762 void SNTPClientInit(void) { 00763 00764 #ifdef SNTP_DST_TESTS 00765 // Test our DST algorithms 00766 test_sntp_dst_calc(gSntpTimezone, gSntpDstZone); 00767 test_sntp_dst_point(-2); 00768 test_sntp_dst_point(-1); 00769 test_sntp_dst_point(0); 00770 test_sntp_dst_point(1); 00771 test_sntp_dst_point(2); 00772 #endif 00773 00774 // SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds 00775 if (gSntpUpdateDelay_s < 15) { 00776 gSntpUpdateDelay_s = 15; 00777 } 00778 00779 gSntpRunning = true; 00780 00781 // Just call this to start SNTP client: 00782 sntp_init(); 00783 00784 // // Enable RTC ISR 00785 // gSntpTicker.attach(_sntp_isr, 1); 00786 // We do it from first NTP response 00787 } 00788 00789 // Use instead of system time() 00790 // Returns local time 00791 time_t SNTPTime(void) { 00792 time_t seconds = time(NULL); 00793 if (gSntpRtcUtc) { 00794 seconds += gSntpTimezone + gSntpDST; // Convert to local time 00795 } 00796 return seconds; 00797 } 00798 00799 // Use instead of system set_time() 00800 // seconds - local time 00801 void SNTPSetTime(time_t seconds) { 00802 if (gSntpRtcUtc) { 00803 seconds -= gSntpTimezone + gSntpDST; // Convert from local time 00804 } 00805 set_time(seconds); 00806 } 00807 00808 // Use instead of system time() 00809 // Returns UTC time 00810 time_t SNTPTimeUTC(void) { 00811 time_t seconds = time(NULL); 00812 if (!gSntpRtcUtc) { 00813 seconds -= gSntpTimezone + gSntpDST; // Convert from local time 00814 } 00815 return seconds; 00816 } 00817 00818 // Use instead of system set_time() 00819 // seconds - UTC time 00820 void SNTPSetTimeUTC(time_t seconds) { 00821 if (!gSntpRtcUtc) { 00822 seconds += gSntpTimezone + gSntpDST; // Convert to local time 00823 } 00824 set_time(seconds); 00825 } 00826 00827 //END
Generated on Wed Jul 13 2022 07:13:44 by
1.7.2