ESP8266 闪存必须在每次崩溃后完全擦除才能再次使用

ESP8266 flash memory has to be completely erased after every crash to be usable again

我有一个 ESP8266 D1 mini,我在上面编写了一个程序来获取加密货币价格并在小型 OLED 显示屏上显示。它使用库 ESP8266WiFi、ESP8266HTTPClient、EEPROM、pgmspace、Wire、ArduinoJson、oled 和 ESPAsyncWebServer。用户配置信息存储在EEPROM中。

某处存在问题,可能会导致程序偶尔崩溃,可能在连续运行 30 或 40 小时后。发生这种情况时,处理器重新启动,开始 运行 执行 setup() 函数,然后给出异常 (3)。然后 reset/exception 会再次发生,一遍又一遍,直到设备断电。如果设备恢复供电,相同的 reset/exception 序列将继续,无限循环。

将程序重新载入闪存并不能解决问题。即使是简单的 Blink 素描也不会 运行。事实上,我发现让设备再次可用的唯一方法是使用 esptoolcompletely 擦除闪存,然后重新加载程序。这当然也会清除所有用户配置信息。

对重复异常的分析表明这是错误:

LoadStoreError: Processor internal physical address or data error during load or store  epc1=0x40100ac8 in umm_malloc_core at /home/david/.platformio/packages/framework-arduinoespressif8266@3.20704.0/cores/esp8266/umm_malloc/umm_malloc.cpp:433

到目前为止,这已经发生了几十次,我有很多打印语句显示程序中不同点的堆栈和堆大小,none 其中似乎提供了任何线索坠机的原因。展开堆栈也毫无意义,因为异常解码器会为许多程序位置显示问号。靠近堆栈底部的是我的代码中触发整个崩溃的调用 - Print::println(__FlashStringHelper const)* 语句在这次崩溃之前完美运行发生。有趣的是,我的程序在崩溃分析中显示的位置没有 println 语句。

我想,通过足够的努力,我可以找出导致破坏闪存内容的原始崩溃的原因。我不明白的是它怎么会如此糟糕以至于我什至无法通过重新加载程序来修复它——在我让设备再次工作之前必须擦除整个闪存。我试过更换设备,认为可能是硬件问题,但新设备也因此失败。我在这里错过了什么?

问题

这可能是由堆碎片引起的,其中每次变量大小发生变化时都会产生空洞,特别是 String,直到空闲内存为零:


观察

您可以使用以下方式监控 FreeHeap:

 Serial.println(ESP.getFreeHeap());

运行 它在循环中观察可用内存。


解决方案

在 loop() 函数中定期调用 tcpCleanup。

// do this before including other stuff like wificlient.h
#include "lwip/tcp_impl.h"

void tcpCleanup()
{
  while(tcp_tw_pcbs!=NULL)
  {
    tcp_abort(tcp_tw_pcbs);
  }
}

原来有两个问题:我将文本复制到其中的 9 个字符的缓冲区长度为 10 个字符(旧的零终止符),我错误地使用了 send_p 而不是发送用于发送网页。为什么这些新手错误导致这种行为超出了我的理解,但是在修复这些问题两个月后,错误没有再次发生。