X-Git-Url: https://the.earth.li/gitweb/?a=blobdiff_plain;f=clock.c;h=a0333fd8faff5f95ec07e5affdb01ca23b618b9d;hb=8ecc02cb462cfd4fd0b7510922e7063b65c5795b;hp=02bc1b56c55780fdcd8eec7aa3129d028bbbb400;hpb=a6b9bc4387fe370b7a3cddac157b1b9137847f30;p=esp8266-clock.git diff --git a/clock.c b/clock.c index 02bc1b5..a0333fd 100644 --- a/clock.c +++ b/clock.c @@ -44,19 +44,18 @@ #include #include -#include "espmissingincludes.h" - #include "clock.h" +#define NTP_SERVER "uk.pool.ntp.org" #define NTP_TIMEOUT_MS 5000 static uint32_t sys_last_ticks; static uint32_t sys_delta; static os_timer_t ntp_timeout; -static struct espconn *pCon = NULL; -uint8 ntp_server[] = {87, 124, 126, 49}; +static ip_addr_t ntp_server_ip; +/* See RFC5905 7.3 */ typedef struct { uint8 options; uint8 stratum; @@ -90,8 +89,55 @@ uint32_t ICACHE_FLASH_ATTR get_time(void) return sys_ticks / 1000000 + sys_delta; } +bool ICACHE_FLASH_ATTR is_leap(uint32_t year) +{ + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); +} + +bool ICACHE_FLASH_ATTR is_dst(struct tm *time) +{ + int lastsun = time->tm_mday - time->tm_wday; + + if (time->tm_mon < 2 || time->tm_mon > 9) + return false; + if (time->tm_mon > 2 && time->tm_mon < 9) + return true; + + /* + * Starts last Sunday in March, ends last Sunday in October, which must + * be at least the 25th of the month. So must be past that in March, or + * before that in October, to be in DST. + */ + if (time->tm_mon == 2) + return (lastsun >= 25); + if (time->tm_mon == 9) + return (lastsun < 25); + + return false; +} + +/* + * Takes time, a Unix time (seconds since 1st Jan 1970) and breaks it down to: + * + * Time: + * tm_sec 0-59 + * tm_min 0-59 + * tm_hour 0-23 + * + * Date: + * tm_year + * tm_mon 0-11 + * tm_mday 1-31 + * + * tm_yday + * tm_wday Sunday = 0, Saturday = 6 + * + */ void ICACHE_FLASH_ATTR breakdown_time(uint32_t time, struct tm *result) { + uint32_t era, doe, yoe, mp; + + /* Do the time component */ result->tm_sec = time % 60; time /= 60; result->tm_min = time % 60; @@ -99,11 +145,47 @@ void ICACHE_FLASH_ATTR breakdown_time(uint32_t time, struct tm *result) result->tm_hour = time % 24; time /= 24; - result->tm_year = time / (365 * 4 + 1) * 4 + 70; - time %= 365 * 4 + 1; + /* Now time is the number of days since 1970-01-01 (a Thursday) */ + result->tm_wday = (time + 4) % 7; + + /* Below from http://howardhinnant.github.io/date_algorithms.html */ + time += 719468; + era = time / 146097; + doe = time - era * 146097; + yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + + result->tm_year = yoe + era * 400; + result->tm_yday = doe - (365 * yoe + yoe / 4 - yoe / 100); + mp = (5 * result->tm_yday + 2) / 153; + result->tm_mday = result->tm_yday - (153 * mp + 2) / 5 + 1; + result->tm_mon = mp + (mp < 10 ? 2 : -10); + if (result->tm_mon <= 2) + result->tm_year++; + + /* result->tm_yday is March 1st indexed at this point; fix up */ + result->tm_yday += 28 + 31; + if (is_leap(result->tm_year)) + result->tm_yday++; + + result->tm_isdst = is_dst(result); + if (result->tm_isdst) + result->tm_hour++; + if (result->tm_hour > 23) { + /* + * We can ignore fixing up the date at the end of month etc. + * because all we're actually displaying is the time. + */ + result->tm_hour = 0; + result->tm_wday++; + result->tm_mday++; + result->tm_yday++; + } } -static void ICACHE_FLASH_ATTR ntp_udp_timeout(void *arg) { +static void ICACHE_FLASH_ATTR ntp_udp_timeout(void *arg) +{ + struct espconn *pCon = (struct espconn *) arg; + os_timer_disarm(&ntp_timeout); os_printf("NTP timeout.\n"); @@ -119,6 +201,7 @@ static void ICACHE_FLASH_ATTR ntp_udp_timeout(void *arg) { static void ICACHE_FLASH_ATTR ntp_udp_recv(void *arg, char *pdata, unsigned short len) { + struct espconn *pCon = (struct espconn *) arg; uint32_t timestamp; ntp_t *ntp; struct tm dt; @@ -131,7 +214,7 @@ static void ICACHE_FLASH_ATTR ntp_udp_recv(void *arg, char *pdata, ntp = (ntp_t *) pdata; timestamp = ntp->trans_time[0] << 24 | ntp->trans_time[1] << 16 | ntp->trans_time[2] << 8 | ntp->trans_time[3]; - // Convert to Unix time ms + // NTP 0 is 1st Jan 1900; convert to Unix time 0 of 1st Jan 1970 timestamp -= 2208988800ULL; // Store the time @@ -139,8 +222,9 @@ static void ICACHE_FLASH_ATTR ntp_udp_recv(void *arg, char *pdata, // Print it out breakdown_time(timestamp, &dt); - os_printf("%04d %02d:%02d:%02d (%u)\r\n", dt.tm_year, dt.tm_hour, - dt.tm_min, dt.tm_sec, timestamp); + os_printf("%04d-%02d-%02d %02d:%02d:%02d (%u)\r\n", + dt.tm_year, dt.tm_mon + 1, dt.tm_mday, + dt.tm_hour, dt.tm_min, dt.tm_sec, timestamp); // clean up connection if (pCon) { @@ -151,20 +235,26 @@ static void ICACHE_FLASH_ATTR ntp_udp_recv(void *arg, char *pdata, } } -void ICACHE_FLASH_ATTR ntp_get_time(void) +void ICACHE_FLASH_ATTR ntp_got_dns(const char *name, ip_addr_t *ip, void *arg) { ntp_t ntp; + struct espconn *pCon = (struct espconn *) arg; + + if (ip == NULL) { + os_printf("NTP DNS request failed.\n"); + os_free(pCon); + return; + } os_printf("Sending NTP request.\n"); // Set up the UDP "connection" - pCon = (struct espconn *) os_zalloc(sizeof(struct espconn)); pCon->type = ESPCONN_UDP; pCon->state = ESPCONN_NONE; pCon->proto.udp = (esp_udp *) os_zalloc(sizeof(esp_udp)); pCon->proto.udp->local_port = espconn_port(); pCon->proto.udp->remote_port = 123; - os_memcpy(pCon->proto.udp->remote_ip, ntp_server, 4); + os_memcpy(pCon->proto.udp->remote_ip, &ip->addr, 4); // Create a really simple NTP request packet os_memset(&ntp, 0, sizeof(ntp_t)); @@ -181,6 +271,15 @@ void ICACHE_FLASH_ATTR ntp_get_time(void) espconn_sent(pCon, (uint8_t *) &ntp, sizeof(ntp_t)); } +void ICACHE_FLASH_ATTR ntp_get_time(void) +{ + struct espconn *pCon = NULL; + + os_printf("Sending DNS request for NTP server.\n"); + pCon = (struct espconn *) os_zalloc(sizeof(struct espconn)); + espconn_gethostbyname(pCon, NTP_SERVER, &ntp_server_ip, ntp_got_dns); +} + void ICACHE_FLASH_ATTR rtc_init(void) { sys_last_ticks = system_get_time();