ESP32中delay引发堆内存异常导致数据损坏的解决咨询
ESP32中delay函数引发堆内存异常导致数据损坏问题
在ESP32项目中发现,delay()函数会引发空闲堆内存异常,间接导致数据损坏。在sketch的void loop()函数内,执行delay()后堆内存释放约4KB,且const char*类型的mqttServer变量打印出现乱码,原本预期delay()执行后能正常打印IP地址,但实际输出乱码。
相关代码(loop函数)
void loop() { Serial.print("Heap size start loop: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); Serial.println(mqttPort); //added in v0.5 fanPinChange = (bool)( (fanPin1 != digitalRead(s1) ) || (fanPin2 != digitalRead(s2) ) || (fanPin3 != digitalRead(s3) ) || (fanPin4 != digitalRead(s4) )); if (fanPin1 != digitalRead(s1) ) { fanPin1 = digitalRead(s1); } if (fanPin2 != digitalRead(s2) ) { fanPin2 = digitalRead(s2); } if (fanPin3 != digitalRead(s3) ) { fanPin3 = digitalRead(s3); } if (fanPin4 != digitalRead(s4) ) { fanPin4 = digitalRead(s4); } //v0.5 Serial.print("Heap size check loop1: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); Serial.println(WiFi.status()); Serial.print("Heap size check loop2: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); //no issues so far delay(1000);//notice from here Serial.print("Heap size check loop3: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); //prints gibberish/corrupted data //Check WiFi connection status Serial.print("Wifi status: "); if (WiFi.status() != WL_CONNECTED) {// you can skip to the below else part for now digitalWrite(R6, LOW); if (DEBUG_SW) Serial.println("Wifi Not Connected"); delay(400); //custom digitalWrite(R6, HIGH); delay(400); //custom Serial.print("Heap size check loop wifi off: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); } else { Serial.println("Wifi Connected"); Serial.print("Heap size after wifi connected and before delay: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); delay(1000); Serial.println("Wifi Connected"); Serial.print("Heap size after wifi connected: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); //prints gibberish/corrupted data //0.4 logic added //reconnect to mqtt or process if incoming string present if (pubsubclient.connected()) { Serial.println("connected with MQTT"); digitalWrite(R6, HIGH); Serial.print("Heap size check wifi on pubsub connected: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); pubsubclient.loop(); Serial.print("Heap size check LOOP wifi on pubsub connected: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); InternetStatus =true; Serial.print("Heap size check LOOP wifi on pubsub connected2: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); } else { Serial.print("Heap size after pubsub: "); Serial.println(ESP.getFreeHeap()); Serial.println(mqttServer); Serial.print("Reconnect to MQTT server failed with state "); Serial.println(pubsubclient.state()); digitalWrite(R6, LOW); InternetStatus = setup_mqtt(); } } Serial.print("Heap size before internetstatus: "); Serial.println(ESP.getFreeHeap()); //not important here if (InternetStatus) with_internet(); else without_internet(); Serial.print("Heap size on exit loop: "); Serial.println(ESP.getFreeHeap()); }
串口监视器打印内容
[IP removed in question] 3 Heap size check loop2: 252872 [IP removed in question] Heap size check loop3: 256044 ��?6� Wifi status: Wifi Connected Heap size after wifi connected and before delay: 256044 ��?6� Wifi Connected Heap size after wifi connected: 256044 ��?� connected with MQTT Heap size check wifi on pubsub connected: 256044 ��?� Heap size check LOOP wifi on pubsub connected: 256604
问题分析与解决方法
核心原因
mqttServer变量的内存被非法释放或覆盖,大概率是因为该变量指向的是堆内存中的临时数据(比如通过strdup()、WiFi.localIP().toString().c_str()这类方式获取的指针),而delay()函数会阻塞主线程,触发ESP32的空闲任务执行堆内存整理,可能误释放被错误引用的内存区域,或者其他代码在delay()期间修改了该内存。
具体修复步骤
将
mqttServer存储到静态/全局内存
不要直接用const char*指向临时堆内存,改用全局字符数组存储内容:// 定义全局数组,长度根据实际需求调整 char mqttServer[64]; // 在初始化阶段(比如setup()中)复制内容,示例: void setup() { // 假设从WiFi获取IP地址 IPAddress ip = WiFi.localIP(); strncpy(mqttServer, ip.toString().c_str(), sizeof(mqttServer)-1); mqttServer[sizeof(mqttServer)-1] = '\0'; // 确保字符串以结束符结尾 }替换
delay()为非阻塞延时delay()会阻塞主线程,空闲任务在阻塞期间的堆整理可能破坏内存引用。改用millis()实现非阻塞逻辑:unsigned long lastDelayTime = 0; const unsigned long delayInterval = 1000; void loop() { unsigned long currentTime = millis(); // 替代delay(1000)的非阻塞逻辑 if (currentTime - lastDelayTime >= delayInterval) { lastDelayTime = currentTime; // 原本放在delay后的代码移到这里执行 } // 其他非阻塞任务代码... }排查内存管理问题
- 使用更精准的堆内存监控函数,区分内部堆和外部堆:
Serial.print("Internal free heap: "); Serial.println(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); Serial.print("External free heap: "); Serial.println(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); - 检查所有动态内存分配操作(如
malloc()、strdup()),确保每个分配都有对应的释放,且没有野指针。
- 使用更精准的堆内存监控函数,区分内部堆和外部堆:
验证PubSubClient的内存使用
确保初始化PubSubClient时传入的mqttServer是稳定的内存区域,避免在客户端运行期间修改该内存。同时检查setup_mqtt()函数中是否存在错误的内存操作,导致mqttServer被覆盖。
内容的提问来源于stack exchange,提问作者Shariq Azim
相关产品推荐
相关产品推荐

