如何检测ESP-NOW从设备与主设备的连接断开状态?
ESP-NOW主设备在线状态检测与触发逻辑实现
ESP-NOW本身没有原生的主从连接状态监测机制,要实现主设备在线时执行指定函数、离线时切换到备用逻辑,最可靠的方案是基于心跳包的超时检测,以下是具体实现思路和代码示例:
核心思路
- 主设备定时发送心跳包(固定间隔,比如1~5秒),告知从设备自身在线状态
- 从设备维护一个心跳超时计数器:
- 收到心跳包时重置计数器
- 若计数器超过设定阈值(比如3个心跳周期),判定主设备离线,触发离线逻辑
- 计数器未超时则判定主设备在线,执行指定业务函数
代码实现(Arduino框架)
1. 公共定义(主从设备共享)
#include <esp_now.h> #include <WiFi.h> #include <Ticker.h> // 定义心跳包标识和超时阈值 #define HEARTBEAT_SIGNAL 0xAA #define HEARTBEAT_INTERVAL 2000 // 心跳发送间隔(毫秒) #define HEARTBEAT_TIMEOUT 6000 // 超时时间(3个心跳周期) // 数据结构体:区分心跳包和业务数据 typedef struct { uint8_t type; // 0xAA=心跳,0xBB=业务数据 // 业务数据字段(根据需求添加) float sensor_data; } DataPacket; DataPacket send_packet; DataPacket recv_packet; // 从设备用:记录最后一次收到心跳的时间 unsigned long last_heartbeat_time = 0; bool master_online = true; // 主设备用:定时器发送心跳 Ticker heartbeat_ticker;
2. 主设备代码
// 从设备MAC地址(替换为你的从设备MAC) uint8_t slave_mac[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX}; // 发送心跳包函数 void send_heartbeat() { send_packet.type = HEARTBEAT_SIGNAL; esp_now_send(slave_mac, (uint8_t *)&send_packet, sizeof(send_packet)); } // ESP-NOW发送回调(可选,用于确认发送状态) void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) { // 可在这里处理发送失败的情况(比如重试) } void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); // 初始化ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW初始化失败"); return; } esp_now_register_send_cb(on_data_sent); // 添加从设备为peer esp_now_peer_info_t peer_info = {}; memcpy(peer_info.peer_addr, slave_mac, 6); peer_info.channel = 0; peer_info.encrypt = false; if (esp_now_add_peer(&peer_info) != ESP_OK) { Serial.println("添加peer失败"); return; } // 启动定时器定时发送心跳 heartbeat_ticker.attach_ms(HEARTBEAT_INTERVAL, send_heartbeat); } void loop() { // 主设备在线时执行的业务逻辑 // 比如发送业务数据 send_packet.type = 0xBB; send_packet.sensor_data = analogRead(A0) * (3.3 / 4095.0); esp_now_send(slave_mac, (uint8_t *)&send_packet, sizeof(send_packet)); delay(1000); // 业务数据发送间隔,根据需求调整 }
3. 从设备代码
// ESP-NOW接收回调 void on_data_recv(const uint8_t *mac_addr, const uint8_t *data, int data_len) { memcpy(&recv_packet, data, sizeof(recv_packet)); if (recv_packet.type == HEARTBEAT_SIGNAL) { // 收到心跳,更新时间并标记主设备在线 last_heartbeat_time = millis(); master_online = true; } else if (recv_packet.type == 0xBB) { // 处理业务数据 Serial.print("收到业务数据:"); Serial.println(recv_packet.sensor_data); } } // 主设备在线时执行的函数 void run_online_logic() { // 替换为你的在线业务逻辑 Serial.println("主设备在线,执行在线函数"); digitalWrite(LED_BUILTIN, HIGH); } // 主设备离线时执行的函数 void run_offline_logic() { // 替换为你的离线业务逻辑 Serial.println("主设备离线,执行离线函数"); digitalWrite(LED_BUILTIN, LOW); } void setup() { Serial.begin(115200); pinMode(LED_BUILTIN, OUTPUT); WiFi.mode(WIFI_STA); // 初始化ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW初始化失败"); return; } esp_now_register_recv_cb(on_data_recv); } void loop() { // 检测心跳超时 if (millis() - last_heartbeat_time > HEARTBEAT_TIMEOUT) { if (master_online) { // 从在线切换到离线,触发离线逻辑 master_online = false; run_offline_logic(); } } else { if (!master_online) { // 从离线切换到在线,触发在线逻辑 master_online = true; run_online_logic(); } // 主设备在线时持续执行在线逻辑(或按需调用) run_online_logic(); } delay(100); // 降低循环频率,节省资源 }
关键细节说明
- 心跳间隔与超时阈值:根据实际环境调整,干扰大的场景可增大间隔和阈值,避免误判
- 发送回调处理:主设备可通过
on_data_sent回调检测发送失败,若多次失败可主动标记自身状态(或重试) - 加密配对:若需要安全传输,可开启ESP-NOW加密功能,添加peer时设置
encrypt = true并配置密钥 - 资源优化:从设备的循环中加入短延迟,避免CPU占用过高
内容的提问来源于stack exchange,提问作者Kunaal Kiran Kumar
相关产品推荐
相关产品推荐

