SNTP Time Sync

Overview

This tutorial will show you how to synchronize time with a remote SNTP server.

Potential use cases are:

  • Providing a real-time clock for embedded systems without one
  • Improving the precision and stability of such a clock
  • Providing a time reference for TLS in embedded systems without a clock.

SNTP API

The SNTP client API is really tiny and consists of just two functions:

struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
                                      mg_event_handler_t fn, void *fn_data);
void mg_sntp_request(struct mg_connection *c);

The function mg_sntp_connect() tries to connect to the SNTP server whose domain name is specified in parameter url, returning a connection handler or NULL on failure. The function specified in parameter fn will be our callback function, and parameter fn_data will be available as c->fn_data.

The function mg_sntp_request() performs the action of sending the time request to the SNTP server. The event manager will in turn call the internal protocol handler with the result of this action, and then our callback function with the epoch time in milliseconds.

For detailed information, see the documentation.

Simplest usage example

We can just call mg_now(), that returns current time in milliseconds, whenever we need it; providing we have first setup a server:

mg_sntp_connect(mgr&, NULL, NULL, NULL);
...
  {
    uint64_t curtime = mg_now();
    ...
  }

Advanced example

This example adds periodic syncing, and an event handler. There we can just call mg_now() as before, or run our own synchronization.

Our actions will be:

  • We first write a callback function to handle the results of the call to the SNTP client function. This function will perform the required task to handle the time reference in our system
  • Since we need to perform this synchronization periodically, we then write a timer callback function to call the SNTP client. You can check the timers tutorial for more information on using timers
  • Finally, after initializing an event manager, we add the timer callback function to the event manager's timer list

The timer callback function will be called by the event manager once the specified time has elapsed, this, in turn, will call the SNTP client. Once the SNTP client has finished, the event manager will call our callback function to handle system time. The time information so obtained is internally saved in order to be served by a call to the function my_time().

When using a TLS library such as mbed-TLS, a working time() function is required. Other parts of the system might also want to make good use of a call to get the system time, so we provide it.

  • Follow the Build Tools tutorial to setup your development environment.

  • Start a terminal in the project directory; if you've not already done so, clone the Mongoose Library repo

    git clone https://github.com/cesanta/mongoose
    
  • Build the example, this will also start Mongoose:

    cd mongoose/tutorials/udp/sntp-time-sync
    make clean all
    
  • Observe the log

    961a677 3 net.c:174:mg_connect          1 -1 udp://time.google.com:123
    961a677 3 net.c:174:mg_connect          2 -1 udp://8.8.8.8:53
    961a678 3 sock.c:141:mg_send            2 4 0:0:0 33 err 0
    961a678 1 sntp.c:72:mg_sntp_request     1 wait until resolved
    961a67e 3 sock.c:295:read_conn          2 4 0:0:0 97 err 0
    961a67e 3 dns.c:169:dns_cb              1 time.google.com is 216.239.35.12
    961a67f 3 sock.c:141:mg_send            1 5 0:0:0 48 err 0
    961a701 3 sock.c:295:read_conn          1 5 0:0:0 48 err 0
    961a701 2 main.c:31:sfn                 SNTP-updated current time is: 1718033218521 ms from epoch
    961a701 2 main.c:36:sfn                 Got SNTP time: 1718033218521 ms from epoch, 
    961a701 3 sntp.c:59:sntp_cb             1 got time: 1718033218521 ms from epoch
    961a701 3 net.c:148:mg_close_conn       1 5 closed
    

What this example does is:

  • write an SNTP client callback function that receives the results, logs the time, and performs the time synchronization. It may get one of these events of interest:

    • MG_EV_SNTP_TIME: valid server response, we take the current time information from the server and subtract our uptime to get our boot timestamp
    • MG_EV_CLOSE: connection closed, clean up
  • write a timer callback function that queries the SNTP server via the SNTP client API:

  • initialize the event manager, add the timer callback, and call the event manager in an infinite loop:

    The timer will fire every 5 seconds for demo purposes, you might want to extend this time for production code.

The function to serve the time is the following:

In case it is going to be used with mbedTLS on an embedded system, rename it to time(), or work your way on its platform macros.

The function mg_millis() returns the current uptime in milliseconds; we just add our running time (in seconds) to our boot timestamp to get current time. In fact, this is what mg_now() does internally.

Browse latest code