
ESP-IDF 中 ESP-NOW 协议实战指南帧格式、安全机制与 Wi-Fi 无连接通信开发【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idfESP-NOW 是乐鑫Espressif定义的一种无连接 Wi-Fi 通信协议它无需建立 TCP/IP 连接将应用程序数据封装在供应商动作帧Vendor-Specific Action Frame中直接从一个 Wi-Fi 设备传输到另一个 Wi-Fi 设备广泛应用于智能照明、远程控制、传感器等物联网场景。本文基于 ESP-IDF 官方文档与 esp_now.h 源码系统讲解 ESP-NOW 的帧格式、CCMP 安全机制、生命周期管理、配对与收发流程并结合 espnow 示例工程 给出可直接落地的开发方案帮助你快速掌握在 ESP32 系列芯片上使用 ESP-NOW 进行点对点通信的完整技能。概述ESP-NOW 是什么ESP-NOW 是乐鑫公司定义的无连接 Wi-Fi 通信协议。在 ESP-NOW 中应用程序数据被封装在各个供应商动作帧中然后在无连接的情况下从一个 Wi-Fi 设备传输到另一个 Wi-Fi 设备。这意味着通信双方不需要先完成 Wi-Fi 连接、DHCP 或 TCP/IP 握手只要在同一个信道上即可直接收发数据因此具有极低的建立延迟和极小的协议开销。CTR 与 CBC-MAC 协议CCMPCounter Mode with CBC-MAC可用来保护动作帧的安全其具体实现参考 IEEE Std. 802.11-2012。目前 ESP-NOW 广泛应用于智能照明、远程控制、传感器等领域。帧格式供应商动作帧与特定供应商元素ESP-NOW 使用供应商动作帧传输数据默认比特率为 1 Mbps。理解其帧结构是进行协议分析、抓包调试和兼容性判断的基础。协议版本与数据长度上限目前 ESP-NOW 支持两个版本v1.0和v2.0对应的最大数据包长度定义在 esp_now.h版本最大数据长度宏定义说明v1.0250 字节ESP_NOW_MAX_DATA_LEN即ESP_NOW_MAX_IE_DATA_LEN单次传输的数据长度上限v2.01470 字节ESP_NOW_MAX_DATA_LEN_V2单次传输的数据长度上限版本间的兼容规则如下v2.0 设备可以接收来自 v2.0 和 v1.0 设备的数据包v1.0 设备只能接收来自 v1.0 设备的数据包不过v1.0 设备也可以接收长度不超过 250ESP_NOW_MAX_IE_DATA_LEN字节的 v2.0 数据包如果长度超过此值v1.0 设备要么只接收前 250 字节要么直接丢弃该数据包。具体行为请参考对应 IDF 版本中的文档。应用程序可以通过esp_now_get_version()查询当前固件支持的 ESP-NOW 版本。供应商动作帧格式供应商动作帧的整体格式如下----------------------------------------------------------------------------- | MAC 报头 | 分类代码 | 组织标识符 | 随机值 | 供应商特定内容 | FCS | ----------------------------------------------------------------------------- 24 字节 1 字节 3 字节 4 字节 7-x 字节 4 字节各字段含义分类代码Category Code用于指示各个供应商的类别例如 127组织标识符Organization IdentifierOUI包含一个唯一标识符比如0x18fe34为乐鑫指定的 MAC 地址的前三个字节。可通过esp_now_set_user_oui()/esp_now_get_user_oui()自定义或查询 OUI传NULL时使用默认值0x18fe34随机值Random Value用于防止重放攻击供应商特定内容Vendor Specific Content包含若干个大于等于 1特定供应商元素字段。对于 v2.0 版本x 1512 (1470 6*7)对于 v1.0 版本x 257 (250 7)FCSFrame Check Sequence帧校验序列。特定供应商元素格式特定供应商元素Vendor-Specific Element的帧格式因版本而异ESP-NOW v1.0 --------------------------------------------------------------------------- | 元素 ID | 长度 | 组织标识符 | 类型 | 保留 | 版本 | 正文 | --------------------------------------------------------------------------- 7~4 比特| 3~0 比特 1 字节 1 字节 3 字节 1 字节 1 字节 0-250 字节 ESP-NOW v2.0 ------------------------------------------------------------------------------------- | 元素 ID | 长度 | 组织标识符 | 类型 | 保留 |更多数据 | 版本 | 正文 | ------------------------------------------------------------------------------------- 7~5 比特 | 1 比特 | 3~0 比特 1 字节 1 字节 3 字节 1 字节 1 字节 0-250 字节字段说明元素 IDElement ID用于指示特定于供应商的元素长度Length组织标识符、类型、版本和正文的总长度最大值为 255组织标识符唯一标识符比如0x18fe34为乐鑫指定的 MAC 地址的前三个字节类型Type设置为 4代表 ESP-NOW版本Version设置为 ESP-NOW 的版本v1.0 或 v2.0正文Body包含实际要发送的 ESP-NOW 数据最长 250 字节元素级长度上限。v2.0 在保留字段中新增了“更多数据”More Data位1 比特用于支持超过单元素长度上限的更大数据包传输1470 字节。MAC 报头的无连接特性由于 ESP-NOW 是无连接的其 MAC 报头与标准帧略有不同FrameControl 字段的 FromDS 和 ToDS 位均为 0即不经过 AP 转发属于 IBSS 之外的直接传输第一个地址字段用于配置目标地址目的 MAC第二个地址字段用于配置源地址源 MAC第三个地址字段用于配置广播地址0xff:0xff:0xff:0xff:0xff:0xff。这种地址排列使得 ESP-NOW 既能支持单播目标地址为对端 MAC也能支持广播目标地址为全0xFF。安全PMK 与 LMK 的两级密钥体系ESP-NOW 采用 CCMP 方法保护供应商特定动作帧的安全参考 IEEE Std. 802.11-2012。Wi-Fi 设备维护一个初始主密钥PMKPrimary Master Key和若干本地主密钥LMKLocal Master Key每个配对设备拥有一个 LMK两者长度均为16 字节对应源码中的ESP_NOW_KEY_LEN。两级密钥的分工如下PMK用于使用 AES-128 算法加密 LMK。应用通过esp_now_set_pmk()设置 PMK如果未设置将使用默认 PMKLMK用于通过 CCMP 方法对供应商特定动作帧进行加密。如果未设置配对设备的 LMK则动作帧不进行加密。需要注意目前不支持加密组播multicast供应商特定动作帧。此外从源码结构看SoftAP 与 ESP-NOW 共享同一套加密硬件密钥资源因此 esp_wifi.h 中提示max_connection参数会受CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM的影响。初始化和反初始化调用esp_now_init()初始化 ESP-NOW调用esp_now_deinit()反初始化 ESP-NOW。API 的返回码定义在 esp_now.h其中ESP_ERR_ESPNOW_NOT_INIT、ESP_ERR_ESPNOW_ARG、ESP_ERR_ESPNOW_NO_MEM等错误码可用于排查调用失败原因。关键时序要求ESP-NOW 数据必须在 Wi-Fi 启动后传输因此建议在初始化 ESP-NOW之前启动 Wi-Fi并在反初始化 ESP-NOW之后停止 Wi-Fi当调用esp_now_deinit()时配对设备的所有信息都将被删除。添加配对设备在将数据发送到其他设备之前请先调用esp_now_add_peer()将目标设备添加到配对设备列表中。如果启用了加密则必须设置 LMK。esp_now_peer_info_t结构体定义于 esp_now.h是配对的核心参数字段说明peer_addr[6]对端 MAC 地址Station 或 SoftAP 的 MAC 地址lmk[16]本地主密钥用于加密数据channel对端收发 ESP-NOW 数据的 Wi-Fi 信道若为 0则使用当前信道ifidx对端使用的 Wi-Fi 接口STA 或 APencrypt该对端收发数据是否加密priv对端私有数据指针ESP-NOW 数据可以从Station 或 SoftAP 接口发送。确保在发送 ESP-NOW 数据之前已启用该接口。信道规则按芯片区分ESP32 / ESP32-C2 / ESP32-S2 / ESP32-S3 / ESP32-C3 / ESP32-C6配对设备的信道范围是0 ~ 14。如果信道设置为 0数据将在当前信道上发送否则必须使用本地设备所在的信道ESP32-C52.4G 频段配对设备的信道范围是1 ~ 145G 频段配对设备的信道范围是[36, 40, 44, 48, 52, 56, 60, 64, 100, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177]。如果信道设置为 0数据将在当前信道上发送否则必须使用本地设备所在的信道。接收端是否必须添加配对设备对于接收设备调用esp_now_add_peer()不是必需的如果没有添加配对设备只能接收广播包和不加密的单播包如果需要接收加密的单播包则必须添加配对设备并设置相同的 LMK。配对设备数量限制ESP32-C2配对设备的最大数量是20其中加密设备的数量不超过4默认值是2。可通过 Wi-Fi menuconfig 中的CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM修改加密设备的数量该配置项定义于 esp_wifi/KconfigESP32 / ESP32-S2 / ESP32-S3 / ESP32-C3 / ESP32-C6 / ESP32-C5配对设备的最大数量是20其中加密设备的数量不超过17默认值是7。同样可通过CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM修改。源码中的ESP_NOW_MAX_TOTAL_PEER_NUM20与ESP_NOW_MAX_ENCRYPT_PEER_NUM6提供了编译期默认约束esp_now_get_peer_num()可查询当前已有的配对总数与加密对端数。发送 ESP-NOW 数据调用esp_now_send()发送 ESP-NOW 数据调用esp_now_register_send_cb()注册发送回调函数。发送回调的原型为typedef void (*esp_now_send_cb_t)(const esp_now_send_info_t *tx_info, esp_now_send_status_t status);发送结果的判定与重传策略如果 MAC 层成功接收到数据回调将返回ESP_NOW_SEND_SUCCESS事件否则返回ESP_NOW_SEND_FAILESP-NOW 数据发送失败可能有几种原因比如目标设备不存在、设备的信道不相同、动作帧在传输过程中丢失等应用层并不一定总能接收到数据。如果需要应用层可在接收 ESP-NOW 数据时发回一个应答ACK数据如果接收 ACK 数据超时则重新传输 ESP-NOW 数据可以为 ESP-NOW 数据设置序列号从而在接收端删除重复的数据。esp_now_send()的三种发送目标见 esp_now.h 的注意事项peer_addr非 NULL向 MAC 地址匹配的配对设备发送peer_addr为 NULL向配对列表中的所有设备发送广播地址0xFF:0xFF:0xFF:0xFF:0xFF:0xFF广播发送。大批量发送的注意事项如果有大量 ESP-NOW 数据要发送调用esp_now_send()时需注意单次发送的数据不能超过最大数据包长v1.0 为 250 字节v2.0 为 1470 字节两个 ESP-NOW 数据包的发送间隔太短可能导致回调函数返回混乱因此建议在等到上一次回调函数返回 ACK 后再发送下一个 ESP-NOW 数据发送回调函数从高优先级的 Wi-Fi 任务中运行不要在回调函数中执行冗长的操作。相反应将必要的数据发布到队列交给优先级较低的任务处理。esp_now_send()可能的返回错误包括ESP_ERR_ESPNOW_NO_MEM此时可延迟片刻再发送下一条、ESP_ERR_ESPNOW_NOT_FOUND对端不在列表中、ESP_ERR_ESPNOW_IF当前 Wi-Fi 接口与对端不匹配、ESP_ERR_ESPNOW_CHAN当前 Wi-Fi 信道与对端不匹配等。接收 ESP-NOW 数据调用esp_now_register_recv_cb()注册接收回调函数。接收回调原型为typedef void (*esp_now_recv_cb_t)(const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len);esp_now_recv_info_t中包含源地址src_addr、目的地址des_addr以及接收控制信息rx_ctrl。由于广播与加密单播在接收路径上可能并存可以通过检查des_addr是否为广播地址来区分示例工程中即通过IS_BROADCAST_ADDR(des_addr)判断。接收回调函数同样在Wi-Fi 任务中运行因此不要在回调函数中执行冗长的操作将必要的数据发布到队列交给优先级较低的任务处理注意esp_now_info是局部变量只能在回调函数内部使用。配置 ESP-NOW 速率调用esp_now_set_peer_rate_config()配置每个 peer 的 ESP-NOW 速率对应源码中的esp_now_rate_config_t即wifi_tx_rate_config_t。使用约束请确保在配置速率之前添加 peer此 API 应在esp_wifi_start()和esp_now_add_peer()之后调用。在示例工程的example_wifi_init()中若启用 Long Range 选项CONFIG_ESPNOW_ENABLE_LONG_RANGE会通过esp_wifi_set_protocol()追加WIFI_PROTOCOL_LR此时 ESP32 设备将以512Kbps 或 256Kbps 的 PHY 速率发送数据从而在两端之间实现更远距离的传输。配置 ESP-NOW 功耗参数当且仅当目标芯片配置为STA 模式时允许其进行休眠。进行休眠时调用esp_now_set_wake_window()为 ESP-NOW 收包配置 Window参数window表示每个间隔内芯片保持唤醒的毫秒数范围 0 ~ 65535默认情况下 Window 为最大值将允许一直收包该配置在连接状态下可工作当启用了ESP_WIFI_STA_DISCONNECTED_PM_ENABLE时在断开状态下也可工作见 esp_now.h 的注释。如果对 ESP-NOW 进行功耗管理还需要调用esp_wifi_connectionless_module_set_wake_interval()声明于 esp_wifi.h用于设置无连接模块的唤醒间隔。更完整的信息可参考 ESP-IDF 文档中的“非连接模块功耗管理”connectionless-module-power-save章节。应用示例基于 espnow 示例工程的完整开发流程examples/wifi/espnow 演示了如何在 ESP32 系列芯片上使用 Wi-Fi 的 ESPNOW 功能完整覆盖了本文前述的启动 Wi-Fi、初始化 ESP-NOW、注册发送/接收回调、添加对端信息以及在两台设备之间收发数据的全部步骤。工程至少需要两台 ESP 设备。示例的工作流程依据 README.md 与 espnow_example_main.c示例的交互流程如下设备 1先发送state0的广播 ESP-NOW 数据以便获知对方 MAC 地址设备 2收到state0的广播数据后将设备 1 加入配对列表并开始发送state1的广播数据设备 1收到state1的广播数据后比较本地 magic 数与数据中的 magic 数若本地 magic 更大则停止广播开始向设备 2 发送单播 ESP-NOW 数据设备 2收到单播数据后也停止广播。实际工程中如果已知对方 MAC 地址则不必先通过广播交互直接将对端加入配对列表并收发单播数据即可。示例中的数据可靠性设计示例在 ESP-NOW 原始数据之上额外封装了type、state、seq_num序列号、crcCRC16 校验和magic等“附加字段”见 espnow_example.h。这些字段并非 ESP-NOW 协议所必需仅用于让示例可靠运行但官方明确建议实际产品在 ESP-NOW 数据中加入类似扩展以提升数据的安全性与可靠性seq_num用于识别并去重对应文档中“设置序列号删除重复数据”的建议crc使用esp_crc16_le()计算校验用于检测数据损坏magic用于在广播握手阶段裁决哪台设备负责发起单播。回调与低优先级任务解耦示例中发送/接收回调只负责把事件投递到 FreeRTOS 队列s_example_espnow_queue真正的数据处理解析、应答、发送下一条在低优先级的example_espnow_task中完成。发送端严格遵循“等上一次发送回调返回后再发下一条”的策略并通过send_param-delay控制两条数据之间的间隔这正是文档中“不要连续快速发送、避免回调混乱”建议的工程化落地。配置项说明menuconfig进入工程目录后执行idf.py menuconfig在Example Configuration菜单下可配置定义见 Kconfig.projbuild配置项默认值取值范围说明ESPNOW_WIFI_MODEStationStation / SoftAPWi-Fi 工作模式ESPNOW_PMKpmk123456789012316 字节字符串主密钥收发双方必须一致ESPNOW_LMKlmk123456789012316 字节字符串本地主密钥收发双方必须一致ESPNOW_CHANNEL10 ~ 14收发 ESP-NOW 数据的信道双方必须相同ESPNOW_SEND_COUNT1001 ~ 65535单播数据发送总次数ESPNOW_SEND_DELAY10000 ~ 65535两条数据间的发送间隔msESPNOW_SEND_LEN1010 ~ 1470发送数据长度字节ESPNOW_ENABLE_LONG_RANGE关闭开/关启用后 PHY 速率为 512Kbps 或 256Kbps可远距离传输ESPNOW_ENABLE_POWER_SAVE关闭开/关启用 ESP-NOW 功耗管理仅 Station 模式ESPNOW_WAKE_WINDOW500 ~ 65535唤醒窗口msESPNOW_WAKE_INTERVAL1001 ~ 65535唤醒间隔ms要点PMK 与 LMK 在收发双方必须设置为相同值发送与接收设备必须处于同一信道。构建、烧录与运行idf.py set-target esp32 # 按实际芯片选择目标 idf.py menuconfig # 按上文配置项进行设置 idf.py -p PORT flash monitor # 构建、烧录并打开串口监视器退出串口监视器按Ctrl-]。接收设备控制台输出示例I (898) espnow_example: WiFi started I (898) ESPNOW: espnow [version: 1.0] init I (5908) espnow_example: Start sending broadcast data I (52138) espnow_example: Receive 0th broadcast data from: 30:ae:a4:0c:34:ec, len: 200 I (54168) espnow_example: Receive 0th unicast data from: 30:ae:a4:0c:34:ec, len: 200常见问题排查无法收到对端数据可能原因是对端不在同一信道或 PMK / LMK 配置不一致Station 模式接收问题在真实应用中如果接收设备仅为 Station 模式且已连接 AP应禁用 modem sleep否则可能无法收到来自其他设备的 ESP-NOW 数据加密单播收不到接收端必须添加对端并设置相同的 LMK发送失败检查esp_now_send()返回码特别是ESP_ERR_ESPNOW_IF接口不匹配与ESP_ERR_ESPNOW_CHAN信道不匹配。其他常用 API 速查除本文重点讲解的 API 外esp_now.h 还提供以下常用接口函数作用esp_now_del_peer()从配对列表删除对端esp_now_mod_peer()修改对端信息如切换加密、改信道esp_now_get_peer()/esp_now_fetch_peer()查询 / 遍历配对列表esp_now_is_peer_exist()判断对端是否已在列表中esp_now_get_peer_num()获取当前配对总数与加密对端数esp_now_unregister_recv_cb()/esp_now_unregister_send_cb()注销收发回调esp_now_switch_channel_tx()/esp_now_remain_on_channel()切换到指定信道并发送数据 / 在目标信道停留指定时长esp_now_get_version()查询当前 ESP-NOW 协议版本小结ESP-NOW 以供应商动作帧为载体通过 PMK/LMK 两级密钥与 CCMP 加密提供安全能力并以“无连接、免握手”的特性成为智能照明、远程控制、传感器等低延迟、低开销物联网场景的理想选择。开发时需重点把握先启动 Wi-Fi 再初始化 ESP-NOW、发送前添加对端、收发回调中不做耗时操作、严格控制发送节奏与数据长度。结合 espnow 示例工程 的广播握手、CRC 校验、序列号去重与队列解耦设计即可快速搭建出稳定可靠的 ESP-NOW 点对点通信应用。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考