ESP32 工程实践:读取复位原因,区分掉电、看门狗与软件重启
设备突然重启时,直接扩大任务栈或关闭看门狗容易掩盖问题。更有效的起点是保存完整串口日志,读取上次复位原因,再结合重启前正在执行的操作缩小范围。
本文示例面向 Arduino-ESP32 3.x,使用底层 ESP-IDF 系统接口。示例未完成实机验证;不同芯片和 SDK 版本的复位分类及启动日志可能不同。
读取系统报告
#include <Arduino.h>
#include <esp_system.h>
const char* reasonName(esp_reset_reason_t reason) {
switch (reason) {
case ESP_RST_POWERON: return "power-on";
case ESP_RST_EXT: return "external reset";
case ESP_RST_SW: return "software restart";
case ESP_RST_PANIC: return "panic / exception";
case ESP_RST_INT_WDT: return "interrupt watchdog";
case ESP_RST_TASK_WDT: return "task watchdog";
case ESP_RST_WDT: return "other watchdog";
case ESP_RST_DEEPSLEEP:return "deep-sleep wake";
case ESP_RST_BROWNOUT: return "brownout";
default: return "unknown / other";
}
}
void setup() {
Serial.begin(115200);
delay(300);
esp_reset_reason_t reason = esp_reset_reason();
Serial.printf("reset=%d (%s), sdk=%s\n",
int(reason), reasonName(reason), ESP.getSdkVersion());
}
void loop() {
delay(1000);
}
这个值是系统对最近一次复位的分类,并不是完整的故障根因。供电突然消失后再次上电,可能只报告 power-on;没有 brownout 记录不能证明供电一定正常。
按证据选择排查路径
| 观察结果 | 优先检查 | 后续证据 |
|---|---|---|
| brownout 或开启无线电时重启 | 电源瞬态、线材、稳压器和共地 | 板端电压波形、独立电源对照 |
| panic 或 Guru Meditation | 越界、空指针、资源生命周期 | 完整回溯与对应固件 ELF |
| task watchdog | 任务长期不让出、死循环、阻塞调用 | 被报告任务、执行路径和耗时 |
| interrupt watchdog | 长临界区、关中断时间过长 | 中断处理与临界区代码 |
| software restart | 主动调用重启或升级流程 | 重启前的明确原因日志 |
| deep-sleep wake | 预期休眠唤醒 | 唤醒源与休眠策略 |
串口监视器打开时,部分开发板会因 DTR/RTS 自动复位。调试时先确认重启是否由连接串口或烧录工具引起。使用原生 USB 的板卡还要考虑重启时端口重新枚举导致的日志缺失。
保存能解码的崩溃证据
保留从异常首行到回溯结束的日志,以及当次构建的 ELF 文件、源码版本、编译配置和 SDK 版本。重新编译后的 ELF 可能与现场地址不匹配,不能仅凭相同工程名就用来解码。
使用 ESP-IDF 的项目可通过 idf.py monitor 配合当前构建解码地址;Arduino 项目则需使用与工具链和芯片匹配的异常解码方式。只有数字地址、没有对应固件,往往难以定位源码。
如何加入启动计数
需要区分短时间内反复重启时,可添加启动计数和最后操作阶段。RTC 内存适合部分跨复位场景,但它不是可靠的断电持久存储;NVS 可以保存持久状态,却不宜在主循环高频写入。
实际项目应限制写入频率,并明确状态何时提交。遇到连续启动失败,可进入简化模式,关闭可选外设并等待维护,避免无限重启让设备彻底无法管理。
可重复的验证方法
先观察正常冷启动,再通过明确的维护入口调用一次 ESP.restart(),比较复位原因。若产品使用深睡,再验证定时唤醒路径。不要在有重要数据的设备上故意制造异常或欠压。
排查供电时,依次移除大电流外设、缩短电源线、替换可靠电源,并一次只改变一个条件。万用表通常看不到很短的跌落;持续故障可能需要示波器观察模块供电端。
浙公网安备 33010602011771号