ESP32-S3 AMOLED 手表联网实录:从串口识别到安全写入 Wi‑Fi 配置
一块已经刷好固件、但缺少源码和说明的 ESP32-S3 手表,接到电脑后能看到界面,却一直无法联网。本文记录一次完整的现场排查:识别硬件、读取启动日志、确认分区布局、定位网络名称问题,并在不覆盖原厂应用的前提下写入 Wi‑Fi 配置。
隐私说明:本文已隐藏真实密码、设备 MAC 地址和内网 IP;SSID 也使用示例名称代替。
一、先确认电脑到底识别到了什么
手表通过 USB 连接 macOS 后,系统识别出乐鑫原生 USB JTAG/Serial 设备,并生成串口:
/dev/cu.usbmodem****
使用 esptool 只读查询芯片信息,确认硬件为:
- 芯片:ESP32-S3,revision v0.2
- 无线能力:2.4 GHz Wi‑Fi + BLE
- Flash:32 MB
- PSRAM:8 MB
- 晶振:40 MHz
这里最重要的是先保持“只读”:在不清楚板上内容之前,不执行整片擦除,也不直接刷入新的 bootloader 或分区表。
二、从启动日志识别现有系统
以 115200 波特率监听串口并复位设备,可以得到完整启动信息。当前手表运行的是:
Project name: phone_s3_box_3
App version: v0.4.2-92-g5c6be6c-dirty
ESP-IDF: v5.5.1-dirty
硬件和应用日志进一步表明,这是 Waveshare ESP32-S3-Touch-AMOLED-2.06 上运行的 ESP-Brookesia Phone Demo,使用默认深色主题。启动过程中:
- AMOLED 屏幕和背光正常;
- 触摸设备正常;
- QMI8658 IMU 初始化成功;
- ES8311 / ES7210 音频链路初始化成功;
- SPIFFS 挂载成功,约使用 2 MB;
- 图库发现了
image1.bin、image2.bin、image3.bin; - SD 卡挂载失败,说明当时未插卡或卡未被识别。
串口每秒出现一次下面的警告:
wifi: Haven't to connect to a suitable AP now!
这不是程序崩溃,而是 Wi‑Fi 驱动在尚未连接接入点时查询 AP 信息产生的提示。
三、读取分区表,避免覆盖原固件
设备使用的分区布局如下:
| 分区 | 地址 | 大小 | 用途 |
|---|---|---|---|
nvs | 0x9000 | 24 KB | Wi‑Fi 与应用配置 |
otadata | 0xF000 | 8 KB | OTA 启动状态 |
model | 0x12000 | 952 KB | 模型/资源数据 |
factory | 0x100000 | 9 MB | 当前原厂应用 |
ota_0 | 0xA00000 | 6 MB | OTA 备用槽 0 |
ota_1 | 0x1000000 | 6 MB | OTA 备用槽 1 |
storage | 0x1600000 | 6 MB | SPIFFS 用户资源 |
otadata 最初为空,因此设备默认从 factory 分区启动。这一点给了我们一个安全方案:保留原厂应用不动,只把一次性配置程序写入 ota_0。
四、先备份并检查 NVS
读取 24 KB 的 NVS 分区并用 ESP-IDF 的 NVS 工具解析后,可以看到:
storage:wifi_en = 1,Wi‑Fi 开关已经启用;nvs.net80211命名空间存在;- 但没有保存
sta.ssid和sta.pswd。
因此问题不是“密码错误后反复重连”,而是设备还没有保存过可用的 Wi‑Fi 凭据。
动手写入前先保存 NVS 备份。这样即使后续配置异常,也可以恢复原有的射频校准和应用设置。
五、用一次性 OTA 程序写入配置
由于现有固件没有开放串口命令行,直接从电脑发送 wifi connect 一类命令行不通。最终采用一次性 OTA 配置程序:
- 编译一个最小 ESP-IDF 应用;
- 只写入
ota_0,不改动factory; - 临时切换到
ota_0启动; - 通过 USB Serial/JTAG 接收 SSID 和密码;
- 调用
esp_wifi_set_storage(WIFI_STORAGE_FLASH)与esp_wifi_set_config(),把配置写入 NVS; - 等待 DHCP 分配地址;
- 成功或超时后调用
esp_ota_set_boot_partition(factory),清除临时 OTA 启动状态并返回原系统。
核心配置逻辑可以概括为:
wifi_init_config_t init = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&init));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
wifi_config_t config = {0};
memcpy(config.sta.ssid, ssid, strlen(ssid));
memcpy(config.sta.password, password, strlen(password));
config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_connect());
临时程序不把密码写进源码或构建命令,而是在运行时通过串口接收,并在完成配置后清零内存中的密码缓冲区。
六、真正的坑:口头简称不等于广播 SSID
第一次测试使用了用户口头提供的简称,例如 71DA。配置能够正常写入,但 30 秒内始终无法获得 IP。
随后检查电脑周围的无线网络,发现实际广播名称是类似:
Example_71DA
它是信道 1 上的 2.4 GHz 网络,使用 WPA/WPA2 Personal,信号也足够强。ESP32-S3 不支持 5 GHz,但这次并不是频段问题,而是 SSID 少了前缀。
改用完整广播名称后,串口立即返回:
WIFI_CONFIG_OK IP=192.168.*.*
RETURN_FACTORY=ESP_OK
手表随后自动重启回原来的 Brookesia 界面。再次读取 NVS,可以确认:
nvs.net80211:opmode = 1
storage:wifi_en = 1
nvs.net80211:sta.ssid = <已脱敏的完整 SSID>
至此,Wi‑Fi 配置已经持久化。
七、这次操作中值得保留的安全习惯
1. 先读分区表,再决定写哪里
不要根据常见 ESP32 地址猜测。不同项目的 factory、OTA 和文件系统位置可能完全不同。
2. 原厂分区保持不动
把临时工具放进闲置 OTA 槽,即使工具出现问题,也能通过清除 otadata 回到 factory。
3. 写入前备份 NVS
NVS 不只保存 SSID 和密码,还可能包含射频校准、应用开关和其他设备状态。
4. 不在日志中回显密码
串口日志、终端历史、博客截图和构建参数都可能泄露凭据。公开记录时,应同时隐藏密码、MAC 地址、内网 IP 和真实 SSID。
5. “连接成功”必须以获得 IP 为准
仅看到 Wi‑Fi 驱动启动,或仅看到 SSID 写入 NVS,都不能证明联网成功。至少应等待 IP_EVENT_STA_GOT_IP。
八、结论
面对一块“能亮、能操作、但没有工程源码”的 ESP32 设备,最稳妥的排查顺序是:
识别 USB 设备
→ 读取芯片信息
→ 捕获完整启动日志
→ 读取分区表
→ 备份并解析 NVS
→ 在备用 OTA 槽运行一次性工具
→ 验证获得 IP
→ 自动返回原厂应用
这套方法的重点不是“把 Wi‑Fi 密码塞进去”,而是在不知道设备全部历史的情况下,把写操作限制在最小范围,并为失败预留明确的恢复路径。