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
相关产品推荐
相关产品推荐

