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

Files at this revision

API Documentation at this revision

Comitter:
iva2k
Date:
Fri Jan 08 00:28:14 2010 +0000
Parent:
0:886e4b3119ad
Child:
2:360fda42fefd
Commit message:

Changed in this revision

lwip/Core/contrib/apps/sntp/sntp.c Show annotated file Show diff for this revision Revisions of this file
lwip/Core/lwipopts.h Show annotated file Show diff for this revision Revisions of this file
lwip/SNTPClient/DstZones.h Show annotated file Show diff for this revision Revisions of this file
lwip/SNTPClient/SNTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
lwip/SNTPClient/SNTPClient.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/lwip/Core/contrib/apps/sntp/sntp.c	Sun Jan 03 07:00:43 2010 +0000
+++ b/lwip/Core/contrib/apps/sntp/sntp.c	Fri Jan 08 00:28:14 2010 +0000
@@ -599,6 +599,7 @@
     sntp_initialize_request(sntpmsg);
     /* send request */
     udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
+pbuf_free(p);    // [iva2k] fixing memory leak
     /* set up receive timeout: try next server or retry on timeout */
     sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
 #if SNTP_CHECK_RESPONSE >= 1
@@ -629,7 +630,9 @@
   } else {
     /* DNS resolving failed -> try another server */
     LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
-    sntp_try_next_server(NULL);
+//    sntp_try_next_server(NULL);
+// [iva2k] changing to delayed - having no DNS may mean that there is something wrong. don't hang in here.
+    sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
   }
 }
 
--- a/lwip/Core/lwipopts.h	Sun Jan 03 07:00:43 2010 +0000
+++ b/lwip/Core/lwipopts.h	Fri Jan 08 00:28:14 2010 +0000
@@ -96,7 +96,8 @@
 
 // Do not change these:
 #define SNTP_SOCKET                 0
-#define SNTP_DEBUG                  LWIP_DBG_OFF
+#define SNTP_DEBUG                  LWIP_DBG_ON
+#define PBUF_DEBUG                  LWIP_DBG_ON
 #define SNTP_PORT                   123
 #define SNTP_SERVER_DNS             1   // Override. Use URLs. Do not use IP addresses!
 #define SNTP_SUPPORT_MULTIPLE_SERVERS 1 // Override. Allow multiple servers.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/SNTPClient/DstZones.h	Fri Jan 08 00:28:14 2010 +0000
@@ -0,0 +1,37 @@
+// DstZone.h
+// Written by iva2k
+//
+// Data-driven implementation of DST zones.
+// This file is included into many places.
+// _() macro is defined for each specific use.
+
+// zone is DST symbol
+// pFnc is NULL or custom function of pFncDstCalc type
+// gmt 0..1
+// dst - shift in seconds (typically 3600)
+// hr 0..23
+// wk 0=last, 1=first, 2=second, etc.
+// wday 0=Sunday
+// mon 0..11
+
+// _( z, fnc, gmt, dst, hr1,wk1,wday1,mon1, hr2,wk2,wday2,mon2 )
+
+// zone,       pFnc, gmt,dst, 1:hr,wk,wday,mon, 2:hr,wk,wday,mon
+//------------------------------------------------------------------------------
+_( DST_NONE,   NULL, 0,    0,    0, 0, 0, 0,    0, 0, 0,  0 )
+
+    // USA: 3600; 2, Second SUN, March; 2, First SUN, November
+_( DST_USA,    NULL, 0, 3600,    2, 2, 0, 2,    2, 1, 0, 10 )
+
+    // USA: 3600; 2, First SUN, April; 2, Last SUN, October
+_( DST_USA_06, NULL, 0, 3600,    2, 1, 0, 3,    2, 0, 0,  9 )
+
+    // EU: 3600; 1GMT, Last SUN, March; 1GMT, Last SUN, October
+_( DST_EU_GMT, NULL, 1, 3600,    1, 0, 0, 2,    1, 0, 0,  9 )
+
+    // Just for test
+_( DST_TEST,   NULL, 0, 3600,   18, 1, 2, 0,   12, 1, 4,  0 )
+
+
+#undef _
+//END
\ No newline at end of file
--- a/lwip/SNTPClient/SNTPClient.cpp	Sun Jan 03 07:00:43 2010 +0000
+++ b/lwip/SNTPClient/SNTPClient.cpp	Fri Jan 08 00:28:14 2010 +0000
@@ -1,57 +1,456 @@
 /*
  * SNTPClient.cpp
- * SNTP (Simple NTP) client 
+ * SNTP (Simple NTP) client
  * Written by iva2k
  *
- * Wrapper around LWIP/sntp for MBED.
+ * Wrapper around LWIP/sntp for MBED, with DST rules.
  * This implementation relies on:
- *  1. LWIP (http://www.sics.se/~adam/lwip/) adopted for MBED 
+ *  1. LWIP (http://www.sics.se/~adam/lwip/) adopted for MBED
  *     http://mbed.org/projects/cookbook/svn/EMAC/lwip/trunk
- *  2. LWIP's contributed SNTP client (sntp.c and sntp.h) from 
+ *  2. LWIP's contributed SNTP client (sntp.c and sntp.h) from
  *     http://cvs.savannah.gnu.org/viewvc/contrib/apps/sntp/?root=lwip
  *     (used version 1.8)
- * 
+ *
  * Changes needed in LWIP/sntp:
- *  -  pointer typecast (line 594)
- *  -  #include "sntp.h" moved after lwip/opt.h et.al.
- *   
+ *  -  pointer typecast (line 594) - fixes mbed compiler problem
+ *  -  #include "sntp.h" moved after lwip/opt.h et.al - for re-defines.
+ *  -  pbuf_free(p) in sntp_send_request() (line 602) - fixes memory leak BUG.
+ *  -  changing sntp_dns_found() to delayed sntp_try_next_server() - to unblock if network is disconnected.
+ *
  * Changes in MBED's LWIP:
  *  -  modified lwipopts.h (this file is automatically included into sntp.c)
- *  
+ *
  * Requirements:
  *  + direct RTC update from receiving the NTP time packet
  *  + optionally support more than one NTP server
  *  + the IP address of the NTP server(s) stored in a local file
  *  + timeout error recovery should the NTP server(s) not be available.  No RTC update should the NTP access fail
  *  + allow for a UTC offset, also stored in the local file
- *  - DST correction
+ *  + DST correction
  *  + use DNS for NTP servers IP address resolution
  *  + periodic updates at specified time intervals
- *   
+ *
  * TODO:
- *  - DST correction (how?) Idea:
- *    have 2 timestamps - DST ON / DST OFF (year is masked off).
- *    Once timestamp is crossed (upon NTP update), user function is called, which in turn 
- *    can change UTC offset (timezone).
+ *  . DST correction
  *  - record metrics (count of updates, failed tries?, correction more than epsilon, etc.?)
  */
- 
+
 #include "mbed.h"
 #include "lwip/opt.h"
 #include "SNTPClient.h"
 
 #define SNTP_EPSILON        10                // time difference noticed as big update (in seconds).
 
+//#define SNTP_DST_TESTS      // Define this to run DST algorithms tests
+
+typedef struct DST_ZONE_DESCR {
+    tDST_ZONE zone;
+    const char *name;
+    int gmt;
+    int dst;
+    int hr1;
+    int wk1;
+    int wday1;
+    int mon1;
+    int hr2;
+    int wk2;
+    int wday2;
+    int mon2;
+    pFncDstCalc fnc;
+} tDST_ZONE_DESR;
+
+static tDST_ZONE_DESR gSntpDstZones[] = {
+#define _(z, fnc, gmt, dst, hr1,wk1,wday1,mon1, hr2,wk2,wday2,mon2) \
+  { z, #z, gmt, dst, hr1,wk1,wday1,mon1, hr2,wk2,wday2,mon2, fnc },
+#include "DstZones.h"
+};
+#define DST_ZONE_DESCR_CNT    (sizeof(gSntpDstZones)/sizeof(gSntpDstZones[0]))
+
+const char *SNTPDstZoneName(tDST_ZONE zone) {
+    if (zone >= DST_ZONE_DESCR_CNT)
+        return "";
+    return gSntpDstZones[zone].name;
+}
+
 #ifdef __cplusplus
 extern "C" {
 #endif
+tDST_ZONE    gSntpDstZone = DST_NONE;       // DST zone - rule selector
 unsigned int gSntpRecvTimeout_ms = 3000;    // 3 sec; SNTP_RECV_TIMEOUT
 unsigned int gSntpUpdateDelay_ms = 3600000; // 1 hour; SNTP_UPDATE_DELAY
 signed int   gSntpTimezone =  0*3600;       // seconds from UTC to local time
 signed int   gSntpDST      =  0*3600;       // seconds from UTC to local time
 bool         gSntpRtcUtc   = false;         // true to keep RTC in UTC, false to keep in local time
-unsigned int gSntpUpdates  = 0;
-unsigned int gSntpUpdatesBig = 0;
+unsigned int gSntpUpdates  = 0;             // Track number of all clock NTP updates
+unsigned int gSntpUpdatesBig = 0;           // Track number of significant clock NTP updates
+bool         gSntpRunning = false;          // true if SNTP service is running
+Timeout      gSntpDelay;                    // For async calls.
+Ticker       gSntpTicker;                   // There is no RTC interrupt on MBED (yet). Use (more wastefull) timer.
+time_t       gSntpRtcTCR = 0;               // Timer Capture Register equivalent (in RTC time - either local or UTC depending on gSntpRtcUtc)
+signed int   gSntpRtcTCRDST = 0;            // New DST value for when RTC crosses gSntpRtcTCR
+
+static void SNTPSetDSTEx(unsigned int dst, bool adjust_clock) {
+    if (adjust_clock && !gSntpRtcUtc && gSntpDST != dst) {
+        time_t seconds = time(NULL);
+        seconds -= gSntpDST;                // Convert from old local time
+        seconds += dst;                     // Convert to   new local time
+        set_time(seconds);
+        if (gSntpRtcTCR) {
+            // Adjust our alarm clock
+            gSntpRtcTCR -= gSntpDST;
+            gSntpRtcTCR += dst;
+        }
+    }
+    gSntpDST = dst;
+}
+
+#if 0
+// Custom DST zone function example
+// USA (since 2007)
+// Calculate start or stop DST point for given year based on rules
+// tz    - Timezone
+// year  - current year. DST changes well away from year end, so does not matter what timezone.
+// start - DST_START for start, DST_STOP for stop
+// Returns DST point (in local time). DST_STOP time is in DST
+static tDstPoint _sntp_dst_calc_custom(int tz, int year, tDST_START start) {
+    tDstPoint ret;
+    struct tm t;
+
+    // USA: 3600; 2, Second SUN, March; 2, First SUN, November
+    int dst = 3600;     // DST shift (seconds)
+    int hour;           // Hour of the change
+    int N;              // Week in the month
+    int wday;           // Day of the week
+    int month;
+    if (start == DST_START) {
+        hour  = 2;      // (0-23) Hour of the change
+        N     = 2-1;    // 2nd in the month
+        wday  = 0;      // Sunday
+        month = 2;      // March
+    } else {  // DST_STOP
+        hour  = 2;      // (0-23) Hour of the change
+        N     = 1-1;    // 1st in the month
+        wday  = 0;      // Sunday
+        month = 10;     // November
+    }
+
+    t.tm_sec  = 0;              // 0-59
+    t.tm_min  = 0;              // 0-59
+    t.tm_hour = hour;           // 0-23
+    t.tm_year = year - 1900;    // 
+    t.tm_mon  = month;          // 0-11
+    t.tm_mday = 1+N*7;          // 1-31, first possible date for Nth given weekday
+    mktime(&t);                 // Calculate tm_wday
+    t.tm_mday += (wday-t.tm_wday+14)%7;   // Shift to wday
+    ret.t = mktime(&t);
+    ret.dst = (start == DST_START) ? dst : 0;
+    ret.dstshift = dst;
+    return ret;
+}
+#endif
+
+// Calculate start or stop DST point for given year based on rules for the DST zone
+// tz    - Timezone
+// zone  - DST zone
+// year  - current year. DST changes well away from year end, so does not matter timezone.
+// start - DST_START for start, DST_STOP for stop
+// Returns DST point (in standard local time). DST_STOP time is in NOT IN DST
+static tDstPoint _sntp_dst_calc(int tz, tDST_ZONE zone, int year, tDST_START start) {
+    tDstPoint ret;
+
+    ret.t = 0;
+    ret.dst = 0;
+    ret.dstshift = 0;
+    if (zone == DST_NONE || zone >= DST_ZONE_DESCR_CNT)
+        return ret;
+
+    tDST_ZONE_DESR *descr = &gSntpDstZones[zone];
+    if (descr->fnc) {
+        // Use custom function
+        ret = descr->fnc(tz, year, start);
+    } else {
+        // Use rules
+        struct tm t;
+        t.tm_sec  = 0;              // 0-59
+        t.tm_min  = 0;              // 0-59
+        t.tm_hour = (start == DST_START) ? descr->hr1  : descr->hr2;
+        t.tm_year = year - 1900;    // 
+        t.tm_mon  = (start == DST_START) ? descr->mon1 : descr->mon2;          // 0-11
+        int wk    =((start == DST_START) ? descr->wk1  : descr->wk2) -1;
+        int wday  = (start == DST_START) ? descr->wday1: descr->wday2;
+        if (wk < 0) {
+            // For "Last in the month" - we go to next month, then move back by one week
+            t.tm_mon  += 1;         // 0-11
+            if (t.tm_mon > 11) {
+                t.tm_mon  -= 12;
+                t.tm_year += 1;
+            }
+            t.tm_mday = 1+0*7;      // 1-31, first possible date for Nth given weekday
+        } else {
+            t.tm_mday = 1+wk*7;      // 1-31, first possible date for Nth given weekday
+        }
+        mktime(&t);                 // Calculate tm_wday
+        t.tm_mday += (wday-t.tm_wday+14)%7;   // Shift to wday
+        ret.t = mktime(&t);
+        if (wk < 0) {
+            ret.t -= 7 * 24 * 3600;
+        }
+        if (descr->gmt) {
+            ret.t += tz;
+        }
+        ret.dst = (start == DST_START) ? descr->dst : 0;
+        ret.dstshift = descr->dst;
+    }
+    if (start == DST_STOP) {
+        ret.t -= ret.dstshift;
+        // this correction is due to the fact that rules are given in local time with DST,
+        // but calculations are made in standard local time (without DST adjustment)
+    }
+    return ret;
+}
+
+// Calculate desired DST point relative to now based on rules for the DST zone
+// tz    - timezone
+// zone  - DST zone
+// now   - current time (standard local time = excluding DST).
+// index - 0 for immediate next, +1 for second next, -1 for immediate previous
+// Returns DST point (in standard local time)
+static tDstPoint _sntp_dst_point(int tz, tDST_ZONE zone, time_t now, int index) {
+    tDstPoint ret;
+    int year;
+    tDST_START type;
+    struct tm *pt = localtime(&now);
+
+    ret.t = 0;
+    ret.dst = 0;
+    ret.dstshift = 0;
+    if (zone == DST_NONE || zone >= DST_ZONE_DESCR_CNT)
+        return ret;
+    
+// Algorithm
+// 1. Determine where now is in respect to current year DST points (find for index=0) 
+// 2. Use index to shift year and type 
+// 3. return the point
+// This algorithm relies on DST start date being before DST stop date in the year.
+
+    year = pt->tm_year + 1900;
+    type = DST_START;
+    ret = _sntp_dst_calc(tz, zone, year, type);
+    if (now > ret.t) {
+        type = DST_STOP;
+        ret = _sntp_dst_calc(tz, zone, year, type);
+        if (now > ret.t) {
+            // It is next year's start point
+            type = DST_START;
+            year += 1;
+        }
+    }
+    // Now year and type are right for index=0
+
+    // Calculate where index points to - shift year and type    
+    int norm = (index > 0) ? (int)(index/2) : (int)((index-1)/2);
+    year  += norm;
+    index -= norm*2;    // Now index is (0,1)
+    if (index) {
+        // Flip the type
+        type = (type == DST_START) ? DST_STOP : DST_START;
+    }
+
+    ret = _sntp_dst_calc(tz, zone, year, type);
+    return ret;
+
+//    struct tm t;
+//    t.tm_sec  = 0;     // 0-59
+//    t.tm_min  = 0;     // 0-59
+//    t.tm_hour = 0;     // 0-23
+//    t.tm_mday = 0;     // 1-31
+//    t.tm_mon  = 0;     // 0-11
+//    t.tm_year = 2005-1900;     // year since 1900
+//    t.tm_wday = 0;     // 0-6 Day of week (Sunday = 0)
+//    t.tm_yday = 0;     // Day of year (0 - 365)
+//    t.tm_isdst = -1;   // Nonzero = Daylight saving time
+}
+
+#if defined(LWIP_DEBUG)
+// Print DST dates report
+static void _sntp_dst_dates(int tz, tDST_ZONE zone, int year) {
+    char _buffer[64];
+    tDstPoint r;
+    printf("\r\nDST DATES for zone %d/%s:\r\n", (int)zone, SNTPDstZoneName(zone));
+
+    r = _sntp_dst_calc(tz, zone, year, DST_START);
+    strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+    printf("    %d DST BEGINS (+%04d sec) %s\r\n", year, r.dst, _buffer);
+
+    r = _sntp_dst_calc(tz, zone, year, DST_STOP);
+    strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+    printf("    %d DST ENDS   (+%04d sec) %s\r\n", year, r.dst, _buffer);
+}
+#endif
+
+#ifdef SNTP_DST_TESTS
+static void test_sntp_dst_calc(int tz, tDST_ZONE zone) {
+    char _buffer[64];
+    tDstPoint r;
+    int year;
+
+    printf("\r\nTEST: _sntp_dst_calc(tz=%d, zone=%d/%s)\r\n", tz, (int)zone, SNTPDstZoneName(zone));
+
+    for (year = 1999; year < 2016; year++) {
+        r = _sntp_dst_calc(tz, zone, year, DST_START);
+        strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+        printf("    (0,%d,start) %s %d\r\n", year, _buffer, r.dst);
+  
+        r = _sntp_dst_calc(tz, zone, year, DST_STOP);
+        strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+        printf("    (0,%d,stop ) %s %d\r\n", year, _buffer, r.dst);
+    }
+}
+
+static void test_sntp_dst_point(int index) {
+    char _buffer[64];
+    struct tm t;
+    tDstPoint r;
+    int tz=gSntpTimezone;
+    tDST_ZONE zone=gSntpDstZone;
+    int year = 2009, day, month;
+
+    int norm = (index > 0) ? (int)(index/2) : (int)((index-1)/2);
+    printf("\r\nTEST: _sntp_dst_point(%d) norm=%d\r\n", index, norm);
+
+    t.tm_sec  = 0;              // 0-59
+    t.tm_min  = 0;              // 0-59
+    t.tm_hour = 9;              // 0-23
+    t.tm_year = year-1900;
+
+    day = 1;
+    for (month = 0; month < 2; month++) {
+      t.tm_mon = month;
+      t.tm_mday = day;
+      t.tm_year = year-1900;
+      r = _sntp_dst_point(tz, zone, mktime(&t), index);
+      strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+      printf("    (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst);
+    }
+
+    for (day = 1; day < 32; day++) {
+      t.tm_mon = month;
+      t.tm_mday = day;
+      t.tm_year = year-1900;
+      r = _sntp_dst_point(tz, zone, mktime(&t), index);
+      strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+      printf("    (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst);
+    }
+
+    day = 30;
+    for (; month < 10; month++) {
+      t.tm_mon = month;
+      t.tm_mday = day;
+      t.tm_year = year-1900;
+      r = _sntp_dst_point(tz, zone, mktime(&t), index);
+      strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+      printf("    (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst);
+    }
+
+    for (day = 1; day < 32; day++) {
+      t.tm_mon = month;
+      t.tm_mday = day;
+      t.tm_year = year-1900;
+      r = _sntp_dst_point(tz, zone, mktime(&t), index);
+      strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&r.t));
+      printf("    (0,%02d/%02d/%d,%d) %s %d\r\n", month+1, day, year, index, _buffer, r.dst);
+    }
+
+}
+#endif  // SNTP_DST_TESTS
+
+// Set current DST
+static void _sntp_dst_now(void) {
+    time_t now = time(NULL);
+    // Convert to standart local time (no DST)
+    now = gSntpRtcUtc ? (now + gSntpTimezone) : (now - gSntpDST);
+
+    // Check DST setting for now
+    tDstPoint dst = _sntp_dst_point(gSntpTimezone, gSntpDstZone, now, -1);
+
+#ifdef LWIP_DEBUG
+    char _buffer[64];
+    strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&dst.t));
+    LWIP_DEBUGF(SNTP_DEBUG_STATE, (
+        "DEBUG: _sntp_dst_now(%d) based on last DST change on (%d) %s to (+%04d sec)\r\n",
+         now, dst.t, _buffer, dst.dst));
+#endif
+
+    // Change RTC
+    SNTPSetDSTEx(dst.dst, true);
+}
+
+// Plan for next DST change
+static void _sntp_dst_schedule(void) {
+    time_t now = time(NULL);
+    // Convert to standart local time (no DST)
+    now = gSntpRtcUtc ? (now + gSntpTimezone) : (now - gSntpDST);
+
+    // Check next DST change point
+    tDstPoint dst = _sntp_dst_point(gSntpTimezone, gSntpDstZone, now, 0);
+
+    if (dst.t) {
+
+#ifdef LWIP_DEBUG
+        char _buffer[64];
+        strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&dst.t));
+#endif
+
+        // Set our alarm clock
+        // Convert from standard local to UTC time or local time
+        dst.t = gSntpRtcUtc ? (dst.t - gSntpTimezone) : (dst.t + gSntpDST);
+
+#ifdef LWIP_DEBUG
+        if (time(NULL) > dst.t) {
+            LWIP_DEBUGF(SNTP_DEBUG_STATE, (
+            "DEBUG: _sntp_dst_schedule() ASSERTION FAILED !(%d<%d) trying to schedule for the past: %s (+%04d sec)\r\n",
+             time(NULL), dst.t, _buffer, dst.dst));
+        } else {
+            LWIP_DEBUGF(SNTP_DEBUG_STATE, (
+            "DEBUG: _sntp_dst_schedule() scheduled in %d seconds, set for %s (+%04d sec)\r\n",
+             dst.t-time(NULL), _buffer, dst.dst));
+        }
+#endif
+    }
+    gSntpRtcTCR = dst.t;
+    gSntpRtcTCRDST = dst.dst;
+}
+
+// RTC ISR - called upon each RTC tick
+static void _sntp_isr(void) {
+    time_t seconds = time(NULL);
+    if (gSntpRtcTCR && seconds > gSntpRtcTCR ) {
+        // DST change has arrived
+        gSntpRtcTCR = 0;
+        // Disable ISR and avoid extra calcs in SNTPSetDSTEx()
+
+//if (gSntpRtcTCRDST != gSntpDST) {
+        // Change to/from DST
+        SNTPSetDSTEx(gSntpRtcTCRDST, true);
+        gSntpRtcTCRDST = 0;
+
+        // Schedule callback to plan for next DST change (take it out of ISR context)
+        gSntpDelay.attach(_sntp_dst_schedule, 1.0);
+
+#ifdef LWIP_DEBUG
+        char _buffer[64];
+        if (gSntpRtcUtc) {
+            seconds += gSntpTimezone + gSntpDST;     // Convert to local time
+        }
+        strftime(_buffer, sizeof(_buffer), "%A %m/%d/%Y %H:%M:%S", localtime(&seconds));
+        LWIP_DEBUGF(SNTP_DEBUG_STATE, (
+        "DEBUG: _sntp_isr() changed DST. datetime=%s\r\n", _buffer));
+#endif
+
+//}
+
+    }
+}
 
 // NTP Callback - timestamp from NTP server arrives here
 void SntpClientSet(time_t seconds) {
@@ -60,13 +459,30 @@
         seconds += gSntpTimezone + gSntpDST;     // Convert to local time
     }
     set_time(seconds);
-    
+
+    // Special tasks for the very first NTP updates
+    if (!gSntpUpdates) {
+
+#if defined(LWIP_DEBUG)
+        // Report DST dates for the zone
+        struct tm *pt = localtime(&seconds);
+        _sntp_dst_dates(gSntpTimezone, gSntpDstZone, pt->tm_year+1900);
+#endif
+
+        // DST scheduler
+        _sntp_dst_now();
+        _sntp_dst_schedule();
+
+        // Enable RTC ISR
+        gSntpTicker.attach(_sntp_isr, 1.0);
+    }
+
     if (gSntpUpdates && abs((int)(old_seconds - seconds)) > SNTP_EPSILON) {
         printf("SNTP settime() corrected big difference: (%d) seconds, more than %d. Big updates count=%d.\r\n",
             old_seconds-seconds, SNTP_EPSILON, gSntpUpdatesBig);
         gSntpUpdatesBig ++;
     }
-    
+
     gSntpUpdates++;
 
     if (1) {
@@ -87,8 +503,6 @@
 extern "C" {
 #endif
 
-//#include "lwip/opt.h"
-
 #include "lwip/def.h"
 #include "lwip/pbuf.h"
 #include "lwip/sys.h"
@@ -116,8 +530,10 @@
     Timeout *t = NULL;
     if (func == &sntp_request) {
         t = &_sntp_timer1;
+        LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_timeout(), func=sntp_request\r\n"));
     } else if (func == &sntp_try_next_server) {
         t = &_sntp_timer2;
+        LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_timeout(), func=sntp_try_next_server\r\n"));
     }
     if (t) {
         t->detach();
@@ -130,10 +546,10 @@
     Timeout *t = NULL;
     if (func == &sntp_request) {
         t = &_sntp_timer1;
-//        printf("DEBUG: IN sntp_sys_untimeout(), func=sntp_request\r\n");
+        LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_untimeout(), func=sntp_request\r\n"));
     } else if (func == &sntp_try_next_server) {
         t = &_sntp_timer2;
-//        printf("DEBUG: IN sntp_sys_untimeout(), func=sntp_try_next_server\r\n");
+        LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_untimeout(), func=sntp_try_next_server\r\n"));
     }
     if (t) {
         t->detach();
@@ -144,6 +560,20 @@
 #error "I don't know how to compile LWIP/SNTP with NO_SYS=0"
 #endif // NO_SYS
 
+// Can be called when already running
+void SNTPSetDstZone(tDST_ZONE zone) {
+    if (zone >= DST_ZONE_DESCR_CNT)
+        return;   // ERR_INVALID_ARG
+
+    gSntpDstZone = zone;
+
+    if (gSntpRunning) {
+        // DST scheduler
+        _sntp_dst_now();
+        _sntp_dst_schedule();
+    }
+}
+
 void SNTPSetRecvTimeout(unsigned int val_ms) { gSntpRecvTimeout_ms = val_ms; }
 void SNTPSetUpdateDelay(unsigned int val_ms) { gSntpUpdateDelay_ms = val_ms; }
 void SNTPSetTimezone(float hours_from_utc, bool adjust_clock) {
@@ -152,24 +582,23 @@
         seconds -= gSntpTimezone;           // Convert from old local time
         seconds += hours_from_utc * 3600;   // Convert to   new local time
         set_time(seconds);
+        if (gSntpRtcTCR) {
+            // Adjust our alarm clock
+            gSntpRtcTCR -= gSntpTimezone;
+            gSntpRtcTCR += hours_from_utc * 3600;
+        }
     }
     gSntpTimezone = hours_from_utc * 3600;
 }
 void SNTPSetDST(float hours_from_utc, bool adjust_clock) {
-    if (adjust_clock && !gSntpRtcUtc) {
-        time_t seconds = time(NULL);
-        seconds -= gSntpDST;                // Convert from old local time
-        seconds += hours_from_utc * 3600;   // Convert to   new local time
-        set_time(seconds);
-    }
-    gSntpDST = hours_from_utc * 3600;
+    SNTPSetDSTEx(hours_from_utc * 3600, adjust_clock);
 }
 
 static int sntp_num_servers_alloc = 0;
 static void _SNTPClrAddresses(void) {
     if (!sntp_num_servers_alloc) sntp_num_servers_alloc = sntp_num_servers;
     // Here we save the original size of the sntp_server_addresses[] array.
-    
+
     if (sntp_addresses_free) {
         for (int i=0; i<sntp_num_servers; i++) {
             sntp_addresses_free(sntp_server_addresses[i]);
@@ -226,7 +655,7 @@
         out[len] = '\0';
         len--;
     }
-    return out;        
+    return out;
 }
 
 #ifndef CRLF
@@ -241,6 +670,8 @@
     fprintf(f, CRLF "[Global]" CRLF);
     fprintf(f, "RtcUtc=%d" CRLF, (int)gSntpRtcUtc);
     fprintf(f, "Timezone=%0.1f" CRLF, gSntpTimezone / 3600.0);
+    fprintf(f, "DstZone=%d" CRLF, gSntpDstZone);
+    fprintf(f, "# %s" CRLF, SNTPDstZoneName(gSntpDstZone));
     fprintf(f, "UpdateDelay=%d" CRLF, gSntpUpdateDelay_ms);
     fprintf(f, "RecvTimeout=%d" CRLF, gSntpRecvTimeout_ms);
     fprintf(f, CRLF "##END" CRLF);
@@ -277,7 +708,7 @@
                 section=SECT_SERVERS;
                 if (!addresses_cleared) {
                     // Clear addresses only once.
-                    _SNTPClrAddresses(); 
+                    _SNTPClrAddresses();
                     addresses_cleared = true;
                 }
             } else if (0 == strncmp(buf1,"[Global]", sizeof("[Global]")-1)) {
@@ -307,8 +738,11 @@
                         gSntpRecvTimeout_ms = strtoul(buf2, &buf2, 10);
                     } else if (0 == strncmp(buf1, "RtcUtc", sizeof("RtcUtc")-1)) {
                         gSntpRtcUtc = (bool)strtol(buf2, &buf2, 10);
+                    } else if (0 == strncmp(buf1, "DstZone", sizeof("DstZone")-1)) {
+                        // FIXME: It would be nice to allow human-readable string here.
+                        gSntpDstZone = (tDST_ZONE)strtol(buf2, &buf2, 10);
                     } else {
-                        fprintf(stderr, "File \"%s\", line %d - unrecognized variable \"%s\" in section [Global]\r\n", filename, line, buf1);                   
+                        fprintf(stderr, "File \"%s\", line %d - unrecognized variable \"%s\" in section [Global]\r\n", filename, line, buf1);
                     }
                 } else {
                     fprintf(stderr, "File \"%s\", line %d - unrecognized statement in section [Global]: %s\r\n", filename, line, buf1);
@@ -326,13 +760,30 @@
 
 // Start the SNTP client
 void SNTPClientInit(void) {
+
+#ifdef SNTP_DST_TESTS
+    // Test our DST algorithms
+    test_sntp_dst_calc(gSntpTimezone, gSntpDstZone);
+    test_sntp_dst_point(-2);
+    test_sntp_dst_point(-1);
+    test_sntp_dst_point(0);
+    test_sntp_dst_point(1);
+    test_sntp_dst_point(2);
+#endif
+
     // SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds
     if (gSntpUpdateDelay_ms < 15000) {
         gSntpUpdateDelay_ms = 15000;
     }
+
+    gSntpRunning = true;
     
     // Just call this to start SNTP client:
     sntp_init();
+
+//    // Enable RTC ISR
+//    gSntpTicker.attach(_sntp_isr, 1);
+// We do it from first NTP response
 }
 
 // Use instead of system time()
--- a/lwip/SNTPClient/SNTPClient.h	Sun Jan 03 07:00:43 2010 +0000
+++ b/lwip/SNTPClient/SNTPClient.h	Fri Jan 08 00:28:14 2010 +0000
@@ -2,6 +2,25 @@
 #define SNTPCLIENT_H
 
 
+typedef enum {DST_START, DST_STOP} tDST_START;
+
+typedef struct DST_POINT {
+    time_t     t;               // Point in time when DST switch happens (in localtime)
+    signed int dst;             // Active DST value (for gSntpDST)
+    signed int dstshift;        // DST shift value (regardless if it is active or not)
+} tDstPoint;
+
+typedef tDstPoint (*pFncDstCalc)(int tz, int year, tDST_START start);
+
+typedef enum {
+#define _(z, fnc, gmt, dst, hr1,wk1,wday1,mon1, hr2,wk2,wday2,mon2) z,
+#include "DstZones.h"
+    DST_LAST    // LAST ITEM IN ENUM
+} tDST_ZONE;
+
+void SNTPSetDstZone(tDST_ZONE zone, bool adjust_clock=false);
+const char *SNTPDstZoneName(tDST_ZONE zone);
+
 void SNTPSetRecvTimeout(unsigned int val_ms);
 void SNTPSetUpdateDelay(unsigned int val_ms);
 void SNTPSetTimezone(float hours_from_utc, bool adjust_clock=false);
--- a/main.cpp	Sun Jan 03 07:00:43 2010 +0000
+++ b/main.cpp	Fri Jan 08 00:28:14 2010 +0000
@@ -183,7 +183,7 @@
 
 
     http.addHandler(new HTTPLinkStatus("/", 
-#if 0
+#if MBED_BOB2
 	  p25, p26,			// MBED-BoB2-mods
 #else
       LED3, LED4,