【ESP32S3】调用百度云语音合成接口后重启
下面的内容是在 https://gitee.com/likexiang/like-code/blob/dev/ESP32-S3-CAM/FromTheBeginning/WifiAndBaiduTTSAandMax98357.py的基础上分析。
一、报错现象
核心报错(Task Watchdog Timeout):
播放完成,总数据量: 158.44 KB
E (62104) task_wdt: Task watchdog got triggered.
- IDLE0 (CPU 0)
E (62104) task_wdt: CPU 0: loopTask
E (62104) task_wdt: CPU 1: IDLE1
E (62104) task_wdt: Aborting.
规律: 播放音频后过一会就重启;多次调用 /dht/speak 也会重启。
二、根因分析
| # | 根因 | 位置 | 影响 |
|---|---|---|---|
| 1 | loopTask 被播放阻塞 | loop() 直接调 playNetworkWav() |
5~15 秒的 HTTPS+I2S 播放独占 CPU0,IDLE0 饿死,TWDT 触发 |
| 2 | DHT 读取阻塞 HTTP 回调 | readDHT22() 内 delay(2000) |
/dht /dht/speak 阻塞 2 秒,进一步加剧 CPU0 饿死 |
| 3 | 历史遗留的堆崩溃(已修复) | new WebServer/new DHT |
已改为静态全局对象,本次不再处理 |
关键认知: Arduino-ESP32 把 loopTask 固定绑在 CPU 0,而 IDLE0 必须在 CPU 0 上跑才能喂看门狗。任何在 loop 里跑长的阻塞操作都会触发重启。
三、修改方案
架构改造:CPU 分核隔离
CPU 0 (loopTask) CPU 1 (PlayTask)
├─ server.handleClient() ├─ xQueueReceive(阻塞等待)
├─ DHT 缓存刷新 (<10ms) └─ playNetworkWav()
└─ vTaskDelay(1) 喂狗 (HTTPS + I2S,可阻塞)
关键代码改动
① 新增播放任务,绑定 CPU 1
cpp
xTaskCreatePinnedToCore(playTask, "PlayTask", 12*1024, NULL, 2, &playTaskHandle, 1);
// 第 7 个参数 1 = 绑定到 CPU 1
② Queue 传递播放请求(替代旧 volatile 标志位)
cpp
QueueHandle_t playQueue = xQueueCreate(1, sizeof(String*));
void enqueuePlay(const String &text) {
String* pNew = new String(text);
xQueueSend(playQueue, &pNew, 0);
}
③ loop() 改为非阻塞
cpp
void loop() {
server.handleClient();
// DHT 缓存定时刷新(替代 readDHT22 里的 delay(2000))
if (millis() - lastDhtRead >= 3000) { ...cachedTemp = t + tempOffset; }
vTaskDelay(1); // 让 IDLE0 喂狗
}
④ HTTP 回调零阻塞
/dht→ 直接返回cachedTemp/cachedHum/dht/speak→ 读缓存 +enqueuePlay()/play→enqueuePlay()
四、验证结果
| 场景 | 修改前 | 修改后 |
|---|---|---|
| 播放后等待 | 几秒后重启 | 稳定运行 |
连续 /dht/speak |
第 2~3 次重启 | 稳定运行 |
/dht 响应 |
2~3 秒 | <10 ms |
| 播放期间 HTTP | 卡死 | 正常响应 |
五、经验教训
- Arduino-ESP32 的 loopTask 固定在 CPU 0,任何 >1 秒的阻塞操作都有 TWDT 风险。
- 耗时任务必须
xTaskCreatePinnedToCore(..., 1)丢到 CPU 1,CPU 0 留给 IDLE0 喂狗。 - HTTP 回调必须零阻塞,传感器类慢操作用 loop 定时缓存。
- 跨任务通信用 FreeRTOS Queue,比 volatile 标志位更安全(避免竞态)。
vTaskDelay(1)是给 IDLE 喂狗的最低保障,不能省。
一句话总结: 把耗时播放移到 CPU 1 独立任务,CPU 0 只做轻量 HTTP + DHT 缓存,从根本上解决 TWDT 重启。