ESP-IDF 6.0.1 SNTP 同步超时:DNS 竟然是罪魁祸首
ESP32 SNTP 时钟同步问题排查手册
背景
ESP32-S3 通过 WiFi 连接互联网后,需要从 NTP 服务器同步系统时间。项目使用 ESP-IDF 6.0.1 框架,目标是为日志记录、定时冻结等功能提供准确时间。
问题现象
I (4524) lua_demo: Initializing and starting SNTP...
I (4704) lua_demo: Waiting for system time to be set... (1/15)
I (6704) lua_demo: Waiting for system time to be set... (2/15)
...
I (32670) lua_demo: Waiting for system time to be set... (15/15)
W (32680) lua_demo: SNTP sync timeout, using system default time
SNTP 连续 15 次(每次 2 秒)重试全部超时,最终使用 1970 年默认时间。
排查过程
第 1 轮:怀疑 NTP 服务器不可达
尝试:换用不同 NTP 服务器域名
ntp.aliyun.compool.ntp.orgtime.google.com
结果:全部超时。PC 端 ping ntp.aliyun.com 正常,说明服务器在线。
第 2 轮:怀疑 SNTP API 用法不对
ESP-IDF 6.0.1 引入了新 API esp_netif_sntp_init() 替代旧的 esp_sntp_init()。
尝试过的方法(全部失败):
| 方法 | 结果 |
|---|---|
esp_sntp_init() + 手写轮询 |
超时 |
增加 vTaskDelay(3000) 等 DNS 就绪 |
超时 |
sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED) 立即同步 |
API 不兼容 |
CONFIG_LWIP_SNTP_UPDATE_DELAY 1h→15s |
超时 |
CONFIG_LWIP_SNTP_STARTUP_DELAY=n 去掉启动延迟 |
超时 |
正确 API(IDF 6.0.1):
#include "esp_netif_sntp.h"
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(3,
ESP_SNTP_SERVER_LIST("ntp.aliyun.com", "pool.ntp.org", "time.google.com"));
esp_netif_sntp_init(&config);
int retry = 0;
while (esp_netif_sntp_sync_wait(pdMS_TO_TICKS(2000)) == ESP_ERR_TIMEOUT
&& ++retry < 15) {
ESP_LOGI(TAG, "Waiting... (%d/15)", retry);
}
易错点:宏参数是
ESP_SNTP_SERVER_LIST(s1, s2, s3)一个列表,不是ESP_SNTP_SERVER(s1), ESP_SNTP_SERVER(s2)...,后者编译报错。
第 3 轮:怀疑 IPv6 干扰
ESP32 同时拿到 IPv4 和 IPv6 地址,NTP 请求可能走 IPv6 不通。
尝试:
config.ip_family = AF_INET→esp_sntp_config_t没有此字段,编译报错- NTP 域名换成 IPv4 地址直连 → 仍然超时!
第 4 轮:关键突破 — DNS 日志
在 SNTP 等待循环中加 DNS 日志:
const ip_addr_t *dns = dns_getserver(0);
ESP_LOGI("DNS_CHECK", "Current DNS Server: %s", ipaddr_ntoa(dns));
发现:
DNS_CHECK: Current DNS Server: 192.168.137.1
根因确认:路由器的 DNS(192.168.137.1)无法解析 NTP 域名!
虽然同设备用这个 DNS 可以访问 39.96.206.90:1883(MQTT,固定 IP)和 39.96.206.90:40071(文件下载),但 NTP 域名解析一直失败。
根本原因
┌─────────────────────────────────────────────────────────┐
│ DNS 解析链路 │
├─────────────────────────────────────────────────────────┤
│ │
│ ESP32 ──DHCP──→ 路由器 DNS (192.168.137.1) │
│ │ │
│ ├─ 39.96.206.90 (MQTT, 固定IP) ── ✅ │
│ ├─ 39.96.206.90 (FileBrowser) ──── ✅ │
│ └─ ntp.aliyun.com (需DNS) ──────── ❌ │
│ │
│ 路由器 DNS 不转发或不支持 NTP 域名解析 │
│ │
└─────────────────────────────────────────────────────────┘
最终解决方案
在 esp_netif_sntp_init() 之前,用 dns_setserver() 强行替换 DNS 为公共 DNS:
#include "lwip/dns.h"
// 在 esp_netif_sntp_init(&config) 之前执行
ip_addr_t dns_server;
ipaddr_aton("119.29.29.29", &dns_server); // 腾讯公共 DNS
dns_setserver(0, &dns_server);
// 验证
const ip_addr_t *dns = dns_getserver(0);
ESP_LOGI("DNS_CHECK", "DNS Server: %s", ipaddr_ntoa(dns));
结果:
I (4524) lua_demo: Initializing and starting SNTP...
I (5300) DNS_CHECK: DNS Server: 119.29.29.29
I (6300) lua_demo: SNTP synced: 2026-05-22 14:30:15 CST
✅ 2 秒内同步成功!
关键知识点
1. ESP32 DNS 来源
WiFi DHCP 过程自动获取 DNS,默认走路由器:
192.168.137.1 (路由器) → 路由器自己的 DNS 上游
2. dns_setserver() 的优先级
dns_setserver(0, ...) ← 最高优先级,覆盖 DHCP 分配
dns_setserver(1, ...) ← 备用
dns_setserver(2, ...) ← 第二备用
注意:必须在
esp_netif_init()之后调用。
3. 常用国内公共 DNS
| DNS 服务器 | IP | 提供方 |
|---|---|---|
| 腾讯 DNS | 119.29.29.29 |
腾讯云 DNSPod |
| 阿里 DNS | 223.5.5.5 |
阿里云 |
| 百度 DNS | 180.76.76.76 |
百度 |
| 114 DNS | 114.114.114.114 |
南京信风 |
| 谷歌 DNS | 8.8.8.8 |
Google (可能慢) |
4. ESP-IDF 6.0.1 SNTP 正确 API
// ✅ 正确:新 API
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(n,
ESP_SNTP_SERVER_LIST("s1", "s2", "s3"));
esp_netif_sntp_init(&config);
esp_netif_sntp_sync_wait(timeout);
// ❌ 不要参考 v5.x 旧代码
esp_sntp_init();
sntp_get_sync_status();
排查方法论总结
| 排查步骤 | 方法 | 本次结果 |
|---|---|---|
| 1. 确认服务器可达 | PC 端 ping/测试 | ✅ 通 |
| 2. 确认 DNS 正常 | 加日志打印 dns_getserver() |
❌ 发现是路由器 DNS |
| 3. 排除代码问题 | 参考官方示例 | ✅ API 正确 |
| 4. 确认网络层 | MQTT/HTTP 同服务器正常 | ✅ IP 通,DNS 问题 |
| 5. 替换 DNS 源 | dns_setserver() |
✅ 解决 |
核心教训:嵌入式网络问题不要只换 NTP 服务器,先用
dns_getserver()看看实际 DNS 是谁。
完整代码
#include "esp_netif_sntp.h"
#include "lwip/dns.h"
static void sntp_time_sync(void)
{
setenv("TZ", "CST-8", 1);
tzset();
ESP_LOGI(TAG, "Timezone set: CST-8 (UTC+8)");
vTaskDelay(pdMS_TO_TICKS(3000));
// ★ 关键:替换为公共 DNS
ip_addr_t dns_server;
ipaddr_aton("119.29.29.29", &dns_server);
dns_setserver(0, &dns_server);
ESP_LOGI(TAG, "Initializing and starting SNTP...");
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(3,
ESP_SNTP_SERVER_LIST("ntp.aliyun.com", "pool.ntp.org", "time.google.com"));
esp_netif_sntp_init(&config);
int retry = 0;
const int retry_count = 15;
while (esp_netif_sntp_sync_wait(pdMS_TO_TICKS(2000)) == ESP_ERR_TIMEOUT
&& ++retry < retry_count) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
}
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
if (timeinfo.tm_year > (2016 - 1900)) {
ESP_LOGI(TAG, "SNTP synced: %04d-%02d-%02d %02d:%02d:%02d CST",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
} else {
ESP_LOGW(TAG, "SNTP sync timeout, using system default time");
}
}
参考
- ESP-IDF SNTP 官方示例
esp_netif_sntp.h源码:components/esp_netif/include/esp_netif_sntp.hlwip/dns.h源码:components/lwip/lwip/src/include/lwip/dns.h
浙公网安备 33010602011771号