this will take a image from C328 serial camera and store those images in mbed flash as html and this html page is uploaded into the server at ip:192.168.1.2
Diff: lwip/SNTPClient/SNTPClient.cpp
- Revision:
- 0:e1a0471e5ffb
diff -r 000000000000 -r e1a0471e5ffb lwip/SNTPClient/SNTPClient.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwip/SNTPClient/SNTPClient.cpp Wed Dec 15 15:01:56 2010 +0000 @@ -0,0 +1,827 @@ +/* + * SNTPClient.cpp + * SNTP (Simple NTP) client + * Written by iva2k + * + * Wrapper around LWIP/sntp for MBED, with DST rules. + * This implementation relies on: + * 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 + * http://cvs.savannah.gnu.org/viewvc/contrib/apps/sntp/?root=lwip + * (used version 1.8) + * + * Changes needed in LWIP/sntp: + * - 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 + * + use DNS for NTP servers IP address resolution + * + periodic updates at specified time intervals + * + * TODO: + * . 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_s = 3; // 3 sec; SNTP_RECV_TIMEOUT +unsigned int gSntpUpdateDelay_s = 3600; // 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; // 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) { + time_t old_seconds = time(NULL); + if (!gSntpRtcUtc) { + 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) { + 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)); + printf("SNTP settime() #%d seconds=%d TZ=%0.1f datetime=%s\r\n", + gSntpUpdates, seconds, gSntpTimezone/3600.0, _buffer); + } +} +#ifdef __cplusplus +}; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "string.h" + +#ifdef __cplusplus +}; +#endif + +// Accomodating sntp timeout functions +#if NO_SYS +#include "sntp.h" +extern void sntp_request(void *arg); // this is dirty hack around "static" function. Some linkers may revolt! +extern void sntp_try_next_server(void *arg); +extern char* sntp_server_addresses[]; +extern u8_t sntp_current_server; +extern u8_t sntp_num_servers; +static void (*sntp_addresses_free)(void*) = NULL; +static Timeout _sntp_timer1; +static Timeout _sntp_timer2; +void sntp_sys_timeout(u32_t timeout_s, void (*func)(void *arg), void *arg) { +// all we really need to track is only 2 functions: sntp_request, sntp_try_next_server + 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(); + t->attach((void(*)(void))func, 1.0*timeout_s); + // Another shortcut - we have no arg to pass, so just typecast the func. + } +} + +void sntp_sys_untimeout(void (*func)(void *arg), void *arg) { + Timeout *t = NULL; + if (func == &sntp_request) { + t = &_sntp_timer1; + 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; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("DEBUG: IN sntp_sys_untimeout(), func=sntp_try_next_server\r\n")); + } + if (t) { + t->detach(); + } +} + +#else // NO_SYS +#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_s) { gSntpRecvTimeout_s = val_s; } +void SNTPSetUpdateDelay(unsigned int val_s) { gSntpUpdateDelay_s = val_s; } +void SNTPSetTimezone(float hours_from_utc, bool adjust_clock) { + if (adjust_clock && !gSntpRtcUtc) { + time_t seconds = time(NULL); + 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) { + 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]); + } + } + sntp_current_server = 0; + sntp_num_servers = 0; + sntp_addresses_free = NULL; +} +static int _SNTPAddAddress(const char* server_address) { + if (sntp_num_servers+1 > sntp_num_servers_alloc) + return -1; + sntp_server_addresses[sntp_num_servers] = (char*)malloc(strlen(server_address)+1); + if (sntp_server_addresses[sntp_num_servers] == NULL) return -1; // Out of memory + strcpy(sntp_server_addresses[sntp_num_servers], server_address); + sntp_num_servers++; + return 0; +} + +// Override default servers list. +// For no-copy, pass pointer to free() in p_free. +// Returns: actual number of servers set (limited by allocated buffer size) +// WARNING! There is no interlock to ensure that SNTP service does not read its data while we are updating it. +// This function is intended to be called only before SNTPClientInit(). +int SNTPSetAddresses(const char* server_addresses[], int count, void (*p_free)(void*)) { + // In order to use sntp.c as-is, we hack into its static variables. + // Not all compilers/linkers will support that. + + _SNTPClrAddresses(); + if (count > sntp_num_servers_alloc) + count = sntp_num_servers_alloc; + for (int i=0; i<count; i++) { + if (p_free) { + sntp_server_addresses[i] = (char *)server_addresses[i]; + } else { + _SNTPAddAddress(server_addresses[i]); + } + } + sntp_num_servers = count; + sntp_current_server = 0; + sntp_addresses_free = p_free ? p_free : &free; + return count; +} + +// Trim whitespace/CRLFs from both ends of a given string +char * str_cleanup(char *in) { + char * out = in; + // Trim leading spaces and CR/LF + while (*out == ' ' || *out == '\t' || *out == '\r' || *out == '\n') + out ++; + // Trim trailing spaces and CR/LF + int len = strlen(out)-1; + while (out[len] == ' ' || out[len] == '\t' || out[len] == '\r' || out[len] == '\n') { + out[len] = '\0'; + len--; + } + return out; +} + +#ifndef CRLF +#define CRLF "\r\n" +#endif +void SNTPWriteIniFile(FILE * f) { + fprintf(f, "# SNTP Configuration file" CRLF); + fprintf(f, CRLF "[Servers]" CRLF); + for (int i=0; i<sntp_num_servers; i++) { + fprintf(f, "%s" CRLF, sntp_server_addresses[i]); + } + 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_s); + fprintf(f, "RecvTimeout=%d" CRLF, gSntpRecvTimeout_s); + fprintf(f, CRLF "##END" CRLF); +} + +// Simple ini file parser for SNTP configuration (Case-sensitive!) +// This function is intended to be called only before SNTPClientInit(). +// Returns: 0 if OK, errno otherwise +enum { + SECT_NONE, + SECT_SERVERS, + SECT_GLOBAL, +}; +int SNTPReadIniFile(const char* filename) { + FILE *f; + char buf[512]; + bool addresses_cleared = false; + + f = fopen(filename, "r"); + if (!f) + return -1; // errno not used? + + char *buf1, *buf2; + int section=SECT_NONE; + int line = 0; + while (fgets(buf, sizeof(buf)/sizeof(buf[0]), f)) { + line++; + buf1 = str_cleanup(buf); + if (*buf1 == '#' || *buf1 == '\0') + continue; // Comment line or empty line - skip + if (*buf1 == '[') { + // New section + if (0 == strncmp(buf1,"[Servers]", sizeof("[Servers]")-1)) { + section=SECT_SERVERS; + if (!addresses_cleared) { + // Clear addresses only once. + _SNTPClrAddresses(); + addresses_cleared = true; + } + } else if (0 == strncmp(buf1,"[Global]", sizeof("[Global]")-1)) { + section=SECT_GLOBAL; + } else { + section=SECT_NONE; + fprintf(stderr, "File \"%s\", line %d - section \"%s\" is not understood.\r\n", filename, line, buf1); + } + } else { + // Section values + switch (section) { + case SECT_SERVERS: + if (_SNTPAddAddress(buf1)) { + fprintf(stderr, "File \"%s\", line %d - cannot add server \"%s\" - exceeded allocated slots.\r\n", filename, line, buf1); + } + break; + case SECT_GLOBAL: + buf2 = strchr(buf1, '='); + if (buf2) { + *buf2++ = '\0'; // Now buf1 has variable name, buf2 has value + buf2 = str_cleanup(buf2); + if (0 == strncmp(buf1, "Timezone", sizeof("Timezone")-1)) { + gSntpTimezone = strtod(buf2, &buf2) * 3600; + } else if (0 == strncmp(buf1, "UpdateDelay", sizeof("UpdateDelay")-1)) { + gSntpUpdateDelay_s = strtoul(buf2, &buf2, 10); + } else if (0 == strncmp(buf1, "RecvTimeout", sizeof("RecvTimeout")-1)) { + gSntpRecvTimeout_s = 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); + } + } else { + fprintf(stderr, "File \"%s\", line %d - unrecognized statement in section [Global]: %s\r\n", filename, line, buf1); + } + break; + default: + fprintf(stderr, "File \"%s\", line %d - unrecognized statement / no section: %s\r\n", filename, line, buf1); + } + } + } + fclose(f); + printf("SNTP configuration read from file \"%s\", %d lines.\r\n", filename, line); + return 0; +} + +// 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_s < 15) { + gSntpUpdateDelay_s = 15; + } + + 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() +// Returns local time +time_t SNTPTime(void) { + time_t seconds = time(NULL); + if (gSntpRtcUtc) { + seconds += gSntpTimezone + gSntpDST; // Convert to local time + } + return seconds; +} + +// Use instead of system set_time() +// seconds - local time +void SNTPSetTime(time_t seconds) { + if (gSntpRtcUtc) { + seconds -= gSntpTimezone + gSntpDST; // Convert from local time + } + set_time(seconds); +} + +// Use instead of system time() +// Returns UTC time +time_t SNTPTimeUTC(void) { + time_t seconds = time(NULL); + if (!gSntpRtcUtc) { + seconds -= gSntpTimezone + gSntpDST; // Convert from local time + } + return seconds; +} + +// Use instead of system set_time() +// seconds - UTC time +void SNTPSetTimeUTC(time_t seconds) { + if (!gSntpRtcUtc) { + seconds += gSntpTimezone + gSntpDST; // Convert to local time + } + set_time(seconds); +} + +//END \ No newline at end of file