为什么你的 ESP32 气象站在运行 24 小时后会崩溃(以及如何解决)

学校的 ESP32 物联网项目很少因为芯片硬件故障而崩溃。它们之所以死机,通常是因为动态堆内存碎片化、阻塞式的 Wi-Fi 重连循环以及未处理的硬件看门狗超时。
如果你的课堂 ESP32 气象站在第四节课的实验台上运行得非常完美,但在第二天早上却彻底黑屏死机,这并不是芯片有缺陷。微控制器不会在 24 小时内老化损坏。相反,长时间运行的嵌入式软件几乎总是由于两个隐蔽的累积性问题而崩溃:动态文本操作导致的堆内存碎片化,以及将处理器困在无限阻塞循环中的未处理网络断开。
解决这些问题需要摆脱快速原型的权宜之计,采用标准的嵌入式工程实践:固定大小的内存缓冲区、非阻塞式网络状态机以及硬件看门狗定时器。
罪魁祸首 1:动态字符串与堆碎片化
ESP32 拥有大约 320 KB 的可用内部 SRAM。在一个每十秒轮询一次传感器的气象站中,使用标准 Arduino String 类编写的代码每天会分配、调整大小和释放小块内存成千上万次:
// 悄悄破坏项目的典型写法:
String payload = "{\"temp\": ";
payload += String(temperature);
payload += ", \"humidity\": ";
payload += String(humidity);
payload += "}";
http.POST(payload);每次在 loop() 中创建并拼接动态字符串时,C++ 运行时都会在堆上分配空间。由于其他系统后台任务(例如 Wi-Fi 和蓝牙协议栈)也在同时分配内存,这种持续不断的分配与释放会让堆内存变得像瑞士奶酪一样千疮百孔。总空闲内存可能仍显示有 150 KB,但最大连续空闲内存块可能已经缩减到只有几百字节。当 Wi-Fi 协议栈或 JSON 序列化程序突然需要为 TCP 数据包申请一块完整的 2 KB 连续内存块时,内存分配就会失败,返回的空指针(null pointer)将触发无声的内核崩溃或未处理的异常死机。
解决方法:使用固定大小的栈缓冲区和 snprintf。 微控制器固件应分配在编译时就已知大小的静态或基于栈的缓冲区。
// 稳健、零堆内存分配的替代方案:
char payload[128];
snprintf(payload, sizeof(payload),
"{\"temp\":%.2f,\"humidity\":%.2f}",
temperature, humidity);
http.POST((uint8_t*)payload, strlen(payload));罪魁祸首 2:学校 Wi-Fi 重连死循环
在学校环境中,Wi-Fi 连接很少是一成不变的。接入点会定期更新安全令牌、DHCP 租约会过期,即使物联网气象站使用电池供电,当地停电或限电(load-shedding)切换也会导致上游路由器短暂掉线。许多入门教程建议这样重新连接 Wi-Fi:
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
while (WiFi.status() != WL_CONNECTED) {
delay(500); // 致命错误:无限期阻塞处理器
}
}如果路由器重启并花费两分钟才恢复局域网子网,这个 while 循环就会冻结程序执行。由于 FreeRTOS 在双核上运行 Wi-Fi 协议栈和系统管理任务,阻塞主循环会导致后台看门狗任务得不到调度,从而导致立即触发硬件复位或任务永久死锁。
解决方法:将网络连接处理为非阻塞状态机。 绝不要为了等待连接而暂停程序执行。如果 Wi-Fi 断开,应将数据暂存在本地 Flash 闪存或 SD 卡中,并在设定的时间间隔重试连接,同时不中断传感器的数据采集。
罪魁祸首 3:缺失看门狗定时器
无论你的代码写得多规范,室外传感器、附近压缩机产生的电涌干扰,或无响应的 I2C 总线(尤其是随处可见的 BME280 或 DHT22 传感器)都可能将 ESP32 拖入硬件死锁。如果传感器未能释放 I2C 时钟线,标准的 Wire 库就会无限期挂起。
任务看门狗定时器(TWDT)负责监控关键循环。如果主循环未能在指定的时间窗口(例如 8 秒)内“喂狗”或重置看门狗,ESP32 就会自动触发硬件硬重启,从而实现无人值守自愈。
#include <esp_task_wdt.h>
#define WDT_TIMEOUT_SECONDS 8
void setup() {
// 初始化看门狗,超时时间设为 8 秒
esp_task_wdt_init(WDT_TIMEOUT_SECONDS, true);
esp_task_wdt_add(NULL); // 注册当前线程
}
void loop() {
// 每个循环周期重置看门狗定时器(喂狗)
esp_task_wdt_reset();
readSensors();
sendTelemetry();
// 避免使用耗时较长的 delay();应使用非阻塞的 millis() 检查
}关于电源稳定性的注意事项
在认定故障纯属软件问题之前,请先检查硬件供电轨。ESP32 在 Wi-Fi 发送突发数据(射频校准和数据包突发传输)期间会产生高达 350–500 mA 的瞬态电流脉冲。如果开发板使用的是连接到笔记本电脑的劣质细 USB 线或廉价 5V 电源适配器供电,电压跌落至 2.7V 掉电(brownout)阈值以下时,内部掉电检测器就会触发突发性重启。
- 在
VIN和GND引脚之间并联一个 100 µF 至 470 µF 的电解电容,以吸收瞬态电流尖峰。 - 确保在开发框架中启用了掉电检测器而不是将其屏蔽,因为欠压会导致内部 Flash 闪存数据损坏。
如果你正在为环境科学或校园监测构建可实地部署的监测站,我们在 Sheen IoT 的课程工程团队提供了模块化硬件框架和可用于生产环境的固件设计模式,能帮你避开这些经典的课堂踩坑点。



