Skip to content

Commit

Permalink
Use NTP server from DHCP first, then sntp.server
Browse files Browse the repository at this point in the history
  • Loading branch information
rojer committed Sep 14, 2021
1 parent d9f7fd7 commit 7776561
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 12 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
4 changes: 2 additions & 2 deletions mos.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
author: mongoose-os
type: lib
description: A SNTP library
version: 1.0
version: 1.1.0

sources:
- src
Expand All @@ -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"}]
Expand Down
60 changes: 50 additions & 10 deletions src/mgos_sntp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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.
Expand All @@ -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 {
Expand All @@ -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 =
Expand All @@ -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;
}
Expand All @@ -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);
Expand Down

0 comments on commit 7776561

Please sign in to comment.