diff --git a/Makefile b/Makefile index 8a10c22..54f1f6e 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ ESPPORT ?= /dev/ttyUSB0 TARGET = app # which modules (subdirectories) of the project to include in compiling -MODULES = driver user mqtt +MODULES = driver user mqtt ntp #EXTRA_INCDIR = $(BUILD_AREA)/esp-open-sdk/esp-open-lwip/include include EXTRA_INCDIR = include diff --git a/ntp/ntp.c b/ntp/ntp.c new file mode 100644 index 0000000..c504ba7 --- /dev/null +++ b/ntp/ntp.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "ntp.h" +#include "lwip/def.h" +#include "user_config.h" +#include "driver/uart.h" +//#include "utils.h" + +#define OFFSET 2208988800ULL + +static ip_addr_t ntp_server_ip = {0}; +static os_timer_t ntp_timeout; +static struct espconn *pCon, pConDNS; + +static struct timeval t_tv = {0,0}; +static uint64_t t_offset = 0; + +void ICACHE_FLASH_ATTR get_cur_time(struct timeval *tv) +{ + uint64_t t_curr = get_long_systime() - t_offset + t_tv.tv_usec; + tv->tv_sec = t_tv.tv_sec + t_curr/1000000; + tv->tv_usec = t_curr%1000000; +} + + +uint8_t* ICACHE_FLASH_ATTR get_timestr(int16_t timezone) +{ + struct timeval tv; + static uint8_t buf[10]; + + get_cur_time(&tv); + tv.tv_sec += timezone * 3600; + os_sprintf(buf, "%02d:%02d:%02d", (tv.tv_sec/3600)%24, (tv.tv_sec/60)%60, tv.tv_sec%60); + return buf; +} + + +void ICACHE_FLASH_ATTR ntp_to_tv(uint8_t ntp[8], struct timeval *tv) +{ +uint64_t aux = 0; + + tv->tv_sec = ntohl(*(uint32_t*)ntp) - OFFSET; + + aux = ntohl(*(uint32_t*)&ntp[4]); + + // aux is the NTP fraction (0..2^32-1) + aux *= 1000000; // multiply by 1e6 + aux >>= 32; // and divide by 2^32 + tv->tv_usec = (uint32_t)aux; +} + + +LOCAL void ICACHE_FLASH_ATTR ntp_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { +struct espconn *pespconn = (struct espconn *)arg; + + if (ipaddr != NULL) { + os_printf("Got NTP server: %d.%d.%d.%d\r\n", IP2STR(ipaddr)); + // Call the NTP update + ntp_server_ip.addr = ipaddr->addr; + ntp_get_time(); + } +} + + +static void ICACHE_FLASH_ATTR ntp_udp_timeout(void *arg) { + + os_timer_disarm(&ntp_timeout); + os_printf("NTP timout\r\n"); + + // clean up connection + if (pCon) { + espconn_delete(pCon); + os_free(pCon->proto.udp); + os_free(pCon); + pCon = 0; + } +} + + +static void ICACHE_FLASH_ATTR ntp_udp_recv(void *arg, char *pdata, unsigned short len) { +ntp_t *ntp; + +struct timeval tv; +int32_t hh, mm, ss; + + get_cur_time(&tv); + + // get the according sys_time; + t_offset = get_long_systime(); + + os_timer_disarm(&ntp_timeout); + + // extract ntp time + ntp = (ntp_t*)pdata; + + ntp_to_tv(ntp->trans_time, &t_tv); + + os_printf("NTP resync - diff: %d usecs\r\n", t_tv.tv_usec-tv.tv_usec); +/* + ss = t_tv.tv_sec%60; + mm = (t_tv.tv_sec/60)%60; + hh = (t_tv.tv_sec/3600)%24; + os_printf("time: %2d:%02d:%02d\r\n", hh, mm, ss); +*/ + // clean up connection + if (pCon) { + espconn_delete(pCon); + os_free(pCon->proto.udp); + os_free(pCon); + pCon = 0; + } +} + + +void ICACHE_FLASH_ATTR ntp_set_server(uint8_t *ntp_server) { + + ntp_server_ip.addr = 0; + + // invalid arg? + if (ntp_server == NULL) + return; + + // ip or DNS name? + if (UTILS_IsIPV4(ntp_server)) { + // read address + UTILS_StrToIP(ntp_server, &ntp_server_ip); + ntp_get_time(); + } else { + // call DNS and wait for callback + espconn_gethostbyname(&pConDNS, ntp_server, &ntp_server_ip, ntp_dns_found); + } +} + + +void ICACHE_FLASH_ATTR ntp_get_time() { +ntp_t ntp; + + // either ongoing request or invalid ip? + if (pCon != 0 || ntp_server_ip.addr == 0) + return; + + // 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_ip, 4); + + // create a really simple ntp request packet + os_memset(&ntp, 0, sizeof(ntp_t)); + ntp.options = 0b00011011; // leap = 0, version = 3, mode = 3 (client) + + // set timeout timer + os_timer_disarm(&ntp_timeout); + os_timer_setfn(&ntp_timeout, (os_timer_func_t*)ntp_udp_timeout, pCon); + os_timer_arm(&ntp_timeout, NTP_TIMEOUT_MS, 0); + + // send the ntp request + espconn_create(pCon); + espconn_regist_recvcb(pCon, ntp_udp_recv); + espconn_sent(pCon, (uint8*)&ntp, sizeof(ntp_t)); +} diff --git a/ntp/ntp.h b/ntp/ntp.h new file mode 100644 index 0000000..bc89712 --- /dev/null +++ b/ntp/ntp.h @@ -0,0 +1,26 @@ +#ifndef __NTP_H__ +#define __NTP_H__ +#include + +#define NTP_TIMEOUT_MS 5000 + +typedef struct { + uint8 options; + uint8 stratum; + uint8 poll; + uint8 precision; + uint32 root_delay; + uint32 root_disp; + uint32 ref_id; + uint8 ref_time[8]; + uint8 orig_time[8]; + uint8 recv_time[8]; + uint8 trans_time[8]; +} ntp_t; + +void ntp_set_server(uint8_t *ntp_server); +void ntp_get_time(); +void get_cur_time(struct timeval *tv); +uint8_t *get_timestr(int16_t timezone); + +#endif diff --git a/user/config_flash.c b/user/config_flash.c index fd0be60..f9dbe1a 100644 --- a/user/config_flash.c +++ b/user/config_flash.c @@ -42,6 +42,11 @@ uint8_t mac[6]; wifi_get_macaddr(0, mac); os_sprintf(config->mqtt_id,"%s_%2x%2x%2x", MQTT_ID, mac[3], mac[4], mac[5]); #endif +#ifdef NTP + os_sprintf(config->ntp_server,"%s", "none"); + config->ntp_interval = 60000000; + config->ntp_timezone = 0; +#endif } int config_load(sysconfig_p config) diff --git a/user/config_flash.h b/user/config_flash.h index cc917c3..59beff0 100644 --- a/user/config_flash.h +++ b/user/config_flash.h @@ -53,7 +53,11 @@ typedef struct uint8_t mqtt_password[32]; // Password for broker login uint8_t mqtt_id[32]; // MQTT clientId #endif - +#ifdef NTP + uint8_t ntp_server[32]; // IP or hostname of the MQTT broker, "none" if empty + uint32_t ntp_interval; // Sync interval in usec + int16_t ntp_timezone; // Timezone (hour offset to GMT) +#endif } sysconfig_t, *sysconfig_p; int config_load(sysconfig_p config); diff --git a/user/user_config.h b/user/user_config.h index e691c8d..65e15cb 100644 --- a/user/user_config.h +++ b/user/user_config.h @@ -26,6 +26,11 @@ typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_SEND_DATA, SIG_UART0, SI #define MQTT_ID "ESPBroker" +// +// Define this if you want to have NTP support. +// +#define NTP 1 + // // Size of the console buffers // diff --git a/user/user_main.c b/user/user_main.c index 0e99dcc..ee98614 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -18,6 +18,11 @@ #include "mqtt_topiclist.h" #include "mqtt_retainedlist.h" +#ifdef NTP +#include "ntp.h" +uint64_t t_ntp_resync = 0; +#endif + /* System Task, for signals refer to user_config.h */ #define user_procTaskPrio 0 #define user_procTaskQueueLen 1 @@ -242,6 +247,10 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) os_sprintf(response, "|scan"); ringbuf_memcpy_into(console_tx_buffer, response, os_strlen(response)); #endif +#ifdef NTP + os_sprintf(response, "|time|set ntp_server |set ntp_interval |set \r\n"); + ringbuf_memcpy_into(console_tx_buffer, response, os_strlen(response)); +#endif #ifdef MQTT_CLIENT os_sprintf(response, "|set [mqtt_host|mqtt_port|mqtt_user|mqtt_password|mqtt_id] \r\n"); ringbuf_memcpy_into(console_tx_buffer, response, os_strlen(response)); @@ -287,6 +296,13 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) config.locked?"***":(char*)config.mqtt_password, config.mqtt_id); ringbuf_memcpy_into(console_tx_buffer, response, os_strlen(response)); } +#endif +#ifdef NTP + if (os_strcmp(config.ntp_server, "none") != 0) { + os_sprintf(response, "NTP server: %s (interval: %d s, tz: %d)\r\n", + config.ntp_server, config.ntp_interval/1000000, config.ntp_timezone); + ringbuf_memcpy_into(console_tx_buffer, response, os_strlen(response)); + } #endif os_sprintf(response, "Clock speed: %d\r\n", config.clock_speed); ringbuf_memcpy_into(console_tx_buffer, response, os_strlen(response)); @@ -361,6 +377,13 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) os_sprintf(response, "Scanning...\r\n"); goto command_handled; } +#endif +#ifdef NTP + if (strcmp(tokens[0], "time") == 0) + { + os_sprintf(response, "%s\r\n", get_timestr(config.ntp_timezone)); + goto command_handled; + } #endif if (strcmp(tokens[0], "reset") == 0) { @@ -389,7 +412,7 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) goto command_handled; } if (strcmp(tokens[1], "local") == 0) { - MQTT_local_publish(tokens[2], tokens[3], os_strlen(tokens[2]), 0, 0); + MQTT_local_publish(tokens[2], tokens[3], os_strlen(tokens[3]), 0, 0); } #ifdef MQTT_CLIENT else if (strcmp(tokens[1], "remote") == 0 && mqtt_connected) { @@ -625,7 +648,30 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) IP2STR(&config.my_gw)); goto command_handled; } +#ifdef NTP + if (strcmp(tokens[1], "ntp_server") == 0) + { + os_strncpy(config.ntp_server, tokens[2], 32); + config.ntp_server[31] = 0; + ntp_set_server(config.ntp_server); + os_sprintf(response, "NTP server set to %s\r\n", config.ntp_server); + goto command_handled; + } + if (strcmp(tokens[1], "ntp_interval") == 0) + { + config.ntp_interval = atoi(tokens[2])*1000000; + os_sprintf(response, "NTP interval set to %d s\r\n", atoi(tokens[2])); + goto command_handled; + } + + if (strcmp(tokens[1], "ntp_timezone") == 0) + { + config.ntp_timezone = atoi(tokens[2]); + os_sprintf(response, "NTP timezone set to %d h\r\n", config.ntp_timezone); + goto command_handled; + } +#endif #ifdef MQTT_CLIENT if (strcmp(tokens[1], "mqtt_host") == 0) { @@ -749,6 +795,12 @@ uint32_t t_diff; } t_new = get_long_systime(); +#ifdef NTP + if (t_new - t_ntp_resync > config.ntp_interval) { + ntp_get_time(); + t_ntp_resync = t_new; + } +#endif os_timer_arm(&ptimer, toggle?1000:100, 0); } @@ -1006,6 +1058,11 @@ struct ip_info info; } #endif /* MQTT_CLIENT */ +#ifdef NTP + if (os_strcmp(config.ntp_server, "none") != 0) + ntp_set_server(config.ntp_server); +#endif + remote_console_disconnect = 0; // Now start the STA-Mode