深入解析 QuickESPNow:为 Tasmota 打造的免路由器 ESP-NOW 点对点通信库
【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota
导读
QuickESPNow(Quick Esp-NOW)是一个基于 Arduino 框架、面向 Espressif ESP8266 与 ESP32 系列微控制器的轻量通信库,它把 ESP-NOW 协议中繁琐的节点注册、WiFi 信道选择、设备角色分配等限制全部封装隐藏,让开发者用几行代码即可实现设备间的直接点对点通信。本库已内置集成在 Tasmota 固件仓库中:一方面它支撑着 TASMESH 驱动 将 Tasmota 节点移出 WLAN、通过 ESP-NOW 与 ESP32 网关双向通信;另一方面也被 WizMote 驱动 直接用于接收 Wiz 无线开关的按键消息。读完本文,你将掌握 QuickESPNow 的核心 API、广播/单播两种模式、同步/异步发送机制,以及如何结合 Tasmota 的规则系统与源码示例落地一套无路由器依赖的设备互联方案。
QuickESPNow 是什么
ESP-NOW 是乐鑫提供的一种无线通信协议,允许两台设备不经过无线网络(路由器/AP)直接互发数据。然而直接使用原生 ESP-NOW API 并不直观,开发者必须自行处理设备数量上限、WiFi 信道选择、节点角色划分等一系列限制。
QuickESPNow 的目的正是"隐藏所有这些限制",让开发者只需几行代码即可完成通信。本项目 lib/lib_div/QuickESPNow/README.md 中的版本是一个针对Espressif IDF 5.x 兼容性的 fork,其主要改动在于调整了接收回调rx_cb的函数签名,使其适配新版 ESP-IDF 的esp_now_recv_info_t参数结构。
从源码结构看,库的核心设计分为三层:
- QuickEspNow.h:平台分发头文件,根据
ESP32/ESP8266宏选择对应实现,否则编译报#error "Unsupported platform"; - Comms_hal.h:通信抽象层接口,定义了
begin、send、onDataRcvd、onDataSent、getAddressLength、getMaxMessageLength、enableTransmit等虚方法及发送错误码枚举; - QuickEspNow_esp32.cpp 与 QuickEspNow_esp8266.cpp:分别在 ESP32(FreeRTOS 队列)与 ESP8266(
RingBuffer)上的具体实现,两者都导出全局单例quickEspNow。
相比原生 ESP-NOW 的优势
官方 README 明确列出了本库消除的若干限制:
- 不再有 20 台设备上限:原生 ESP-NOW 受 peer 注册表限制,而本库接管了 peer 的自动注册与维护,理论上任意数量设备均可通信;
- 无需为 WiFi 共存手动选择信道:库会自动跟随/指定信道;
- 无需为每台设备分配角色:即可用于纯点对点通信;
- 每条消息附带 RSSI 信息:接收端可据此估计发送端距离远近;
- 接收端可区分广播与单播消息:通过回调的
broadcast参数直接判断; - 加密不被支持(这是刻意取舍):因为使用 ESP-NOW 原生加密会将系统限制回 6 台设备,需要加密时可在更高层自行实现(如 TASMESH 就是采用 ChaCha20Poly1305 在应用层完成认证加密)。
性能基线
README 提供了一张官方实测吞吐量表(ESP32 与 ESP8266 各自以最快速度连续发送,覆盖广播/单播、同步/异步两种模式、五种消息长度):
| 设备 | 广播/单播 | 同步/异步 | 250 字节 | 125 字节 | 75 字节 | 35 字节 | 12 字节 |
|---|---|---|---|---|---|---|---|
| ESP32 | broadcast | async | 640 kbps | 450 kbps | 340 kbps | 190 kbps | 75 kbps |
| ESP32 | broadcast | sync | 615 kbps | 440 kbps | 320 kbps | 180 kbps | 73 kbps |
| ESP8266 | broadcast | async | 200 kbps | 100 kbps | 60 kbps | 28 kbps | 9.5 kbps |
| ESP8266 | broadcast | sync | 200 kbps | 100 kbps | 60 kbps | 28 kbps | 9.5 kbps |
| ESP32 | unicast | async | 570 kbps | 400 kbps | 285 kbps | 160 kbps | 60 kbps |
| ESP32 | unicast | sync | 550 kbps | 375 kbps | 270 kbps | 150 kbps | 57 kbps |
| ESP8266 | unicast | async | 200 kbps | 100 kbps | 60 kbps | 28 kbps | 9.5 kbps |
| ESP8266 | unicast | sync | 200 kbps | 100 kbps | 60 kbps | 28 kbps | 9.5 kbps |
阅读该表时需注意:
- 表中为理想最坏场景(无消息丢失、MCU 不运行其他任务)的数值;
- 早期版本曾误报 ESP8266 可达 600 kbps,实为缺少"上一帧确认前禁止发送下一帧"的检查所致,0.8.1 版本加入该检查后修正为 200 kbps;
- 同步模式下用户代码会被阻塞 1~20 ms 直到发送完成,实际性能可能因主循环其余代码而明显下降;而异步模式下
send仅耗时约 22 µs(ESP32 与 ESP8266 均如此),对主逻辑影响可忽略; - ESP8266 吞吐显著低于 ESP32。若 ESP32 以高于 ESP8266 处理能力的速率发包,接收端可能丢包甚至崩溃,混网部署时应以最慢设备的速率安全值为准。
快速上手:核心 API 与最小示例
README 给出的使用模式极简:调用begin初始化、通过onDataRcvd注册接收回调,然后调用send向指定设备或ESPNOW_BROADCAST_ADDRESS(广播地址0xFF:0xFF:0xFF:0xFF:0xFF:0xFF)发送数据。
void dataReceived (uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast) { Serial.printf("Received message: %.*s\n", len, data); } void setup () { Serial.begin (115200); WiFi.mode (WIFI_MODE_STA); WiFi.disconnect (false); quickEspNow.onDataRcvd (dataReceived); quickEspNow.begin (1); // If you are not connected to WiFi, channel should be specified } void loop () { String message = "Hello, world!"; quickEspNow.send (DEST_ADDR, (uint8_t*)message.c_str (), message.length ()); delay (1000); }其中接收回调dataReceived的五个参数与 Comms_hal.h 中定义的回调类型完全对应:
| 参数 | 类型 | 含义 |
|---|---|---|
address | uint8_t* | 发送方 MAC 地址(6 字节) |
data | uint8_t* | 消息负载 |
len | uint8_t | 负载长度 |
rssi | signed int | 接收信号强度(dBm) |
broadcast | bool | 是否为广播消息 |
send的返回值对应comms_send_error_t枚举:COMMS_SEND_OK = 0表示成功,其余负数分别表示参数错误(-1)、负载超长(-2)、队列已满(-3)、入队失败(-4)与确认失败(-5,仅同步发送模式)。
begin 函数:信道、接口与发送模式
begin是本库的初始化入口,其完整签名为:
bool begin (uint8_t channel = CURRENT_WIFI_CHANNEL, uint32_t interface = 0, bool synchronousSend = true);三个参数的含义与使用场景如下:
- channel(信道):默认值
CURRENT_WIFI_CHANNEL = 255表示"跟随当前 WiFi 信道"。若设备未连接任何 WiFi,必须显式指定 1~13 的信道号(MIN_WIFI_CHANNEL = 0,MAX_WIFI_CHANNEL = 14)。示例quickEspNow.begin (1)即表示在信道 1 上工作; - interface(接口):默认 0 对应 STA 接口,也可传
WIFI_IF_AP使用 SoftAP 接口。注意:ESP-NOW 与 AP 必须使用相同信道,见 wifi_ap_and_espnow 示例; - synchronousSend(发送模式):默认
true即同步阻塞模式——send会等待发送确认后才返回,返回 0 表示发送成功,非 0 表示出错,用户无需自行实现 TX 回调即可获得可靠发送。若追求最佳吞吐,可在begin中传入false切换为异步模式。
值得强调的是,异步模式已成为默认推荐:它允许库在后台发送消息而用户代码继续运行,send调用本身仅需 22 µs。同步模式仅面向"需要一个阻塞式发送方法以便于使用"的开发者。两个平台的头文件 QuickEspNow_esp32.h 与 QuickEspNow_esp8266.h 内部都维护了synchronousSend、waitingForConfirmation、readyToSend状态位与发送任务(ESP32 用espnowTxTask/espnowRxTaskFreeRTOS 任务,ESP8266 用ETSTimer),这正是同步/异步两种模式差异的实现基础。
其它常用 API 一览:
| API | 说明 |
|---|---|
sendBcast (payload, len) | 便捷广播,等价于send (ESPNOW_BROADCAST_ADDRESS, ...) |
onDataSent (cb) | 注册发送完成回调(uint8_t* address, uint8_t status),异步模式下用于感知发送结果 |
readyToSendData () | 查询是否可发送新消息,配合异步模式避免过速发包导致丢包 |
stop () | 停止 ESP-NOW 并释放资源 |
getMaxMessageLength () | 返回最大负载长度(ESP32 为 250 字节,ESP8266 为 255 字节) |
setChannel (channel) | 动态切换信道(ESP32 额外支持setWiFiBandwidth) |
四种官方示例:覆盖典型部署形态
仓库在 examples 目录下提供了四个可直接编译运行的示例,覆盖了最常见的部署场景,适合作为开发起点:
1. basicespnow —— 无 WiFi 环境的最小对讲
basicespnow.cpp 演示了"裸 ESP-NOW"模式:WiFi.mode (WIFI_MODE_STA)后调用WiFi.disconnect (false)断开连接,再以quickEspNow.begin (1)指定信道启动。由于没有关联任何 AP,信道必须显式给出。示例通过宏USE_BROADCAST切换广播/单播:置 1 使用ESPNOW_BROADCAST_ADDRESS,否则指定接收端 MAC(如{ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12 })。主循环每 2 秒发送一条带计数器的消息,接收回调会打印负载内容、RSSI、来源 MAC 与消息类型。若需 ESP32 与 ESP8266 在同一网络共存,示例还调用了quickEspNow.setWiFiBandwidth (WIFI_IF_STA, WIFI_BW_HT20)将带宽收敛到 HT20。
2. wifi_sta_and_espnow —— 已连 WiFi 的共存模式
wifi_sta_and_espnow.cpp 演示最常见的"STA + ESP-NOW"共存:设备先WiFi.begin ("ssid", "pass")连上路由器,再以quickEspNow.begin ()无参启动——此时库自动使用当前 WiFi 信道,ESP-NOW 与上网互不干扰。
3. wifi_ap_and_espnow —— 软 AP 热点 + ESP-NOW
wifi_ap_and_espnow.cpp 展示设备以 SoftAP 身份广播热点(SSIDespnowAP,信道 1)同时运行 ESP-NOW:quickEspNow.begin (CURRENT_WIFI_CHANNEL, WIFI_IF_AP)显式指定 AP 接口,注释特别强调AP 与 ESP-NOW 必须使用同一信道。
4. advancedespnow —— 异步模式与流量控制
advancedespnow.cpp 演示异步发送的最佳实践:以quickEspNow.begin (1, 0, false)关闭同步模式,通过onDataSent注册发送完成回调维护sent标志,并在主循环中结合quickEspNow.readyToSendData () && sent双重闸门控制发包节奏。注释明确指出这是避免消息丢失、最大化吞吐的关键——不要在上一帧尚未确认时连续投递新帧。
在 Tasmota 中的实际集成
QuickESPNow 在 Tasmota 仓库中有两处直接落地,可作为库的实战范例:
TASMESH:将节点移出 WLAN 的网格方案
TASMESH 驱动说明 及其实现 xdrv_57_1_tasmesh_support.ino / xdrv_57_9_tasmesh.ino 展示了一个完整的 ESP-NOW 网格应用:ESP32 作为网关/代理接入 WLAN 并订阅每个节点的 MQTT 主题,节点(典型为 ESP8266)则通过 ESP-NOW 与网关双向通信。这样既减轻了路由器负担、释放 2.4 GHz 频段流量,也显著降低节点功耗,利于电池供电项目。TASMESH 的负载加密采用 WiFi 密码 1 的前 32 字节作为 ChaCha20Poly1305 认证加密密钥,即"更高层实现数据加密"理念的实例。
启用方式为在user_config_override.h中添加#define USE_TASMESH后重新编译。相关命令(MAC 地址中的冒号可选,注意网关使用的 MAC 是 Soft AP MAC 而非 WiFi MAC):
| 命令 | 说明 |
|---|---|
MeshBroker | 在 ESP32 上启动代理,日志输出 MAC 与信道;必须在 WiFi 初始化后调用 |
MeshChannel 1..13 | 将节点 WiFi 信道切换为 1~13,与代理保持一致 |
MeshNode AA:BB:CC:DD:EE:FF | 启动节点并连接指定 MAC 的代理,自动上报 MQTT 主题 |
MeshPeer AA:BB:CC:DD:EE:FF | 将已知节点添加为对等节点,用于经网格中继发送数据 |
MeshInterval 2..200 | 调整网格消息间隔(默认 50 ms) |
规则示例:代理在 WiFi 就绪后启动——rule1 on system#boot do meshbroker endon;节点在system#init立即启动(启动后 WiFi 与 Web 服务会被按设计关闭)——rule1 on system#init do meshnode FA:KE:AD:DR:ES:S1 endon;网格状态与参数尚未持久化到 Flash,断电或深睡眠唤醒后需通过规则重新初始化;若全网同时上电,建议改用组主题 + 代理就绪后再广播meshnode命令的方式,避免节点因代理未就绪而加入失败(当前版本未实现自动重试)。
WizMote:直接消费quickEspNow单例
xdrv_77_wizmote.ino 展示了在 Tasmota 驱动中直接使用本库的方式:在 WIZMOTE 初始化逻辑 中,驱动先quickEspNow.stop ()清理状态,然后按场景调用quickEspNow.begin (WIZMOTE_CHANNEL)(未连接 WiFi 时指定信道)或quickEspNow.begin ()(跟随 WiFi 信道),并通过quickEspNow.onDataRcvd (EspNowDataReceived)注册接收回调来解析 Wiz 无线开关发来的按键事件。这恰好印证了本库"库本身不依赖 Tasmota、但可被 Tasmota 驱动即插即用"的抽象设计。
测试与工程化支撑
仓库为该库提供了配套测试:test/test_peer_list/test_peer_list.cpp 针对 peer 列表管理(PeerListClass的添加、查找、删除与使用计数更新)编写了单元测试,验证自动 peer 注册这一核心机制的可靠性;library.json 与 library.properties 则使其可作为标准 PlatformIO/Arduino 库被引用,README 顶部的 PlatformIO Registry 徽章表明其可经 PlatformIO 包管理直接安装。吞吐基准数据(Throughput.xlsx)也随库一并托管,便于复现验证。
注意事项与限制总结
综合 README 与源码,使用本库时需要牢记:
- 消息负载上限:ESP32 为 250 字节、ESP8266 为 255 字节,超出将返回
COMMS_SEND_PAYLOAD_LENGTH_ERROR; - 发送队列有限:默认队列深度为 3(
ESPNOW_QUEUE_SIZE),过速发包会触发队列满错误,异步模式下请结合readyToSendData ()控制节奏; - 不支持 ESP-NOW 原生加密(会限制为 6 台设备),需要机密性时应在应用层加密,TASMESH 的 ChaCha20Poly1305 方案可作参考;
- 跨平台混网时注意速率差:ESP32 吞吐约为 ESP8266 的 3 倍以上,应以慢设备为基准限速;
- 同步模式会阻塞主循环 1~20 ms,对时序敏感的业务优先选择异步模式;
- 本 fork 针对 ESP-IDF 5.x 调整了接收回调签名,使用旧版 IDF 时需注意适配差异。
QuickESPNow 以极小的 API 面换取了对 ESP-NOW 底层复杂性的完整封装,配合仓库内的四个示例、TASMESH 网格驱动与 WizMote 集成案例,无论是做无路由器传感器网络、电池供电节点,还是 Tasmota 生态内的旁路通信,都能快速起步并直接落地。
【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考