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

ESP32-WROVER-IE-N8R8 MQTT传感器运行超100天后死机求助

ESP32-WROVER-IE-N8R8运行100多天后代码完全停止,看门狗未触发

问题背景

  • 硬件:定制PCB搭载ESP32-WROVER-IE-N8R8,用于读取过滤系统压差数据
  • 功能:每分钟通过MQTT将数据发送至Linux服务器的Mosquitto Broker
  • 开发环境:Arduino IDE,代码含德语注释/字符串
  • 故障:所有设备运行100多天后均出现代码完全停止的情况,无任何串口输出
  • 已尝试措施:实现millis()溢出自动重启(约40-50天触发),启用任务级看门狗(WDT_TIMEOUT=90秒),但看门狗未触发重启;添加大量串口输出无法定位故障点,测试周期过长(每年仅能验证3种方案)

代码片段

#include <WiFiManager.h>    
#include <WiFi.h>           
#include <PubSubClient.h>   
#include <Wire.h>           
#include <esp_task_wdt.h>   

#define TRIGGER_PIN 33
#define LEDError 14
#define LEDConnect 5
#define LEDWiFi 15
#define WDT_TIMEOUT 90      

#define SensorName "Filtersensor 9"
#define SensorLogin "fs-09"
#define SensorPasswort "xxx"
#define SensorPublish "xxx"
#define SensorStatus "Status_FS_09"

WiFiManager wm;              
char Status[30];            

//I2C 
byte ADCAddress = 0x48;      
const int ADCref =  4096;    
float ADCvoltage;            
float Druck;                 

//MQTT 
const char* mqtt_server = "RandomIP";       
WiFiClient espClient;                           
PubSubClient client(espClient);                 
unsigned long lastMsg = 0;                      
char msg[50];                                   

void setup() {
  Wire.begin();                                 
  Serial.begin(115200);
  delay(100);
  pinMode(LEDError, OUTPUT);
  pinMode(LEDConnect, OUTPUT);
  pinMode(LEDWiFi, OUTPUT);

  wm.setHostname(SensorLogin);        
  WiFi.mode(WIFI_STA);
  Serial.setDebugOutput(true);  
  delay(3000);
  Serial.println("\n Starte...");
  pinMode(TRIGGER_PIN, INPUT);

  //WiFi连接
  bool res;
  res = wm.autoConnect(SensorName,"password");                     
  if(!res) {                                                       
    Serial.println("Verbindung fehlgeschalgen oder Time-Out");
    digitalWrite(LEDError, HIGH);
    Serial.println("Neustarten...");
    delay(10000);
    ESP.restart();
  } 
  else {    
    Serial.println("WiFi verbunden!");                              
    digitalWrite(LEDWiFi, HIGH);
  }

  //MQTT设置
  client.setServer(mqtt_server, 1883);       

  //看门狗初始化
  Serial.println("Einrichtung des WDT...");
  esp_task_wdt_init(WDT_TIMEOUT, true);       
  esp_task_wdt_add(NULL);                    
}

void checkButton(){
  if ( digitalRead(TRIGGER_PIN) == LOW ) {
    delay(50);
    if( digitalRead(TRIGGER_PIN) == LOW ){
      Serial.println("WiFi Button gedrückt");
      delay(3000);
      if( digitalRead(TRIGGER_PIN) == LOW ){
        Serial.println("Button gehalten");
        Serial.println("Lösche Config, Starte neu....");
        wm.resetSettings();                                        
        digitalWrite(LEDWiFi, LOW);
        delay(5000);
        ESP.restart();
      }
      
      esp_task_wdt_reset();                                          
      Serial.println("Starte Wifi-Konfiguration...");
      wm.setConfigPortalTimeout(120);
      
      if (!wm.startConfigPortal(SensorName,"password")) {             
        Serial.println("Verbindung fehlgeschalgen oder Time-Out");
        delay(3000);
        digitalWrite(LEDError, HIGH);
        Serial.println("Neustarten...");
        delay(10000);
        ESP.restart();
      } else {
        Serial.println("WiFi verbunden!");
        digitalWrite(LEDWiFi, HIGH);
      }
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Versuche MQTT-Verbindung herzustellen...");
    if (client.connect(SensorName, SensorLogin, SensorPasswort)) {         
      Serial.println("Verbunden!");
      digitalWrite(LEDConnect, HIGH);
    } else {
      Serial.print("Fehlgeschlagen!, rc=");
      Serial.print(client.state());
      Serial.println(" Neuer Versuch in 5 Sekunden");
      digitalWrite(LEDConnect, LOW);
      delay(5000);                                                          
    }
  }
}

void Sensor() {
  uint16_t ADCresult;
  Wire.requestFrom(ADCAddress, (uint8_t) 2);
  if (Wire.available()) 
  {
    ADCresult = Wire.read();
    ADCresult = ADCresult<<8;
    ADCresult += Wire.read();
    ADCvoltage = (ADCresult*(ADCref/4095));
    Serial.print("Spannung ADC: ");
    Serial.print(ADCvoltage);
    Serial.println(" mV");

    ADCvoltage /= 1000;
    Druck = ((500.0*(ADCvoltage-0.25))/3.75);
    Druck /= 100;
    Serial.print("Differenzdruck: ");
    Serial.print(Druck,3);
    Serial.println(" mbar");
    char DruckMessage[8];

    dtostrf(Druck, 1, 3, DruckMessage);
    strcat (Status," SENT");
    
    if (client.connected()) {  
      esp_task_wdt_reset();                       
      client.publish(SensorPublish, DruckMessage);                                                              
      delay(100);
      client.publish(SensorStatus, Status);
      digitalWrite(LEDConnect, LOW);
      digitalWrite(LEDError, LOW);
      delay(500);
      digitalWrite(LEDConnect, HIGH);
      Serial.print("Gesendete Daten: ");
      Serial.println(DruckMessage);
    }
  }
  else{
    digitalWrite(LEDError, HIGH);
    strcat (Status," ERROR");
    if (client.connected()) {  
      client.publish(SensorStatus, Status); 
    }    
    Serial.println("Keine Verbindung zum ADC herstellbar");
  }
}

void Neustart() {
  Serial.println("Regelmäßiger Neustart wird ausgeführt");
  char StatusMessage[10] ="RESTART";
  client.publish(SensorStatus, StatusMessage);
  delay(500);
  ESP.restart();
}

void getReadableTime(String &readableTime) {
  unsigned long currentMillis;
  unsigned long seconds;
  unsigned long minutes;
  unsigned long hours;
  unsigned long days;

  currentMillis = millis();
  seconds = currentMillis / 1000;
  minutes = seconds / 60;
  hours = minutes / 60;
  days = hours / 24;
  currentMillis %= 1000;
  seconds %= 60;
  minutes %= 60;
  hours %= 24;

  if (days < 10) {
      readableTime += "0";
    }
  readableTime = String(days) + "D ";

  if (hours < 10) {
      readableTime += "0";
    }
  readableTime += String(hours) + "H ";

  if (minutes < 10) {
      readableTime += "0";
    }
  readableTime += String(minutes) + "M ";
}

void loop() {
  checkButton();
 if (!client.connected()) {                     
    digitalWrite(LEDConnect, LOW);
    reconnect();
  }
  client.loop();

  unsigned long now = millis();
  if (now > 86400000) { 
    Neustart();  
  }

  if (now - lastMsg > 60000) {
    lastMsg = now;
    String readableTime;
    getReadableTime(readableTime);
    Serial.print("Laufzeit: ");
    Serial.println(readableTime);
    readableTime.toCharArray(Status, 20);
    
    Sensor();
  }
}

关键问题分析及解决方案

1. Status数组内存越界(最可能的崩溃原因)

代码中Status是固定长度30字节的数组,每次循环执行readableTime.toCharArray(Status,20)后,直接调用strcat(Status," SENT")或strcat(Status," ERROR"),但未清空数组旧内容。多次循环后,Status会累积字符串(比如变成"01D 02H 03M SENT SENT SENT..."),最终超过30字节的容量,导致内存溢出,破坏系统栈或堆,引发无预兆崩溃。

修复方案:
在readableTime.toCharArray(Status,20)前添加数组清空操作:

// 清空Status数组
memset(Status, 0, sizeof(Status));
readableTime.toCharArray(Status,20);

2. 看门狗未覆盖阻塞场景

当前仅在Sensor()和checkButton()中调用esp_task_wdt_reset(),但reconnect()函数中的while循环如果遇到MQTT长期无法连接,会持续执行delay(5000),主任务会被阻塞在该循环中,超过90秒看门狗超时时间却未触发重启——因为循环内没有喂狗操作。

修复方案:
在reconnect()的循环内添加看门狗喂狗:

void reconnect() {
  while (!client.connected()) {
    esp_task_wdt_reset(); // 添加喂狗
    Serial.print("Versuche MQTT-Verbindung herzustellen...");
    if (client.connect(SensorName, SensorLogin, SensorPasswort)) {         
      Serial.println("Verbunden!");
      digitalWrite(LEDConnect, HIGH);
    } else {
      Serial.print("Fehlgeschlagen!, rc=");
      Serial.print(client.state());
      Serial.println(" Neuer Versuch in 5 Sekunden");
      digitalWrite(LEDConnect, LOW);
      delay(5000);                                                          
    }
  }
}

同时建议改用硬件看门狗替代任务级看门狗,避免任务阻塞导致喂狗失效:

// 替换原看门狗初始化代码
#include <esp_system.h>
void setup() {
  // ...其他代码
  // 启用硬件看门狗,超时300秒
  esp_wdt_enable(300000);
}

// 在loop()末尾添加喂狗
void loop() {
  // ...其他代码
  esp_wdt_reset();
}

3. I2C通信无超时机制

Wire.requestFrom和Wire.read如果遇到I2C总线干扰或ADC模块故障,可能导致ESP32无限阻塞在I2C操作中,无法执行喂狗或其他代码。

修复方案:
在setup()中设置I2C超时:

void setup() {
  Wire.begin();
  Wire.setWireTimeout(500000); // 设置500ms超时
  // ...其他代码
}

同时检查Wire.requestFrom的返回值,替代仅判断Wire.available():

void Sensor() {
  uint16_t ADCresult;
  // 检查requestFrom的返回值,确认读取到2字节
  if (Wire.requestFrom(ADCAddress, (uint8_t)2) == 2) 
  {
    ADCresult = Wire.read();
    ADCresult = ADCresult<<8;
    ADCresult += Wire.read();
    // ...后续代码
  }
  else{
    digitalWrite(LEDError, HIGH);
    // ...错误处理代码
  }
}

4. 内存泄漏与库稳定性

长期运行中,WiFi/MQTT库可能存在内存泄漏,建议:

  • 更新WiFiManager、PubSubClient到最新版本,修复已知内存泄漏问题
  • 在每日重启的Neustart()函数中,先断开MQTT和WiFi连接,避免残留资源:
void Neustart() {
  Serial.println("Regelmäßiger Neustart wird ausgeführt");
  char StatusMessage[10] ="RESTART";
  if(client.connected()){
    client.publish(SensorStatus, StatusMessage);
    client.disconnect();
  }
  WiFi.disconnect(true);
  delay(500);
  ESP.restart();
}

5. 硬件电源稳定性检查

定制PCB需确保ESP32供电长期稳定:

  • 验证3.3V供电纹波小于100mV,电压范围在3.135V-3.465V之间
  • 在ESP32的VCC引脚旁添加100nF陶瓷电容+10µF电解电容做滤波
  • 检查电源模块的散热设计,避免长期运行过热老化

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 12:14:53