From 16c831fffaf95027aec0e84c03874304d84ae1c9 Mon Sep 17 00:00:00 2001 From: doragasu Date: Wed, 9 Mar 2016 17:18:51 +0100 Subject: [PATCH 1/5] Added sntp support and example --- examples/sntp/Makefile | 6 + examples/sntp/sntp_example.c | 108 +++++ extras/sntp/component.mk | 8 + extras/sntp/sntp.c | 748 +++++++++++++++++++++++++++++++++++ extras/sntp/sntp.h | 52 +++ extras/sntp/sntp_fun.c | 105 +++++ 6 files changed, 1027 insertions(+) create mode 100644 examples/sntp/Makefile create mode 100644 examples/sntp/sntp_example.c create mode 100644 extras/sntp/component.mk create mode 100644 extras/sntp/sntp.c create mode 100644 extras/sntp/sntp.h create mode 100644 extras/sntp/sntp_fun.c diff --git a/examples/sntp/Makefile b/examples/sntp/Makefile new file mode 100644 index 0000000..5d55b10 --- /dev/null +++ b/examples/sntp/Makefile @@ -0,0 +1,6 @@ +# Makefile for the sntp_example program + +PROGRAM=sntp_example +EXTRA_COMPONENTS = extras/sntp + +include ../../common.mk diff --git a/examples/sntp/sntp_example.c b/examples/sntp/sntp_example.c new file mode 100644 index 0000000..6083f96 --- /dev/null +++ b/examples/sntp/sntp_example.c @@ -0,0 +1,108 @@ +/* + * Test code for SNTP on esp-open-rtos. + * + * Jesus Alonso (doragasu) + */ +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +// Add extras/sntp component to makefile for this include to work +#include + +#define SNTP_SERVERS "0.pool.ntp.org", "1.pool.ntp.org", \ + "2.pool.ntp.org", "3.pool.ntp.org" + +#define vTaskDelayMs(ms) vTaskDelay((ms)/portTICK_RATE_MS) +#define UNUSED_ARG(x) (void)x + +// Use GPIO pin 2 +const int gpio_frc2 = 2; +// 1 Hz blink frequency +const int freq_frc2 = 1; + +void SntpTsk(void *pvParameters) +{ + char *servers[] = {SNTP_SERVERS}; + UNUSED_ARG(pvParameters); + + // Wait until we have joined AP and are assigned an IP + while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) { + vTaskDelayMs(100); + } + + // Start SNTP + printf("Starting SNTP... "); + sntp_set_update_delay(1*60000); + sntp_initialize(1, 0); + sntp_set_servers(servers, sizeof(servers) / sizeof(char*)); + printf("DONE!\n"); + while(1) { + vTaskDelayMs(5000); + time_t ts = sntp_get_rtc_time(NULL); + printf("TIME: %s", ctime(&ts)); + } +} + +void frc2_interrupt_handler(void) +{ + /* FRC2 needs the match register updated on each timer interrupt */ + timer_set_frequency(FRC2, freq_frc2); + gpio_toggle(gpio_frc2); +} + +// Configure FRC2 to blink the LED. I'm messing with FRC2 just to test if +// it does affect FreeRTOS. +void Frc2Config(void) { + /* configure GPIO */ + gpio_enable(gpio_frc2, GPIO_OUTPUT); + gpio_write(gpio_frc2, 1); + + /* stop timer and mask its interrupt as a precaution */ + timer_set_interrupts(FRC2, false); + timer_set_run(FRC2, false); + + /* set up ISR */ + _xt_isr_attach(INUM_TIMER_FRC2, frc2_interrupt_handler); + + /* configure timer frequency */ + timer_set_frequency(FRC2, freq_frc2); + + /* unmask interrupt and start timer */ + timer_set_interrupts(FRC2, true); + timer_set_run(FRC2, true); +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct sdk_station_config config = { + .ssid = WIFI_SSID, + .password = WIFI_PASS, + }; + + /* required to call wifi_set_opmode before station_set_config */ + sdk_wifi_set_opmode(STATION_MODE); + sdk_wifi_station_set_config(&config); + + // Mess with FRC2 to test if it interferes with FreeRTOS + Frc2Config(); + + xTaskCreate(SntpTsk, (signed char *)"SNTP", 1024, NULL, 1, NULL); +} + diff --git a/extras/sntp/component.mk b/extras/sntp/component.mk new file mode 100644 index 0000000..155537a --- /dev/null +++ b/extras/sntp/component.mk @@ -0,0 +1,8 @@ +# Component makefile for extras/sntp + +INC_DIRS += $(sntp_ROOT) + +# args for passing into compile rule generation +sntp_SRC_DIR = $(sntp_ROOT) + +$(eval $(call component_compile_rules,sntp)) diff --git a/extras/sntp/sntp.c b/extras/sntp/sntp.c new file mode 100644 index 0000000..5654075 --- /dev/null +++ b/extras/sntp/sntp.c @@ -0,0 +1,748 @@ +/** + * @file + * SNTP client module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt (lwIP raw API part) + * + * Modified by Jesus Alonso (doragasu) to allow setting servers dynamically. + */ + +#include "lwip/opt.h" + +#include "sntp.h" + +#include "lwip/timers.h" +#include "lwip/udp.h" +#include "lwip/dns.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" + +#include +#include + +#if LWIP_UDP + +/** This is simple "SNTP" client for socket or raw API. + * It is a minimal implementation of SNTPv4 as specified in RFC 4330. + * + * For a list of some public NTP servers, see this link : + * http://support.ntp.org/bin/view/Servers/NTPPoolServers + * + * @todo: + * - set/change servers at runtime + * - complete SNTP_CHECK_RESPONSE checks 3 and 4 + * - support broadcast/multicast mode? + */ + +/** Decide whether to build SNTP for socket or raw API + * The socket API SNTP client is a very minimal implementation that does not + * fully confor to the SNTPv4 RFC, especially regarding server load and error + * procesing. */ +#ifndef SNTP_SOCKET +#define SNTP_SOCKET 0 +#endif + +#if SNTP_SOCKET +#include "lwip/sockets.h" +#endif + +/** + * SNTP_DEBUG: Enable debugging for SNTP. + */ +#ifndef SNTP_DEBUG +#define SNTP_DEBUG LWIP_DBG_OFF +#endif + +/** SNTP server port */ +#ifndef SNTP_PORT +#define SNTP_PORT 123 +#endif + +/** Set this to 1 to allow SNTP_SERVER_ADDRESS to be a DNS name */ +#ifndef SNTP_SERVER_DNS +#define SNTP_SERVER_DNS 0 +#endif + +/** Set to the number of servers supported (at least 1) */ +#ifndef SNTP_NUM_SERVERS_SUPPORTED +#define SNTP_NUM_SERVERS_SUPPORTED 1 +#endif + +/** SNTP server address: + * - as IPv4 address in "u32_t" format + * - as a DNS name if SNTP_SERVER_DNS is set to 1 + * May contain multiple server names (e.g. "pool.ntp.org","second.time.server") + */ +#ifndef SNTP_SERVER_ADDRESS +#if SNTP_SERVER_DNS +#define SNTP_SERVER_ADDRESS "pool.ntp.org" +#else +#define SNTP_SERVER_ADDRESS "213.161.194.93" /* pool.ntp.org */ +#endif +#endif + +/** Sanity check: + * Define this to + * - 0 to turn off sanity checks (default; smaller code) + * - >= 1 to check address and port of the response packet to ensure the + * response comes from the server we sent the request to. + * - >= 2 to check returned Originate Timestamp against Transmit Timestamp + * sent to the server (to ensure response to older request). + * - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp + * fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast). + * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each + * greater than or equal to 0 and less than infinity, where infinity is + * currently a cozy number like one second. This check avoids using a + * server whose synchronization source has expired for a very long time. + */ +#ifndef SNTP_CHECK_RESPONSE +#define SNTP_CHECK_RESPONSE 0 +#endif + +/** According to the RFC, this shall be a random delay + * between 1 and 5 minutes (in milliseconds) to prevent load peaks. + * This can be defined to a random generation function, + * which must return the delay in milliseconds as u32_t. + * Turned off by default. + */ +#ifndef SNTP_STARTUP_DELAY +#define SNTP_STARTUP_DELAY 0 +#endif + +/** SNTP receive timeout - in milliseconds + * Also used as retry timeout - this shouldn't be too low. + * Default is 3 seconds. + */ +#ifndef SNTP_RECV_TIMEOUT +#define SNTP_RECV_TIMEOUT 3000 +#endif + +/** SNTP update delay - in milliseconds + * Default is 1 hour. + */ +#ifndef SNTP_UPDATE_DELAY +#define SNTP_UPDATE_DELAY 3600000 +#endif +#if (SNTP_UPDATE_DELAY < 15000) && !SNTP_SUPPRESS_DELAY_CHECK +#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds!" +#endif + +/** SNTP macro to change system time and/or the update the RTC clock */ +#ifndef SNTP_SET_SYSTEM_TIME +#define SNTP_SET_SYSTEM_TIME(sec) ((void)sec) +#endif + +/** SNTP macro to change system time including microseconds */ +#ifdef SNTP_SET_SYSTEM_TIME_US +#define SNTP_CALC_TIME_US 1 +#define SNTP_RECEIVE_TIME_SIZE 2 +#else +#define SNTP_SET_SYSTEM_TIME_US(sec, us) +#define SNTP_CALC_TIME_US 0 +#define SNTP_RECEIVE_TIME_SIZE 1 +#endif + +/** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2 + * to send in request and compare in response. + */ +#ifndef SNTP_GET_SYSTEM_TIME +#define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0) +#endif + +/** Default retry timeout (in milliseconds) if the response + * received is invalid. + * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached. + */ +#ifndef SNTP_RETRY_TIMEOUT +#define SNTP_RETRY_TIMEOUT SNTP_RECV_TIMEOUT +#endif + +/** Maximum retry timeout (in milliseconds). */ +#ifndef SNTP_RETRY_TIMEOUT_MAX +#define SNTP_RETRY_TIMEOUT_MAX (SNTP_RETRY_TIMEOUT * 10) +#endif + +/** Increase retry timeout with every retry sent + * Default is on to conform to RFC. + */ +#ifndef SNTP_RETRY_TIMEOUT_EXP +#define SNTP_RETRY_TIMEOUT_EXP 1 +#endif + +/* the various debug levels for this file */ +#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) +#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) +#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +#define SNTP_ERR_KOD 1 + +/* SNTP protocol defines */ +#define SNTP_MSG_LEN 48 + +#define SNTP_OFFSET_LI_VN_MODE 0 +#define SNTP_LI_MASK 0xC0 +#define SNTP_LI_NO_WARNING 0x00 +#define SNTP_LI_LAST_MINUTE_61_SEC 0x01 +#define SNTP_LI_LAST_MINUTE_59_SEC 0x02 +#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */ + +#define SNTP_VERSION_MASK 0x38 +#define SNTP_VERSION (4/* NTP Version 4*/<<3) + +#define SNTP_MODE_MASK 0x07 +#define SNTP_MODE_CLIENT 0x03 +#define SNTP_MODE_SERVER 0x04 +#define SNTP_MODE_BROADCAST 0x05 + +#define SNTP_OFFSET_STRATUM 1 +#define SNTP_STRATUM_KOD 0x00 + +#define SNTP_OFFSET_ORIGINATE_TIME 24 +#define SNTP_OFFSET_RECEIVE_TIME 32 +#define SNTP_OFFSET_TRANSMIT_TIME 40 + +/* number of seconds between 1900 and 1970 */ +#define DIFF_SEC_1900_1970 (2208988800UL) + +/** + * SNTP packet format (without optional fields) + * Timestamps are coded as 64 bits: + * - 32 bits seconds since Jan 01, 1970, 00:00 + * - 32 bits seconds fraction (0-padded) + * For future use, if the MSB in the seconds part is set, seconds are based + * on Feb 07, 2036, 06:28:16. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct sntp_msg { + PACK_STRUCT_FIELD(u8_t li_vn_mode); + PACK_STRUCT_FIELD(u8_t stratum); + PACK_STRUCT_FIELD(u8_t poll); + PACK_STRUCT_FIELD(u8_t precision); + PACK_STRUCT_FIELD(u32_t root_delay); + PACK_STRUCT_FIELD(u32_t root_dispersion); + PACK_STRUCT_FIELD(u32_t reference_identifier); + PACK_STRUCT_FIELD(u32_t reference_timestamp[2]); + PACK_STRUCT_FIELD(u32_t originate_timestamp[2]); + PACK_STRUCT_FIELD(u32_t receive_timestamp[2]); + PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* function prototypes */ +static void sntp_request(void *arg); + +/** The UDP pcb used by the SNTP client */ +static struct udp_pcb* sntp_pcb; +/** Addresses of servers */ +static char* sntp_server_addresses[SNTP_NUM_SERVERS_SUPPORTED]; +/** The currently used server (initialized to 0) */ +static u8_t sntp_num_servers; +#if (SNTP_NUM_SERVERS_SUPPORTED > 1) +static u8_t sntp_current_server; +#else +#define sntp_current_server 0 +#endif /* SNTP_NUM_SERVERS_SUPPORTED */ + +static u32_t sntp_update_delay = SNTP_UPDATE_DELAY; + +#if SNTP_RETRY_TIMEOUT_EXP +#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT +/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */ +static u32_t sntp_retry_timeout; +#else /* SNTP_RETRY_TIMEOUT_EXP */ +#define SNTP_RESET_RETRY_TIMEOUT() +#define sntp_retry_timeout SNTP_RETRY_TIMEOUT +#endif /* SNTP_RETRY_TIMEOUT_EXP */ + +#if SNTP_CHECK_RESPONSE >= 1 +/** Saves the last server address to compare with response */ +static ip_addr_t sntp_last_server_address; +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + +#if SNTP_CHECK_RESPONSE >= 2 +/** Saves the last timestamp sent (which is sent back by the server) + * to compare against in response */ +static u32_t sntp_last_timestamp_sent[2]; +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + +/** + * SNTP processing of received timestamp + */ +static void +sntp_process(u32_t *receive_timestamp) +{ + /* convert SNTP time (1900-based) to unix GMT time (1970-based) + * @todo: if MSB is 1, SNTP time is 2036-based! + */ + time_t t = (ntohl(receive_timestamp[0]) - DIFF_SEC_1900_1970); + +#if SNTP_CALC_TIME_US + u32_t us = ntohl(receive_timestamp[1]) / 4295; + SNTP_SET_SYSTEM_TIME_US(t, us); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&t), us)); + +#else /* SNTP_CALC_TIME_US */ + + /* change system time and/or the update the RTC clock */ + SNTP_SET_SYSTEM_TIME(t); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&t))); +#endif /* SNTP_CALC_TIME_US */ +} + +/** + * Initialize request struct to be sent to server. + */ +static void +sntp_initialize_request(struct sntp_msg *req) +{ + memset(req, 0, SNTP_MSG_LEN); + req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT; + +#if SNTP_CHECK_RESPONSE >= 2 + { + u32_t sntp_time_sec, sntp_time_us; + /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */ + SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us); + sntp_last_timestamp_sent[0] = htonl(sntp_time_sec + DIFF_SEC_1900_1970); + req->transmit_timestamp[0] = sntp_last_timestamp_sent[0]; + /* we send/save us instead of fraction to be faster... */ + sntp_last_timestamp_sent[1] = htonl(sntp_time_us); + req->transmit_timestamp[1] = sntp_last_timestamp_sent[1]; + } +#endif /* SNTP_CHECK_RESPONSE >= 2 */ +} + +#if SNTP_SOCKET + +/** + * Send an SNTP request via sockets. + * This is a very minimal implementation that does not fully conform + * to the SNTPv4 RFC, especially regarding server load and error procesing. + */ +static void +sntp_request(void *arg) +{ + int sock; + struct sockaddr_in local; + struct sockaddr_in to; + int tolen; + int size; + int timeout; + struct sntp_msg sntpmsg; + ip_addr_t sntp_server_address; + + LWIP_UNUSED_ARG(arg); + + /* if we got a valid SNTP server address... */ + if (ipaddr_aton(SNTP_SERVER_ADDRESS, &sntp_server_address)) { + /* create new socket */ + sock = lwip_socket(AF_INET, SOCK_DGRAM, 0); + if (sock >= 0) { + /* prepare local address */ + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = PP_HTONS(INADDR_ANY); + local.sin_addr.s_addr = PP_HTONL(INADDR_ANY); + + /* bind to local address */ + if (lwip_bind(sock, (struct sockaddr *)&local, sizeof(local)) == 0) { + /* set recv timeout */ + timeout = SNTP_RECV_TIMEOUT; + lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); + + /* prepare SNTP request */ + sntp_initialize_request(&sntpmsg); + + /* prepare SNTP server address */ + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_port = PP_HTONS(SNTP_PORT); + inet_addr_from_ipaddr(&to.sin_addr, &sntp_server_address); + + /* send SNTP request to server */ + if (lwip_sendto(sock, &sntpmsg, SNTP_MSG_LEN, 0, (struct sockaddr *)&to, sizeof(to)) >= 0) { + /* receive SNTP server response */ + tolen = sizeof(to); + size = lwip_recvfrom(sock, &sntpmsg, SNTP_MSG_LEN, 0, (struct sockaddr *)&to, (socklen_t *)&tolen); + /* if the response size is good */ + if (size == SNTP_MSG_LEN) { + /* if this is a SNTP response... */ + if (((sntpmsg.li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_SERVER) || + ((sntpmsg.li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_BROADCAST)) { + /* do time processing */ + sntp_process(sntpmsg.receive_timestamp); + } else { + LWIP_DEBUGF( SNTP_DEBUG_WARN, ("sntp_request: not response frame code\n")); + } + } + } else { + LWIP_DEBUGF( SNTP_DEBUG_WARN, ("sntp_request: not sendto==%i\n", errno)); + } + } + /* close the socket */ + closesocket(sock); + } + } +} + +/** + * SNTP thread + */ +static void +sntp_thread(void *arg) +{ + LWIP_UNUSED_ARG(arg); + while(1) { + sntp_request(NULL); + sys_msleep(sntp_update_delay); + } +} + +/** + * Initialize this module when using sockets + */ +void +sntp_init(void) +{ + sys_thread_new("sntp_thread", sntp_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); +} + +#else /* SNTP_SOCKET */ + +/** + * Retry: send a new request (and increase retry timeout). + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_retry(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n", + sntp_retry_timeout)); + + /* set up a timer to send a retry and increase the retry delay */ + sys_timeout(sntp_retry_timeout, sntp_request, NULL); + +#if SNTP_RETRY_TIMEOUT_EXP + { + u32_t new_retry_timeout; + /* increase the timeout for next retry */ + new_retry_timeout = sntp_retry_timeout << 1; + /* limit to maximum timeout and prevent overflow */ + if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) && + (new_retry_timeout > sntp_retry_timeout)) { + sntp_retry_timeout = new_retry_timeout; + } + } +#endif /* SNTP_RETRY_TIMEOUT_EXP */ +} + +#if (SNTP_NUM_SERVERS_SUPPORTED > 1) +/** + * If Kiss-of-Death is received (or another packet parsing error), + * try the next server or retry the current server and increase the retry + * timeout if only one server is available. + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_try_next_server(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + if (sntp_num_servers > 1) { + /* new server: reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + sntp_current_server++; + if (sntp_current_server >= sntp_num_servers) { + sntp_current_server = 0; + } + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n", + (u16_t)sntp_current_server)); + /* instantly send a request to the next server */ + sntp_request(NULL); + } else { + sntp_retry(NULL); + } +} +#else /* SNTP_NUM_SERVERS_SUPPORTED > 1 */ +/* Always retry on error if only one server is supported */ +#define sntp_try_next_server sntp_retry +#endif /* SNTP_NUM_SERVERS_SUPPORTED > 1 */ + +/** UDP recv callback for the sntp pcb */ +static void +sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + u8_t mode; + u8_t stratum; + u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE]; + err_t err; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + + /* packet received: stop retry timeout */ + sys_untimeout(sntp_try_next_server, NULL); + sys_untimeout(sntp_request, NULL); + + err = ERR_ARG; +#if SNTP_CHECK_RESPONSE >= 1 + /* check server address and port */ + if (ip_addr_cmp(addr, &sntp_last_server_address) && + (port == SNTP_PORT)) +#else /* SNTP_CHECK_RESPONSE >= 1 */ + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + { + /* process the response */ + if (p->tot_len == SNTP_MSG_LEN) { + pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE); + mode &= SNTP_MODE_MASK; + /* if this is a SNTP response... */ + if ((mode == SNTP_MODE_SERVER) || + (mode == SNTP_MODE_BROADCAST)) { + pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM); + if (stratum == SNTP_STRATUM_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + err = SNTP_ERR_KOD; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n")); + } else { +#if SNTP_CHECK_RESPONSE >= 2 + /* check originate_timetamp against sntp_last_timestamp_sent */ + u32_t originate_timestamp[2]; + pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME); + if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) || + (originate_timestamp[1] != sntp_last_timestamp_sent[1])) + { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n")); + } else +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */ + { + /* correct answer */ + err = ERR_OK; + pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_RECEIVE_TIME); + } + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode)); + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len)); + } + } + pbuf_free(p); + if (err == ERR_OK) { + /* Correct response, reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + + sntp_process(receive_timestamp); + + /* Set up timeout for next request */ + sys_timeout(sntp_update_delay, sntp_request, NULL); + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n", + sntp_update_delay)); + } else if (err == SNTP_ERR_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + sntp_try_next_server(NULL); + } else { + /* another error, try the same server again */ + sntp_retry(NULL); + } +} + +/** Actually send an sntp request to a server. + * + * @param server_addr resolved IP address of the SNTP server + */ +static void +sntp_send_request(ip_addr_t *server_addr) +{ + struct pbuf* p; + p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM); + if (p != NULL) { + struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n")); + /* initialize request message */ + sntp_initialize_request(sntpmsg); + /* send request */ + udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT); + /* 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 + /* save server address to verify it in sntp_recv */ + ip_addr_set(&sntp_last_server_address, server_addr); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + } else { + LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n", + (u32_t)SNTP_RETRY_TIMEOUT)); + /* out of memory: set up a timer to send a retry */ + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL); + } +} + +#if SNTP_SERVER_DNS +/** + * DNS found callback when using DNS names as server address. + */ +static void +sntp_dns_found(const char* hostname, ip_addr_t *ipaddr, void *arg) +{ + LWIP_UNUSED_ARG(hostname); + LWIP_UNUSED_ARG(arg); + + if (ipaddr != NULL) { + /* Address resolved, send request */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n")); + sntp_send_request(ipaddr); + } 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); + } +} +#endif /* SNTP_SERVER_DNS */ + +/** + * Send out an sntp request via raw API. + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_request(void *arg) +{ + ip_addr_t sntp_server_address; + err_t err; + + LWIP_UNUSED_ARG(arg); + + /* initialize SNTP server address */ +#if SNTP_SERVER_DNS + err = dns_gethostbyname(sntp_server_addresses[sntp_current_server], &sntp_server_address, + sntp_dns_found, NULL); + if (err == ERR_INPROGRESS) { + /* DNS request sent, wait for sntp_dns_found being called */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n")); + return; + } +#else /* SNTP_SERVER_DNS */ + err = ipaddr_aton(sntp_server_addresses[sntp_current_server], &sntp_server_address) + ? ERR_OK : ERR_ARG; + +#endif /* SNTP_SERVER_DNS */ + + if (err == ERR_OK) { + sntp_send_request(&sntp_server_address); + } else { + /* address conversion failed, try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n")); + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL); + } +} + +/** + * Initialize this module when using raw API. + * Send out request instantly or after SNTP_STARTUP_DELAY. + */ +void +sntp_init(void) +{ + char *def_addr[] = {SNTP_SERVER_ADDRESS}; + + sntp_num_servers = 0; + sntp_set_servers(def_addr, sizeof(def_addr) / sizeof(char*)); + + SNTP_RESET_RETRY_TIMEOUT(); + sntp_pcb = udp_new(); + LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL); + if (sntp_pcb != NULL) { + udp_recv(sntp_pcb, sntp_recv, NULL); +#if SNTP_STARTUP_DELAY + sys_timeout((u32_t)SNTP_STARTUP_DELAY, sntp_request, NULL); +#else + sntp_request(NULL); +#endif + } +} + +#endif /* SNTP_SOCKET */ + +/* Additions to allow dynamically settings servers and update delay */ + +/** + * Set the NTP servers + */ +int sntp_set_servers(char *server_url[], int num_servers) +{ + int i; + + /* Check number of servers requested */ + if (SNTP_NUM_SERVERS_SUPPORTED < num_servers) return -1; + + /* Free previously allocated buffers */ + for (i = sntp_num_servers - 1; i >= 0; i--) { + free(sntp_server_addresses[i]); + sntp_server_addresses[i] = NULL; + } + + /* Allocate memory and copy servers */ + for (i = 0; i < num_servers; i++) { + sntp_server_addresses[i] = malloc(strlen(server_url[i])); + if (sntp_server_addresses[i]) { + strcpy(sntp_server_addresses[i], server_url[i]); + } else { + sntp_num_servers = i; + return -2; + } + } + sntp_num_servers = num_servers; + return 0; +} + +void sntp_set_update_delay(uint32_t ms) +{ + sntp_update_delay = ms > 15000?ms:15000; +} + +#endif /* LWIP_UDP */ diff --git a/extras/sntp/sntp.h b/extras/sntp/sntp.h new file mode 100644 index 0000000..9b5043a --- /dev/null +++ b/extras/sntp/sntp.h @@ -0,0 +1,52 @@ +/************************************************************************//** + * RTC time keeping and synchronization using SNTP for esp-open-rtos. + * Uses SNTP lwIP contribution. + * + * 2016, Jesus Alonso (doragasu) + ****************************************************************************/ + +#ifndef _SNTP_H_ +#define _SNTP_H_ + +#include +#include + +/// Update SNTP RTC timer +void sntp_update_rtc(time_t t, uint32_t us); + +/// Function used to update the date/time, with microseconds resolution. +#define SNTP_SET_SYSTEM_TIME_US(sec, us) sntp_update_rtc(sec, us) + +/// For the lwIP implementation of SNTP to allow using names for NTP servers. +#define SNTP_SERVER_DNS 1 + +/// Number of supported NTP servers +#define SNTP_NUM_SERVERS_SUPPORTED 4 + +/// Initialize the module, and start requesting SNTP updates. This function +/// must be called only once. +void sntp_initialize(int time_zone, int day_light); + +/// Sets time zone. Allowed values are in the range [-11, 13]. +/// NOTE: Settings do not take effect until SNTP time is updated. It is +void sntp_set_timezone(int time_zone); + +/// Sets daylight. +/// NOTE: Settings do not take effect until SNTP time is updated. +void sntp_set_daylight(int day_light); + +/// Returns the time read from RTC counter, in seconds from Epoch. If +/// us is not null, it will be filled with the microseconds. +time_t sntp_get_rtc_time(int32_t *us); + +/// Set SNTP servers. Up to SNTP_NUM_SERVERS_SUPPORTED can be set. +/// Returns 0 if OK, less than 0 if error. +/// NOTE: This function must NOT be called before sntp_initialize(). +int sntp_set_servers(char *server_url[], int num_servers); + +/// Sets the update delay in ms. If requested value is less than 15s, +/// a 15s update interval will be set. +void sntp_set_update_delay(uint32_t ms); + +#endif /* _SNTP_H_ */ + diff --git a/extras/sntp/sntp_fun.c b/extras/sntp/sntp_fun.c new file mode 100644 index 0000000..83f5e6b --- /dev/null +++ b/extras/sntp/sntp_fun.c @@ -0,0 +1,105 @@ +/* + * Auxiliar functions to handle date/time along with lwIP sntp implementation. + * + * Jesus Alonso (doragasu) + */ + +#include +#include +#include +#include +#include "sntp.h" + +#define TIMER_COUNT RTC.COUNTER + +// daylight settings +// Base calculated with value obtained from NTP server (64 bits) +#define sntp_base (*((uint64_t*)RTC.SCRATCH)) +// Timer value when base was obtained +#define tim_ref (RTC.SCRATCH[2]) + +// Setters and getters for CAL, TZ and DST. +#define RTC_CAL_SET(val) (RTC.SCRATCH[3] |= (val) & 0x0000FFFF) +#define RTC_DST_SET(val) (RTC.SCRATCH[3] |= ((val)<<16) & 0x00010000) +#define RTC_TZ_SET(val) (RTC.SCRATCH[3] |= ((val)<<24) & 0xFF000000) + +#define RTC_CAL_GET() (RTC.SCRATCH[3] & 0x0000FFFF) +#define RTC_DST_GET() ((RTC.SCRATCH[3] & 0x00010000)>>16) +#define RTC_TZ_GET() ((((int)RTC.SCRATCH[3]) & ((int)0xFF000000))>>24) + +// Implemented in sntp.c +void sntp_init(void); + +// Sets time zone. Allowed values are in the range [-11, 13]. +// NOTE: Settings do not take effect until SNTP time is updated. It is +void sntp_set_timezone(int time_zone) { + //tz = time_zone; + RTC_TZ_SET(time_zone); +} + +// Sets daylight. +// NOTE: Settings do not take effect until SNTP time is updated. +void sntp_set_daylight(int day_light) { + //dst = day_light; + RTC_DST_SET(day_light); +} + + +void sntp_initialize(int time_zone, int day_light) { + sntp_base = 0; + RTC_TZ_SET(time_zone); + RTC_DST_SET(day_light); + // To avoid div by 0 exceptions if requesting time before SNTP config + RTC_CAL_SET(1); + tim_ref = TIMER_COUNT; + sntp_init(); +} + +// Check if a timer wrap has occurred. Compensate sntp_base reference +// if affirmative. +// TODO: think about multitasking and race conditions +static inline void sntp_check_timer_wrap(uint32_t current_value) { + if (current_value < tim_ref) { + // Timer wrap has occurred, compensate by subtracting 2^32 to ref. + sntp_base -= 1LLU<<32; + // DEBUG + printf("\nTIMER WRAPPED!\n"); + } +} + +// Return secs. If us is not a null pointer, fill it with usecs +time_t sntp_get_rtc_time(int32_t *us) { + time_t secs; + uint32_t tim; + uint64_t base; + + tim = TIMER_COUNT; + // Check for timer wrap + sntp_check_timer_wrap(tim); + base = sntp_base + tim - tim_ref; + secs = base * RTC_CAL_GET() / (1000000U<<12); + if (us) { + *us = base * RTC_CAL_GET() % (1000000U<<12); + } + return secs; +} + +/// Update RTC timer. Called by SNTP module each time it receives an update. +void sntp_update_rtc(time_t t, uint32_t us) { + // Apply daylight and timezone correction + t += (RTC_TZ_GET() + RTC_DST_GET()) * 3600; + // DEBUG: Compute and print drift + int64_t sntp_current = sntp_base + TIMER_COUNT - tim_ref; + int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); + printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), RTC_CAL_GET()); + + tim_ref = TIMER_COUNT; + RTC_CAL_SET(sdk_system_rtc_clock_cali_proc()); + + sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); + + // DEBUG: Print obtained secs and check calculated secs are the same + time_t deb = sntp_base * RTC_CAL_GET() / (1000000U<<12); + printf("\nT: %lu, %lu, %s\n", t, deb, ctime(&deb)); +} + From 0482aebf7ddfca440db04f5c9093ed7a27418bc9 Mon Sep 17 00:00:00 2001 From: doragasu Date: Fri, 11 Mar 2016 13:11:15 +0100 Subject: [PATCH 2/5] Added quick and dirty _gettimeofday_r() test implementation. --- examples/sntp/sntp_example.c | 8 ++++++++ extras/sntp/sntp_fun.c | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/examples/sntp/sntp_example.c b/examples/sntp/sntp_example.c index 6083f96..8702718 100644 --- a/examples/sntp/sntp_example.c +++ b/examples/sntp/sntp_example.c @@ -50,6 +50,14 @@ void SntpTsk(void *pvParameters) sntp_initialize(1, 0); sntp_set_servers(servers, sizeof(servers) / sizeof(char*)); printf("DONE!\n"); + + struct timespec ts; + clock_getres(CLOCK_REALTIME, &ts); + printf("Time resolution: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); + + clock_gettime(CLOCK_REALTIME, &t); + printf("Time: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); + while(1) { vTaskDelayMs(5000); time_t ts = sntp_get_rtc_time(NULL); diff --git a/extras/sntp/sntp_fun.c b/extras/sntp/sntp_fun.c index 83f5e6b..f4c02ae 100644 --- a/extras/sntp/sntp_fun.c +++ b/extras/sntp/sntp_fun.c @@ -4,6 +4,10 @@ * Jesus Alonso (doragasu) */ +#include +#include +#include +#include #include #include #include @@ -84,6 +88,16 @@ time_t sntp_get_rtc_time(int32_t *us) { return secs; } +int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) { + (void)r; + (void)tzp; + + printf("DEB; gettimeofday called"); + tp->tv_sec = sntp_get_rtc_time((int32_t*)&tp->tv_usec); + return 0; +} + + /// Update RTC timer. Called by SNTP module each time it receives an update. void sntp_update_rtc(time_t t, uint32_t us) { // Apply daylight and timezone correction From f14025b1c7bf24508afb71e1ac48a7f405d615dc Mon Sep 17 00:00:00 2001 From: doragasu Date: Sun, 13 Mar 2016 17:04:03 +0100 Subject: [PATCH 3/5] Removed non working clock_* calls from example. Using vars for tz and dst. --- examples/sntp/sntp_example.c | 12 +++--- extras/sntp/sntp.h | 4 ++ extras/sntp/sntp_fun.c | 83 ++++++++++++++++++++++-------------- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/examples/sntp/sntp_example.c b/examples/sntp/sntp_example.c index 8702718..78601dd 100644 --- a/examples/sntp/sntp_example.c +++ b/examples/sntp/sntp_example.c @@ -51,12 +51,12 @@ void SntpTsk(void *pvParameters) sntp_set_servers(servers, sizeof(servers) / sizeof(char*)); printf("DONE!\n"); - struct timespec ts; - clock_getres(CLOCK_REALTIME, &ts); - printf("Time resolution: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); - - clock_gettime(CLOCK_REALTIME, &t); - printf("Time: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); +// struct timespec ts; +// clock_getres(CLOCK_REALTIME, &ts); +// printf("Time resolution: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); +// +// clock_gettime(CLOCK_REALTIME, &t); +// printf("Time: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); while(1) { vTaskDelayMs(5000); diff --git a/extras/sntp/sntp.h b/extras/sntp/sntp.h index 9b5043a..d284d5c 100644 --- a/extras/sntp/sntp.h +++ b/extras/sntp/sntp.h @@ -39,6 +39,10 @@ void sntp_set_daylight(int day_light); /// us is not null, it will be filled with the microseconds. time_t sntp_get_rtc_time(int32_t *us); +/// Returns the time in seconds since Epoch. If tloc is not NULL, return +/// value is also stored in the memory pointed by tloc. +time_t time(time_t *tloc); + /// Set SNTP servers. Up to SNTP_NUM_SERVERS_SUPPORTED can be set. /// Returns 0 if OK, less than 0 if error. /// NOTE: This function must NOT be called before sntp_initialize(). diff --git a/extras/sntp/sntp_fun.c b/extras/sntp/sntp_fun.c index f4c02ae..efb6da8 100644 --- a/extras/sntp/sntp_fun.c +++ b/extras/sntp/sntp_fun.c @@ -1,5 +1,5 @@ /* - * Auxiliar functions to handle date/time along with lwIP sntp implementation. + * Auxiliary functions to handle date/time along with lwIP sntp implementation. * * Jesus Alonso (doragasu) */ @@ -15,46 +15,56 @@ #include "sntp.h" #define TIMER_COUNT RTC.COUNTER +#define __UNUSED(var) (void)var // daylight settings // Base calculated with value obtained from NTP server (64 bits) #define sntp_base (*((uint64_t*)RTC.SCRATCH)) // Timer value when base was obtained #define tim_ref (RTC.SCRATCH[2]) +// Calibration value +#define cal (RTC.SCRATCH[3]) +// Timezone (-11 to +13) +static int8_t tz; +// Daylight savings +static bool dst; -// Setters and getters for CAL, TZ and DST. -#define RTC_CAL_SET(val) (RTC.SCRATCH[3] |= (val) & 0x0000FFFF) -#define RTC_DST_SET(val) (RTC.SCRATCH[3] |= ((val)<<16) & 0x00010000) -#define RTC_TZ_SET(val) (RTC.SCRATCH[3] |= ((val)<<24) & 0xFF000000) - -#define RTC_CAL_GET() (RTC.SCRATCH[3] & 0x0000FFFF) -#define RTC_DST_GET() ((RTC.SCRATCH[3] & 0x00010000)>>16) -#define RTC_TZ_GET() ((((int)RTC.SCRATCH[3]) & ((int)0xFF000000))>>24) +//// Setters and getters for CAL, TZ and DST. +//#define RTC_CAL_SET(val) (RTC.SCRATCH[3] |= (val) & 0x0000FFFF) +//#define RTC_DST_SET(val) (RTC.SCRATCH[3] |= ((val)<<16) & 0x00010000) +//#define RTC_TZ_SET(val) (RTC.SCRATCH[3] |= ((val)<<24) & 0xFF000000) +// +//#define RTC_CAL_GET() (RTC.SCRATCH[3] & 0x0000FFFF) +//#define RTC_DST_GET() ((RTC.SCRATCH[3] & 0x00010000)>>16) +//#define RTC_TZ_GET() ((((int)RTC.SCRATCH[3]) & ((int)0xFF000000))>>24) // Implemented in sntp.c void sntp_init(void); // Sets time zone. Allowed values are in the range [-11, 13]. -// NOTE: Settings do not take effect until SNTP time is updated. It is +// NOTE: Settings do not take effect until SNTP time is updated. void sntp_set_timezone(int time_zone) { - //tz = time_zone; - RTC_TZ_SET(time_zone); + tz = time_zone; + //RTC_TZ_SET(time_zone); } // Sets daylight. // NOTE: Settings do not take effect until SNTP time is updated. void sntp_set_daylight(int day_light) { - //dst = day_light; - RTC_DST_SET(day_light); + dst = day_light; + //RTC_DST_SET(day_light); } void sntp_initialize(int time_zone, int day_light) { sntp_base = 0; - RTC_TZ_SET(time_zone); - RTC_DST_SET(day_light); + tz = time_zone; + //RTC_TZ_SET(time_zone); + dst = day_light; + //RTC_DST_SET(day_light); // To avoid div by 0 exceptions if requesting time before SNTP config - RTC_CAL_SET(1); + cal = 1; + //RTC_CAL_SET(1); tim_ref = TIMER_COUNT; sntp_init(); } @@ -72,7 +82,7 @@ static inline void sntp_check_timer_wrap(uint32_t current_value) { } // Return secs. If us is not a null pointer, fill it with usecs -time_t sntp_get_rtc_time(int32_t *us) { +inline time_t sntp_get_rtc_time(int32_t *us) { time_t secs; uint32_t tim; uint64_t base; @@ -81,39 +91,50 @@ time_t sntp_get_rtc_time(int32_t *us) { // Check for timer wrap sntp_check_timer_wrap(tim); base = sntp_base + tim - tim_ref; - secs = base * RTC_CAL_GET() / (1000000U<<12); +// secs = base * RTC_CAL_GET() / (1000000U<<12); + secs = base * cal / (1000000U<<12); if (us) { - *us = base * RTC_CAL_GET() % (1000000U<<12); +// *us = base * RTC_CAL_GET() % (1000000U<<12); + *us = base * cal % (1000000U<<12); } return secs; } +// Syscall implementation int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) { - (void)r; - (void)tzp; + __UNUSED(r); + __UNUSED(tzp); printf("DEB; gettimeofday called"); tp->tv_sec = sntp_get_rtc_time((int32_t*)&tp->tv_usec); return 0; } +time_t time(time_t *tloc) { + time_t datetime; + + datetime = sntp_get_rtc_time(NULL); + if (tloc) *tloc = datetime; + return datetime; +} /// Update RTC timer. Called by SNTP module each time it receives an update. void sntp_update_rtc(time_t t, uint32_t us) { // Apply daylight and timezone correction - t += (RTC_TZ_GET() + RTC_DST_GET()) * 3600; +// t += (RTC_TZ_GET() + RTC_DST_GET()) * 3600; + t += (tz + dst) * 3600; // DEBUG: Compute and print drift int64_t sntp_current = sntp_base + TIMER_COUNT - tim_ref; - int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); - printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), RTC_CAL_GET()); +// int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); + int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal; +// printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), RTC_CAL_GET()); + printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), cal); tim_ref = TIMER_COUNT; - RTC_CAL_SET(sdk_system_rtc_clock_cali_proc()); + cal = sdk_system_rtc_clock_cali_proc(); +// RTC_CAL_SET(sdk_system_rtc_clock_cali_proc()); - sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); - - // DEBUG: Print obtained secs and check calculated secs are the same - time_t deb = sntp_base * RTC_CAL_GET() / (1000000U<<12); - printf("\nT: %lu, %lu, %s\n", t, deb, ctime(&deb)); +// sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); + sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal; } From 9651692ca23eeacf2f830deeac87efa6c5e16ecb Mon Sep 17 00:00:00 2001 From: doragasu Date: Sun, 13 Mar 2016 18:29:30 +0100 Subject: [PATCH 4/5] Cleanup and some changes to make implementation a bit more conforming to the standard. --- examples/sntp/sntp_example.c | 66 +++++++---------------------- extras/sntp/sntp.h | 80 ++++++++++++++++++++++++------------ extras/sntp/sntp_fun.c | 71 ++++++++++++-------------------- 3 files changed, 93 insertions(+), 124 deletions(-) diff --git a/examples/sntp/sntp_example.c b/examples/sntp/sntp_example.c index 78601dd..96066a2 100644 --- a/examples/sntp/sntp_example.c +++ b/examples/sntp/sntp_example.c @@ -20,7 +20,7 @@ #include -// Add extras/sntp component to makefile for this include to work +/* Add extras/sntp component to makefile for this include to work */ #include #define SNTP_SERVERS "0.pool.ntp.org", "1.pool.ntp.org", \ @@ -29,71 +29,36 @@ #define vTaskDelayMs(ms) vTaskDelay((ms)/portTICK_RATE_MS) #define UNUSED_ARG(x) (void)x -// Use GPIO pin 2 -const int gpio_frc2 = 2; -// 1 Hz blink frequency -const int freq_frc2 = 1; - -void SntpTsk(void *pvParameters) +void sntp_tsk(void *pvParameters) { char *servers[] = {SNTP_SERVERS}; UNUSED_ARG(pvParameters); - // Wait until we have joined AP and are assigned an IP + /* Wait until we have joined AP and are assigned an IP */ while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) { vTaskDelayMs(100); } - // Start SNTP + /* Start SNTP */ printf("Starting SNTP... "); - sntp_set_update_delay(1*60000); - sntp_initialize(1, 0); + /* SNTP will request an update each 5 minutes */ + sntp_set_update_delay(5*60000); + /* Set GMT+1 zone, daylight savings off */ + const struct timezone tz = {1*60, 0}; + /* SNTP initialization */ + sntp_initialize(&tz); + /* Servers must be configured right after initialization */ sntp_set_servers(servers, sizeof(servers) / sizeof(char*)); printf("DONE!\n"); -// struct timespec ts; -// clock_getres(CLOCK_REALTIME, &ts); -// printf("Time resolution: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); -// -// clock_gettime(CLOCK_REALTIME, &t); -// printf("Time: %d secs, %d nanosecs\n", t.tv_sec, t.tv_nsec); - + /* Print date and time each 5 seconds */ while(1) { vTaskDelayMs(5000); - time_t ts = sntp_get_rtc_time(NULL); + time_t ts = time(NULL); printf("TIME: %s", ctime(&ts)); } } -void frc2_interrupt_handler(void) -{ - /* FRC2 needs the match register updated on each timer interrupt */ - timer_set_frequency(FRC2, freq_frc2); - gpio_toggle(gpio_frc2); -} - -// Configure FRC2 to blink the LED. I'm messing with FRC2 just to test if -// it does affect FreeRTOS. -void Frc2Config(void) { - /* configure GPIO */ - gpio_enable(gpio_frc2, GPIO_OUTPUT); - gpio_write(gpio_frc2, 1); - - /* stop timer and mask its interrupt as a precaution */ - timer_set_interrupts(FRC2, false); - timer_set_run(FRC2, false); - - /* set up ISR */ - _xt_isr_attach(INUM_TIMER_FRC2, frc2_interrupt_handler); - - /* configure timer frequency */ - timer_set_frequency(FRC2, freq_frc2); - - /* unmask interrupt and start timer */ - timer_set_interrupts(FRC2, true); - timer_set_run(FRC2, true); -} - void user_init(void) { uart_set_baud(0, 115200); @@ -108,9 +73,6 @@ void user_init(void) sdk_wifi_set_opmode(STATION_MODE); sdk_wifi_station_set_config(&config); - // Mess with FRC2 to test if it interferes with FreeRTOS - Frc2Config(); - - xTaskCreate(SntpTsk, (signed char *)"SNTP", 1024, NULL, 1, NULL); + xTaskCreate(sntp_tsk, (signed char *)"SNTP", 1024, NULL, 1, NULL); } diff --git a/extras/sntp/sntp.h b/extras/sntp/sntp.h index d284d5c..dde9331 100644 --- a/extras/sntp/sntp.h +++ b/extras/sntp/sntp.h @@ -9,48 +9,74 @@ #define _SNTP_H_ #include +#include #include -/// Update SNTP RTC timer -void sntp_update_rtc(time_t t, uint32_t us); - -/// Function used to update the date/time, with microseconds resolution. +/* + * Function used by lwIP sntp module to update the date/time, + * with microseconds resolution. + */ #define SNTP_SET_SYSTEM_TIME_US(sec, us) sntp_update_rtc(sec, us) -/// For the lwIP implementation of SNTP to allow using names for NTP servers. +/* + * For the lwIP implementation of SNTP to allow using names for NTP servers. + */ #define SNTP_SERVER_DNS 1 -/// Number of supported NTP servers +/* + * Number of supported NTP servers + */ #define SNTP_NUM_SERVERS_SUPPORTED 4 -/// Initialize the module, and start requesting SNTP updates. This function -/// must be called only once. -void sntp_initialize(int time_zone, int day_light); +/* + * Initialize the module, and start requesting SNTP updates. This function + * must be called only once. + * WARNING: tz->tz_dsttime doesn't have the same meaning as the standard + * implementation. If it is set to 1, a dst hour will be applied. If set + * to zero, time will not be modified. + */ +void sntp_initialize(const struct timezone *tz); -/// Sets time zone. Allowed values are in the range [-11, 13]. -/// NOTE: Settings do not take effect until SNTP time is updated. It is -void sntp_set_timezone(int time_zone); +/* + * Sets time zone. Allowed values are in the range [-11, 13]. + * NOTE: Settings do not take effect until SNTP time is updated. It is + * recommended to set these parameters only during initialization. + * WARNING: tz->tz_dsttime doesn't have the same meaning as the standard + * implementation. If it is set to 1, a dst hour will be applied. If set + * to zero, time will not be modified. + */ +void sntp_set_timezone(const struct timezone *tz); -/// Sets daylight. -/// NOTE: Settings do not take effect until SNTP time is updated. -void sntp_set_daylight(int day_light); - -/// Returns the time read from RTC counter, in seconds from Epoch. If -/// us is not null, it will be filled with the microseconds. -time_t sntp_get_rtc_time(int32_t *us); - -/// Returns the time in seconds since Epoch. If tloc is not NULL, return -/// value is also stored in the memory pointed by tloc. +/* + * Returns the time in seconds since Epoch. If tloc is not NULL, return + * value is also stored in the memory pointed by tloc. + */ time_t time(time_t *tloc); -/// Set SNTP servers. Up to SNTP_NUM_SERVERS_SUPPORTED can be set. -/// Returns 0 if OK, less than 0 if error. -/// NOTE: This function must NOT be called before sntp_initialize(). +/* + * Set SNTP servers. Up to SNTP_NUM_SERVERS_SUPPORTED can be set. + * Returns 0 if OK, less than 0 if error. + * NOTE: This function must NOT be called before sntp_initialize(). + */ int sntp_set_servers(char *server_url[], int num_servers); -/// Sets the update delay in ms. If requested value is less than 15s, -/// a 15s update interval will be set. +/* + * Sets the update delay in ms. If requested value is less than 15s, + * a 15s update interval will be set. + */ void sntp_set_update_delay(uint32_t ms); +/* + * Returns the time read from RTC counter, in seconds from Epoch. If + * us is not null, it will be filled with the microseconds. + */ +time_t sntp_get_rtc_time(int32_t *us); + +/* + * Update RTC timer. This function is called by the SNTP module each time + * an SNTP update is received. + */ +void sntp_update_rtc(time_t t, uint32_t us); + #endif /* _SNTP_H_ */ diff --git a/extras/sntp/sntp_fun.c b/extras/sntp/sntp_fun.c index efb6da8..35c068d 100644 --- a/extras/sntp/sntp_fun.c +++ b/extras/sntp/sntp_fun.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -15,7 +14,6 @@ #include "sntp.h" #define TIMER_COUNT RTC.COUNTER -#define __UNUSED(var) (void)var // daylight settings // Base calculated with value obtained from NTP server (64 bits) @@ -24,47 +22,35 @@ #define tim_ref (RTC.SCRATCH[2]) // Calibration value #define cal (RTC.SCRATCH[3]) -// Timezone (-11 to +13) -static int8_t tz; -// Daylight savings -static bool dst; -//// Setters and getters for CAL, TZ and DST. -//#define RTC_CAL_SET(val) (RTC.SCRATCH[3] |= (val) & 0x0000FFFF) -//#define RTC_DST_SET(val) (RTC.SCRATCH[3] |= ((val)<<16) & 0x00010000) -//#define RTC_TZ_SET(val) (RTC.SCRATCH[3] |= ((val)<<24) & 0xFF000000) -// -//#define RTC_CAL_GET() (RTC.SCRATCH[3] & 0x0000FFFF) -//#define RTC_DST_GET() ((RTC.SCRATCH[3] & 0x00010000)>>16) -//#define RTC_TZ_GET() ((((int)RTC.SCRATCH[3]) & ((int)0xFF000000))>>24) +// Timezone related data. +static struct timezone stz; // Implemented in sntp.c void sntp_init(void); -// Sets time zone. Allowed values are in the range [-11, 13]. +// Sets time zone. // NOTE: Settings do not take effect until SNTP time is updated. -void sntp_set_timezone(int time_zone) { - tz = time_zone; - //RTC_TZ_SET(time_zone); +void sntp_set_timezone(const struct timezone *tz) { + if (tz) { + stz = *tz; + } else { + stz.tz_minuteswest = 0; + stz.tz_dsttime = 0; + } } -// Sets daylight. -// NOTE: Settings do not take effect until SNTP time is updated. -void sntp_set_daylight(int day_light) { - dst = day_light; - //RTC_DST_SET(day_light); -} - - -void sntp_initialize(int time_zone, int day_light) { +// Initialization +void sntp_initialize(const struct timezone *tz) { + if (tz) { + stz = *tz; + } else { + stz.tz_minuteswest = 0; + stz.tz_dsttime = 0; + } sntp_base = 0; - tz = time_zone; - //RTC_TZ_SET(time_zone); - dst = day_light; - //RTC_DST_SET(day_light); // To avoid div by 0 exceptions if requesting time before SNTP config cal = 1; - //RTC_CAL_SET(1); tim_ref = TIMER_COUNT; sntp_init(); } @@ -91,25 +77,25 @@ inline time_t sntp_get_rtc_time(int32_t *us) { // Check for timer wrap sntp_check_timer_wrap(tim); base = sntp_base + tim - tim_ref; -// secs = base * RTC_CAL_GET() / (1000000U<<12); secs = base * cal / (1000000U<<12); if (us) { -// *us = base * RTC_CAL_GET() % (1000000U<<12); *us = base * cal % (1000000U<<12); } return secs; } -// Syscall implementation +// Syscall implementation. doesn't seem to use tzp. int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) { - __UNUSED(r); - __UNUSED(tzp); + (void)r; + // Syscall defined by xtensa newlib defines tzp as void* + // So it looks like it is not used. Also check tp is not NULL + if (tzp || !tp) return EINVAL; - printf("DEB; gettimeofday called"); tp->tv_sec = sntp_get_rtc_time((int32_t*)&tp->tv_usec); return 0; } +// Added te get nearer the standard way of using time functions. time_t time(time_t *tloc) { time_t datetime; @@ -118,23 +104,18 @@ time_t time(time_t *tloc) { return datetime; } -/// Update RTC timer. Called by SNTP module each time it receives an update. +// Update RTC timer. Called by SNTP module each time it receives an update. void sntp_update_rtc(time_t t, uint32_t us) { // Apply daylight and timezone correction -// t += (RTC_TZ_GET() + RTC_DST_GET()) * 3600; - t += (tz + dst) * 3600; + t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60; // DEBUG: Compute and print drift int64_t sntp_current = sntp_base + TIMER_COUNT - tim_ref; -// int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal; -// printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), RTC_CAL_GET()); printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), cal); tim_ref = TIMER_COUNT; cal = sdk_system_rtc_clock_cali_proc(); -// RTC_CAL_SET(sdk_system_rtc_clock_cali_proc()); -// sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / RTC_CAL_GET(); sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal; } From 11c9031d9b044013ceca3704360dcf5349cf3e41 Mon Sep 17 00:00:00 2001 From: doragasu Date: Thu, 31 Mar 2016 16:05:15 +0200 Subject: [PATCH 5/5] Removed my _time() implementation to use newlib provided one. --- examples/sntp/sntp_example.c | 1 + extras/sntp/sntp.h | 6 ------ extras/sntp/sntp_fun.c | 9 --------- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/examples/sntp/sntp_example.c b/examples/sntp/sntp_example.c index 96066a2..242489b 100644 --- a/examples/sntp/sntp_example.c +++ b/examples/sntp/sntp_example.c @@ -22,6 +22,7 @@ /* Add extras/sntp component to makefile for this include to work */ #include +#include #define SNTP_SERVERS "0.pool.ntp.org", "1.pool.ntp.org", \ "2.pool.ntp.org", "3.pool.ntp.org" diff --git a/extras/sntp/sntp.h b/extras/sntp/sntp.h index dde9331..dd25cf0 100644 --- a/extras/sntp/sntp.h +++ b/extras/sntp/sntp.h @@ -47,12 +47,6 @@ void sntp_initialize(const struct timezone *tz); */ void sntp_set_timezone(const struct timezone *tz); -/* - * Returns the time in seconds since Epoch. If tloc is not NULL, return - * value is also stored in the memory pointed by tloc. - */ -time_t time(time_t *tloc); - /* * Set SNTP servers. Up to SNTP_NUM_SERVERS_SUPPORTED can be set. * Returns 0 if OK, less than 0 if error. diff --git a/extras/sntp/sntp_fun.c b/extras/sntp/sntp_fun.c index 35c068d..7aaca20 100644 --- a/extras/sntp/sntp_fun.c +++ b/extras/sntp/sntp_fun.c @@ -95,15 +95,6 @@ int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) { return 0; } -// Added te get nearer the standard way of using time functions. -time_t time(time_t *tloc) { - time_t datetime; - - datetime = sntp_get_rtc_time(NULL); - if (tloc) *tloc = datetime; - return datetime; -} - // Update RTC timer. Called by SNTP module each time it receives an update. void sntp_update_rtc(time_t t, uint32_t us) { // Apply daylight and timezone correction