概念先知道
- SNTP(简单网络时间协议):通过互联网向 NTP 服务器请求“当前时间”,毫秒级精度,是物联网设备校时最常用的方式。
- NTP 服务器:提供标准时间的公共服务器,例如阿里云
ntp.aliyun.com、pool.ntp.org。 - Unix 时间戳(epoch):自 1970-01-01 00:00:00 UTC 起经过的秒数,通常用整数保存。
- SNTP_SET_SYSTEM_TIME 钩子:lwIP 的 SNTP 默认不保存收到的校时结果(该宏默认是空操作),必须自己定义它把时间写入变量;本示例存入全局变量
g_sntp_time。
例程功能简介
SDK 没有独立 SNTP 例程,但 lwIP 协议栈自带 SNTP 客户端(components/net/lwip/lwip/src/apps/sntp/,默认编入所有无线例程)。本页为自编示例,在 wifi_tcp 骨架上编写:
- 拿到 IP 后创建
sntp_task:设置轮询模式 → 指定服务器ntp.aliyun.com→ 调用sntp_init(); - 每 5 秒检查一次同步状态(
sntp_getreachability)并打印格式化时间; - 通过
lwipopts_user.h里的SNTP_SET_SYSTEM_TIME(sec)把收到的秒数存入g_sntp_time。 - 相关参考:AT 固件的 SNTP 模块(
components/net/netbus/atmodule/)基于同一套 lwIP 实现,并增加sntp_settimesynccb回调,AT+SNTP指令即实现于此。
操作步骤
SDK 没有独立的 SNTP 例程,本页在 wifi_tcp 骨架上编写(标注自编示例)。先进入目录:
cd examples/wifi/sta/wifi_tcp用下方自编代码覆盖例程的 main.c,然后在同目录的 lwipopts_user.h 末尾追加两行(让 lwIP 把收到的校时秒数存入全局变量,供 SNTP 读取):
Ai-M61 与 Ai-M62 统一填写 bl616:
make CHIP=bl616 BOARD=bl616dk按住 BOOT 键不放、短按 EN/RST 进入下载模式后烧录:
make flash CHIP=bl616 COMX=/dev/ttyUSB0串口助手波特率 2000000。看到 bouffalolab /> 后连接路由器,拿到 IP 后日志打印 Got IP, starting SNTP,SNTP 任务随即启动:
wifi_sta_connect Your_SSID 12345678几秒到十几秒内,日志会打印格式化时间,如 SNTP time: 2026-08-11 10:30:00,说明校时成功;同步前打印 SNTP waiting for sync... 并每 5 秒重试。
在 lwipopts_user.h 末尾追加:
/* 把 SNTP 时间存入全局变量(在本示例中定义) */
#define SNTP_SET_SYSTEM_TIME(sec) g_sntp_time = (time_t)(sec)
extern volatile time_t g_sntp_time;代码执行流程
自编示例从启动到打印时间的完整流程如下:
例程调用的 API 介绍
sntp_setoperatingmode(SNTP_OPMODE_POLL)
设置 SNTP 为轮询模式(主动向服务器请求),必须在 sntp_init() 之前调用。
参数:
SNTP_OPMODE_POLL:轮询模式(默认)
返回值:无
sntp_setservername(idx, "ntp.aliyun.com")
按域名设置第 idx 个 NTP 服务器(内部自动做 DNS 解析),比填 IP 更灵活。
参数:
idx:服务器编号,从 0 开始server:服务器域名
返回值:无
SNTP_SET_SYSTEM_TIME(sec)
lwIP 在同步成功时调用的宏,参数为 Unix 秒数。该宏默认是空操作——必须自行定义(本示例在 lwipopts_user.h 中定义,写入 g_sntp_time),否则读不到时间。
参数:
sec:Unix 秒数(自 1970-01-01 UTC 起)
返回值:无
完整代码
以下为自编示例 main.c(在 SDK wifi_tcp 骨架上编写,非 SDK 文件;g_sntp_time 声明与 lwipopts 追加内容见操作步骤),默认折叠,点击展开:
📜 点击展开 自编 sntp main.c 完整代码
/*
* 自编示例:通过 lwIP SNTP 客户端获取网络时间
* 使用方法:
* 1. 用本文件覆盖 examples/wifi/sta/wifi_tcp/main.c;
* 2. 在 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"
/* SNTP 同步成功后写入的时间(Unix 秒) */
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)
{
/* 等待 Wi-Fi 拿到 IP */
while (!wifi_state) {
vTaskDelay(pdMS_TO_TICKS(200));
}
/* 配置并启动 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
一直打印 waiting for sync
确认 Wi-Fi 已拿到 IP(日志显示 Got IP, starting SNTP);公共 NTP 服务器偶尔会丢 UDP 包,重试几次(默认 15 秒超时)通常能成功;也可以换 pool.ntp.org 或公司内网 NTP 服务器。
打印的时间是 1970
说明 g_sntp_time 仍为 0:检查 lwipopts_user.h 里的 SNTP_SET_SYSTEM_TIME 是否真的追加并重新编译;该宏必须在 sntp 头文件处理前定义(lwipopts 先于 lwIP 处理,满足此条件)。
时间比北京时间慢 8 小时
SNTP 返回的是 UTC,北京是 UTC+8。示例直接打印 UTC;打印前加 8 小时(t += 8 * 3600),或使用时区库转换。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

