Example of HTTPServer with additional features: * SNTPClient, DST rules * Link status indication * Local or SDCard-based WebServer * RPC-able class * Static and Dynamic HTML 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 Tue Jul 12 2022 17:12:14 by 1.7.2