#include <mem.h>
#include <osapi.h>
-#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;
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;
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");
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;
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
// 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) {
}
}
-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));
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();