diff --git a/README.md b/README.md index f8c9ada..db3d041 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,6 @@ When included to a Mongoose OS application, this library fetches the current time from the SNTP server (by default, `time.google.com` is used) every time the Internet connection is established, and adjusts the device time. +If NTP server option is returned by DHCP server, it is tried first. + See `mos.yml` for the SNTP configuration available. diff --git a/mos.yml b/mos.yml index d00d376..430e464 100644 --- a/mos.yml +++ b/mos.yml @@ -1,7 +1,7 @@ author: mongoose-os type: lib description: A SNTP library -version: 1.0 +version: 1.1.0 sources: - src @@ -12,7 +12,7 @@ filesystem: config_schema: - ["sntp", "o", {title: "SNTP settings"}] - ["sntp.enable", "b", true, {title: "Enable SNTP"}] - - ["sntp.server", "s", "time.google.com", {title: "Server address"}] + - ["sntp.server", "s", "time.google.com", {title: "Server address. If DHCP lease contains NTP server, tries that first."}] - ["sntp.retry_min", "i", 1, {title: "Minimum retry interval"}] - ["sntp.retry_max", "i", 30, {title: "Maximum retry interval"}] - ["sntp.update_interval", "i", 7200, {title: "Update interval. If 0, performs a one-off sync"}] diff --git a/src/mgos_sntp.c b/src/mgos_sntp.c index ed11e2d..51d2d94 100644 --- a/src/mgos_sntp.c +++ b/src/mgos_sntp.c @@ -33,8 +33,11 @@ struct mgos_sntp_state { struct mg_connection *nc; double last_synced_uptime; - int retry_timeout_ms; mgos_timer_id retry_timer_id; + unsigned int retry_timeout_ms : 24; + unsigned int sync_ok : 1; + unsigned int idx : 1; + char local_server[16]; }; static struct mgos_sntp_state s_state; @@ -62,6 +65,7 @@ static void mgos_sntp_ev(struct mg_connection *nc, int ev, void *ev_data, if (mgos_settimeofday(m->time, NULL) != 0) { LOG(LL_ERROR, ("Failed to set time")); } + s_state.sync_ok = true; s_state.retry_timeout_ms = 0; s_state.last_synced_uptime = mgos_uptime(); nc->flags |= MG_F_CLOSE_IMMEDIATELY; @@ -78,7 +82,6 @@ static void mgos_sntp_ev(struct mg_connection *nc, int ev, void *ev_data, nc->flags |= MG_F_CLOSE_IMMEDIATELY; break; case MG_EV_CLOSE: - LOG(LL_DEBUG, ("SNTP close")); if (s_state.nc == nc) { s_state.nc = NULL; mgos_sntp_retry(); @@ -92,16 +95,23 @@ static void mgos_sntp_ev(struct mg_connection *nc, int ev, void *ev_data, static bool mgos_sntp_query(const char *server) { if (s_state.nc != NULL) { s_state.nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + if (server == NULL) { return false; } + s_state.sync_ok = false; s_state.nc = mg_sntp_connect(mgos_get_mgr(), mgos_sntp_ev, NULL, server); - LOG(LL_INFO, ("SNTP query to %s", server)); + LOG(LL_DEBUG, ("SNTP query to %s", server)); return (s_state.nc != NULL); } static void mgos_sntp_retry_timer_cb(void *user_data) { s_state.retry_timer_id = MGOS_INVALID_TIMER_ID; - mgos_sntp_query(mgos_sys_config_get_sntp_server()); + const char *server = mgos_sys_config_get_sntp_server(); + if (s_state.idx == 0 && s_state.local_server[0] != '\0') { + server = s_state.local_server; + } + mgos_sntp_query(server); /* * Response may never arrive, so we schedule a retry immediately. * Successful response will clear the timer. @@ -114,7 +124,7 @@ static void mgos_sntp_retry(void) { if (!mgos_sys_config_get_sntp_enable()) return; if (s_state.retry_timer_id != MGOS_INVALID_TIMER_ID) return; int rt_ms = 0; - if (s_state.last_synced_uptime != 0) { + if (s_state.sync_ok) { rt_ms = mgos_sys_config_get_sntp_update_interval() * 1000; if (rt_ms == 0) return; } else { @@ -127,6 +137,11 @@ static void mgos_sntp_retry(void) { } s_state.retry_timeout_ms = rt_ms; } + if (s_state.idx == 1 || s_state.sync_ok) { + s_state.idx = 0; + } else { + s_state.idx = 1; + } rt_ms = (int) mgos_rand_range(rt_ms * 0.9, rt_ms * 1.1); LOG(LL_DEBUG, ("SNTP next query in %d ms", rt_ms)); s_state.retry_timer_id = @@ -146,9 +161,39 @@ static void mgos_time_change_cb(int ev, void *evd, void *arg) { (void) ev; } +static void mgos_sntp_update_server(void) { + struct mgos_net_ip_info ip_info; + memset(&ip_info, 0, sizeof(ip_info)); + if (mgos_net_get_ip_info(MGOS_NET_IF_TYPE_ETHERNET, 0, &ip_info) && + ip_info.ip.sin_addr.s_addr != 0 && ip_info.ntp.sin_addr.s_addr != 0) { + goto out; + } + memset(&ip_info, 0, sizeof(ip_info)); + if (mgos_net_get_ip_info(MGOS_NET_IF_TYPE_WIFI, 0, &ip_info) && + ip_info.ip.sin_addr.s_addr != 0 && ip_info.ntp.sin_addr.s_addr != 0) { + goto out; + } + memset(&ip_info, 0, sizeof(ip_info)); + if (mgos_net_get_ip_info(MGOS_NET_IF_TYPE_PPP, 0, &ip_info) && + ip_info.ip.sin_addr.s_addr != 0 && ip_info.ntp.sin_addr.s_addr != 0) { + goto out; + } + mgos_net_str_to_ip(MGOS_DEFAULT_NAMESERVER, &ip_info.dns); + +out: + if (ip_info.ntp.sin_addr.s_addr != 0) { + mgos_net_ip_to_str(&ip_info.ntp, s_state.local_server); + LOG(LL_DEBUG, ("Setting %s server to %s", "NTP", s_state.local_server)); + } else { + s_state.local_server[0] = '\0'; + } +} + static void mgos_sntp_net_ev(int ev, void *evd, void *arg) { if (ev != MGOS_NET_EV_IP_ACQUIRED) return; + mgos_sntp_update_server(); mgos_sntp_retry(); + s_state.idx = 0; // Start with local server. (void) evd; (void) arg; } @@ -159,11 +204,6 @@ double mgos_sntp_get_last_synced_uptime(void) { bool mgos_sntp_init(void) { if (!mgos_sys_config_get_sntp_enable()) return true; - if (mgos_sys_config_get_sntp_server() == NULL) { - LOG(LL_ERROR, ("sntp.server is required")); - return false; - } - mgos_event_add_handler(MGOS_EVENT_TIME_CHANGED, mgos_time_change_cb, mgos_get_mgr()); mgos_event_add_group_handler(MGOS_EVENT_GRP_NET, mgos_sntp_net_ev, NULL);