ESP32能否同时作为ESP-NOW网关与MQTT客户端?求实现方案
问题:ESP32同时实现ESP-NOW网关与MQTT连接的冲突解决
项目背景
- 多个ESP8266从机作为开关,向ESP32网关发送设备ID数据
- 从机与网关通过ESP-NOW库通信
- 网关需连接树莓派MQTT服务器,服务器根据设备ID下发自动化指令
当前问题
ESP8266与ESP32的ESP-NOW通信正常,树莓派MQTT Broker配置完成,但开启WiFi连接与MQTT连接代码后,ESP-NOW的OnDataRecv回调功能失效。
解决方案
完全可以实现ESP32同时作为ESP-NOW网关和MQTT客户端,问题出在初始化顺序、阻塞式循环以及中断上下文的不当操作,以下是修复方案:
关键修改点
- 调整初始化顺序:先设置WiFi STA模式,再初始化ESP-NOW,确保两者兼容
- 移除阻塞式循环:将WiFi连接的
while(WiFi.status() != WL_CONNECTED)改为loop中的定时重连,避免阻塞ESP-NOW回调 - 中断上下文安全:ESP-NOW回调属于中断函数,禁止使用
delay,改用非阻塞逻辑处理LED闪烁 - 维持MQTT连接:loop中必须调用
MQTTclient.loop(),否则MQTT连接会断开
修改后的完整代码
#include <esp_now.h> #include <WiFi.h> #include <PubSubClient.h> #define LED 2 int myData = 0; bool needBlink = false; // 标记是否需要闪烁LED unsigned long blinkStartTime = 0; int blinkCount = 0; char ssid[] = "my ssid"; char password[] = "mypass"; char mqtt_server[] = "192.168.1.55"; WiFiClient espClient; PubSubClient MQTTclient(espClient); // ESP-NOW接收回调,中断上下文内禁止阻塞操作 void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { memcpy(&myData, incomingData, sizeof(myData)); Serial.print("Bytes received: "); Serial.println(len); Serial.print("data: "); Serial.println(myData); // 标记需要闪烁LED,避免在回调中使用delay needBlink = true; blinkCount = 0; blinkStartTime = millis(); // 仅当MQTT连接正常时发布消息 if(MQTTclient.connected()){ String response = "device" + String(myData); MQTTclient.publish("homecommand", response.c_str()); Serial.println("MQTT message published: " + response); } else { Serial.println("MQTT not connected, skip publishing"); } } void MQTTconnect() { if (MQTTclient.connected()) return; Serial.print("Attempting MQTT connection..."); String clientId = "ESP32-Gateway-"; clientId += String(random(0xffff), HEX); // 连接无密码的MQTT Broker if (MQTTclient.connect(clientId.c_str())) { Serial.println("connected"); // 可选:订阅服务器指令主题 // MQTTclient.subscribe("home/commands"); } else { Serial.print("failed, rc="); Serial.print(MQTTclient.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } void setup() { Serial.begin(115200); pinMode(LED, OUTPUT); digitalWrite(LED, LOW); // 先设置WiFi STA模式 WiFi.mode(WIFI_STA); // 初始化ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println("Error initializing ESP-NOW"); return; } esp_now_register_recv_cb(OnDataRecv); // 启动WiFi连接(非阻塞,在loop中完成) WiFi.begin(ssid, password); MQTTclient.setServer(mqtt_server, 1883); } void loop() { // 处理WiFi重连 if (WiFi.status() != WL_CONNECTED) { static unsigned long lastReconnectAttempt = 0; if (millis() - lastReconnectAttempt > 5000) { lastReconnectAttempt = millis(); Serial.print("Connecting to WiFi..."); if (WiFi.reconnect()) { Serial.println("connected"); } else { Serial.println("failed"); } } } // 处理MQTT连接与消息 if (WiFi.status() == WL_CONNECTED) { MQTTconnect(); MQTTclient.loop(); // 必须调用,维持连接并处理订阅消息 } // 非阻塞式LED闪烁 if (needBlink) { if (millis() - blinkStartTime >= 100) { // 50ms亮+50ms灭,周期100ms blinkStartTime = millis(); digitalWrite(LED, (blinkCount % 2 == 0) ? HIGH : LOW); blinkCount++; if (blinkCount >= 6) { // 3次闪烁=6次状态切换 needBlink = false; digitalWrite(LED, LOW); } } } }
代码说明
- WiFi连接:采用定时重连机制,避免阻塞ESP-NOW的回调处理
- ESP-NOW回调:仅做数据接收和状态标记,把耗时操作(LED闪烁、MQTT发布)移到loop中处理,保证中断上下文的高效性
- MQTT维护:
MQTTclient.loop()会处理心跳和消息接收,确保连接稳定 - LED闪烁:使用非阻塞的时间戳方式,替代原有的
delay,避免影响其他功能
内容的提问来源于stack exchange,提问作者floprm
相关产品推荐
相关产品推荐

