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

NodeMCU基于MQTT的按键控制代码运行数小时后无响应求助

嘿,我来帮你搞定这个NodeMCU跑几个小时就罢工的问题!这种情况在ESP8266的MQTT项目里太常见了,尤其是新手刚拼凑代码的时候,多半是连接稳定性或者内存的锅,咱们一步步来拆解解决:

最可能的几个问题点&修复方案

1. MQTT/WiFi断开后没有自动重连

新手写代码常犯的错:只在setup()里连一次WiFi和MQTT,一旦网络波动、路由器重启或者MQTT服务器临时离线,设备就彻底断联,再也不会自己连回去。

修复:在loop()里持续检查连接状态,断开就自动重试重连,甚至多次失败后重启设备。

2. 用了阻塞式代码(比如delay())

如果你的代码里有delay(),尤其是长时间的延时,会导致client.loop()和WiFi心跳得不到执行,时间久了就会被服务器踢下线,甚至设备假死。

修复:改用millis()实现非阻塞延时,保证loop()能循环跑起来。

3. 内存泄漏

ESP8266的内存本来就小,如果频繁用String类、动态创建对象没释放,或者Servo库使用不当,会导致内存越来越少,最后系统崩溃。

修复:尽量用char数组代替String,定期监控剩余内存,必要时重启设备释放内存。


修改后的完整示例代码

我把这些修复点整合到你的代码框架里了,你可以根据自己的硬件调整引脚和主题:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Servo.h>

// 配置信息
const char* ssid = "ap_name";
const char* password = "ap_pw";
const char* mqttServer = "server_ip";
const int mqttPort = 1883;
const char* mqttUser = "your_mqtt_user"; // 没有的话可以注释掉
const char* mqttPassword = "your_mqtt_pw"; // 没有的话可以注释掉

// 硬件引脚
const int buttonPin = D2;
const int servoPin = D1;

// 全局对象
WiFiClient espClient;
PubSubClient client(espClient);
Servo myServo;

// 按键防抖变量
int lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;

void setup() {
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP);
  myServo.attach(servoPin);

  // 初始化WiFi
  connectWiFi();

  // 配置MQTT
  client.setServer(mqttServer, mqttPort);
  client.setCallback(mqttCallback); // 处理订阅消息的回调

  // 开启硬件看门狗,8秒没喂就自动重启
  ESP.wdtEnable(WDTO_8S);
}

void loop() {
  // 喂看门狗,防止假死
  ESP.wdtFeed();

  // 检查WiFi连接
  if (WiFi.status() != WL_CONNECTED) {
    reconnectWiFi();
  }

  // 检查MQTT连接
  if (!client.connected()) {
    reconnectMQTT();
  }
  client.loop(); // 必须调用,处理MQTT心跳和消息

  // 非阻塞处理按键
  handleButton();

  // 每30秒打印一次剩余内存,排查泄漏
  static unsigned long lastMemCheck = 0;
  if (millis() - lastMemCheck > 30000) {
    Serial.print("剩余内存: ");
    Serial.println(ESP.getFreeHeap());
    lastMemCheck = millis();
  }
}

// 初始连接WiFi
void connectWiFi() {
  Serial.print("连接WiFi: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi连接成功");
}

// WiFi断开后重连
void reconnectWiFi() {
  Serial.println("\nWiFi断开,正在重连...");
  WiFi.disconnect();
  WiFi.begin(ssid, password);

  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 20) {
    delay(500);
    Serial.print(".");
    attempts++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi重连成功");
  } else {
    Serial.println("\n重连失败,重启设备...");
    ESP.restart();
  }
}

// MQTT断开后重连
void reconnectMQTT() {
  Serial.println("\nMQTT断开,正在重连...");
  // 用随机客户端ID,避免重复连接被拒绝
  String clientId = "ESP8266-";
  clientId += String(random(0xffff), HEX);

  int attempts = 0;
  while (!client.connect(clientId.c_str(), mqttUser, mqttPassword) && attempts < 10) {
    delay(1000);
    Serial.print("重连失败,错误码: ");
    Serial.println(client.state());
    attempts++;
  }

  if (client.connected()) {
    Serial.println("MQTT重连成功");
    // 重新订阅你的主题(根据需求修改)
    client.subscribe("your/control/topic");
  }
}

// MQTT消息回调函数
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("收到消息 [");
  Serial.print(topic);
  Serial.print("]: ");
  
  // 把payload转成字符串(避免用String,这里临时用一下方便调试)
  char msg[length+1];
  memcpy(msg, payload, length);
  msg[length] = '\0';
  Serial.println(msg);

  // 示例:根据消息控制舵机
  if (strcmp(msg, "open") == 0) {
    myServo.write(90);
  } else if (strcmp(msg, "close") == 0) {
    myServo.write(0);
  }
}

// 非阻塞按键处理
void handleButton() {
  int reading = digitalRead(buttonPin);

  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != lastButtonState) {
      lastButtonState = reading;
      // 按键按下(INPUT_PULLUP模式下,按下是LOW)
      if (reading == LOW) {
        Serial.println("按键被按下");
        // 发布按键消息到MQTT
        client.publish("your/button/state", "pressed");
      }
    }
  }
}

额外的小建议

  • 尽量用char数组代替String类,String会产生内存碎片,长时间运行后容易导致内存不足。
  • 如果你发现剩余内存持续减少,可以考虑每隔几个小时主动重启一次设备,比如用if (millis() > 43200000) ESP.restart();(12小时重启一次)。
  • 检查舵机的供电,如果用NodeMCU的3.3V供电,舵机可能动力不足导致电流过大,影响设备稳定性,最好给舵机单独供电。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:40:21