You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()期间修改了该内存。

具体修复步骤

  1. 将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'; // 确保字符串以结束符结尾
    }
    
  2. 替换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后的代码移到这里执行
      }
    
      // 其他非阻塞任务代码...
    }
    
  3. 排查内存管理问题

    • 使用更精准的堆内存监控函数,区分内部堆和外部堆:
      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()),确保每个分配都有对应的释放,且没有野指针。
  4. 验证PubSubClient的内存使用
    确保初始化PubSubClient时传入的mqttServer是稳定的内存区域,避免在客户端运行期间修改该内存。同时检查setup_mqtt()函数中是否存在错误的内存操作,导致mqttServer被覆盖。

内容的提问来源于stack exchange,提问作者Shariq Azim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 05:09:55