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

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SNTPClient.cpp Source File

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