Concepts First
- SNTP (Simple Network Time Protocol): requests "current time" from an NTP server over the Internet; millisecond precision, the most common way for IoT devices to calibrate clocks.
- NTP server: public servers that provide standard time, e.g. Aliyun's
ntp.aliyun.com,pool.ntp.org. - Unix timestamp (epoch): seconds elapsed since 1970-01-01 00:00:00 UTC, usually stored as an integer.
- SNTP_SET_SYSTEM_TIME hook: lwIP's SNTP does not store the received time by default (the macro is a no-op); you must define it to save the time into your own variable. This example stores it in the global
g_sntp_time.
Example Overview
The SDK has no standalone SNTP example, but the lwIP stack ships an SNTP client (components/net/lwip/lwip/src/apps/sntp/, compiled into every wireless example by default). This self-written example builds on the wifi_tcp skeleton:
- after getting an IP, creates
sntp_task: sets poll mode → sets serverntp.aliyun.com→ callssntp_init(); - checks sync state every 5 seconds (
sntp_getreachability) and prints the formatted time; - uses
SNTP_SET_SYSTEM_TIME(sec)inlwipopts_user.hto store the received seconds intog_sntp_time. - Related reference: the AT firmware's SNTP module (
components/net/netbus/atmodule/) is built on the same lwIP implementation and adds ansntp_settimesynccbcallback; theAT+SNTPcommand is implemented there.
Operation Steps
The SDK has no standalone SNTP example; this page is written on the wifi_tcp skeleton (marked self-written). First enter the directory:
cd examples/wifi/sta/wifi_tcpOverwrite the example’s main.c with the self-written one below; then append two lines to lwipopts_user.h in the same directory (so lwIP stores the received time into a global variable that SNTP can read):
Both Ai-M61 and Ai-M62 use bl616:
make CHIP=bl616 BOARD=bl616dkHold BOOT, briefly press EN/RST to enter download mode, then flash:
make flash CHIP=bl616 COMX=/dev/ttyUSB0Serial tool at 2000000 baud. Wait for bouffalolab />, connect to the router; after getting an IP the log shows Got IP, starting SNTP and the SNTP task starts:
wifi_sta_connect Your_SSID 12345678Within a few to a dozen seconds, the log prints a formatted time like SNTP time: 2026-08-11 10:30:00, meaning time sync succeeded; before sync it prints SNTP waiting for sync... and retries every 5 seconds.
Append the following to lwipopts_user.h:
/* Store SNTP time into a global variable (defined in this example) */
#define SNTP_SET_SYSTEM_TIME(sec) g_sntp_time = (time_t)(sec)
extern volatile time_t g_sntp_time;Code Execution Flow
The complete flow of the self-written example from boot to printing time:
APIs Used by the Example
sntp_setoperatingmode(SNTP_OPMODE_POLL)
Sets SNTP to poll mode (actively request from the server); must be called before sntp_init().
Parameters:
SNTP_OPMODE_POLL: poll mode (default)
Return: none
sntp_setservername(idx, "ntp.aliyun.com")
Sets the idx-th NTP server by domain name (DNS resolved internally), more flexible than an IP.
Parameters:
idx: server index, starting from 0server: server domain
Return: none
sntp_getreachability(idx)
Queries whether the idx-th server has synced successfully (nonzero = synced).
Parameters:
idx: server index
Return: 0 not synced; nonzero synced
SNTP_SET_SYSTEM_TIME(sec)
Macro invoked by lwIP when sync succeeds; the argument is Unix seconds. It is a no-op by default — you must define it yourself (this example defines it in lwipopts_user.h to write into g_sntp_time), otherwise the time cannot be read.
Parameters:
sec: Unix seconds (since 1970-01-01 UTC)
Return: none
Complete Code
The self-written main.c below (written on the SDK wifi_tcp skeleton; not an SDK file; the g_sntp_time declaration and the lwipopts addition are shown in Operation Steps). Collapsed by default; click to expand:
📜 Click to expand self-written sntp main.c full code
/*
* Self-written example: network time via the lwIP SNTP client
* Usage:
* 1. Overwrite examples/wifi/sta/wifi_tcp/main.c with this file;
* 2. Append to examples/wifi/sta/wifi_tcp/lwipopts_user.h:
* #define SNTP_SET_SYSTEM_TIME(sec) g_sntp_time = (time_t)(sec)
* extern volatile time_t g_sntp_time;
* 3. make CHIP=bl616 BOARD=bl616dk && make flash CHIP=bl616 COMX=/dev/ttyUSB0
*/
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include <time.h>
#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include <lwip/apps/sntp.h>
#include "wifi_mgmr_ext.h"
#include "fhost_api.h"
#include "wifi_mgmr.h"
#include "bflb_irq.h"
#include "bflb_uart.h"
#include "rfparam_adapter.h"
#include "async_event.h"
#include "mm.h"
#include "board.h"
#include "shell.h"
#define DBG_TAG "MAIN"
#include "log.h"
/* Time written after SNTP sync (Unix seconds) */
volatile time_t g_sntp_time = 0;
static volatile uint32_t wifi_state = 0;
static struct bflb_device_s *uart0;
extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);
void wifi_event_handler(async_input_event_t ev, void *priv)
{
switch (ev->code) {
case CODE_WIFI_ON_GOT_IP:
wifi_state = 1;
LOG_I("Got IP, starting SNTP\r\n");
break;
case CODE_WIFI_ON_DISCONNECT:
wifi_state = 0;
LOG_I("WiFi disconnected\r\n");
break;
default:
break;
}
}
static void print_sntp_time(void)
{
struct tm tm_now;
time_t t = g_sntp_time;
localtime_r(&t, &tm_now);
LOG_I("SNTP time: %04d-%02d-%02d %02d:%02d:%02d\r\n",
tm_now.tm_year + 1900, tm_now.tm_mon + 1, tm_now.tm_mday,
tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
}
static void sntp_task(void *param)
{
/* Wait for Wi-Fi to get an IP */
while (!wifi_state) {
vTaskDelay(pdMS_TO_TICKS(200));
}
/* Configure and start SNTP */
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "ntp.aliyun.com");
sntp_init();
LOG_I("SNTP started, server: ntp.aliyun.com\r\n");
while (1) {
if (sntp_getreachability(0) && g_sntp_time != 0) {
print_sntp_time();
} else {
LOG_I("SNTP waiting for sync...\r\n");
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
static void wifi_start_firmware_task(void *param)
{
LOG_I("Starting wifi ...\r\n");
async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);
wifi_task_create();
LOG_I("Starting fhost ...\r\n");
fhost_init();
vTaskDelete(NULL);
}
int main(void)
{
board_init();
uart0 = bflb_device_get_by_name("uart0");
shell_init_with_task(uart0);
if (0 != rfparam_init(0, NULL, 0)) {
LOG_I("PHY RF init failed!\r\n");
return 0;
}
tcpip_init(NULL, NULL);
xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
xTaskCreate(sntp_task, "sntp", 1024, NULL, 11, NULL);
vTaskStartScheduler();
while (1) {
}
}FAQ
Keeps printing waiting for sync
Confirm Wi-Fi got an IP (log shows Got IP, starting SNTP); public NTP servers occasionally drop UDP packets — a few retries (15s default timeout) usually succeed; try pool.ntp.org or a corporate NTP server.
Printed time is 1970
That means g_sntp_time is still 0: check that SNTP_SET_SYSTEM_TIME in lwipopts_user.h was actually appended and rebuild; the macro must be defined before the sntp header is processed (lwipopts satisfies this because lwIP processes it first).
Time is 8 hours behind Beijing time
SNTP returns UTC; Beijing is UTC+8. The example prints UTC directly; add 8 hours (t += 8 * 3600) before printing, or convert with a timezone library.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

