ESP-NOW¶
概述¶
ESP-NOW 是一种由乐鑫公司定义的无连接 Wi-Fi 通信协议。在 ESP-NOW 中,应用程序数据被封装在各个供应商的动作帧中,然后在无连接的情况下,从一个 Wi-Fi 设备传输到另一个 Wi-Fi 设备。 CTR 与 CBC-MAC 协议 (CCMP) 可用来保护动作帧的安全。ESP-NOW 广泛应用于智能照明、远程控制、传感器等领域。
帧格式¶
ESP-NOW 使用各个供应商的动作帧传输数据,默认比特率为 1 Mbps。各个供应商的动作帧格式为:
-----------------------------------------------------------------------------------------
| MAC 报头 | 分类代码 | 组织标识符 | 随机值 | 供应商特定内容 | FCS |
-----------------------------------------------------------------------------------------
24 字节 1 字节 3 字节 4 字节 7~255 字节 4 字节
分类代码:分类代码字段可用于指示各个供应商的类别(比如 127)。
组织标识符:组织标识符包含一个唯一标识符 (比如 0x18fe34),为乐鑫指定的 MAC 地址的前三个字节。
随机值:防止重放攻击。
供应商特定内容:供应商特定内容包含供应商特定字段,如下所示:
----------------------------------------------------------------------------------------
| 元素 ID | 长度 | 组织标识符 | 类型 | 版本 | 正文 |
----------------------------------------------------------------------------------------
1 字节 1 字节 3 字节 1 字节 1 字节 0~250 字节
元素 ID:元素 ID 字段可用于指示特定于供应商的元素。
长度:长度是组织标识符、类型、版本和正文的总长度。
组织标识符:组织标识符包含一个唯一标识符 (比如 0x18fe34),为乐鑫指定的 MAC 地址的前三个字节。
类型:类型字段设置为 4,代表 ESP-NOW。
版本:版本字段设置为 ESP-NOW 的版本。
正文:正文包含 ESP-NOW 数据。
由于 ESP-NOW 是无连接的,因此 MAC 报头与标准帧略有不同。FrameControl 字段的 FromDS 和 ToDS 位均为 0。第一个地址字段用于配置目标地址。第二个地址字段用于配置源地址。第三个地址字段用于配置广播地址 (0xff:0xff:0xff:0xff:0xff:0xff)。
安全¶
- ESP-NOW 采用 CCMP 方法保护供应商特定动作帧的安全,具体可参考 IEEE Std. 802.11-2012。Wi-Fi 设备维护一个初始主密钥 (PMK) 和若干本地主密钥 (LMK),长度均为 16 个字节。
PMK 可使用 AES-128 算法加密 LMK。请调用
esp_now_set_pmk()
设置 PMK。如果未设置 PMK,将使用默认 PMK。LMK 可通过 CCMP 方法对供应商特定的动作帧进行加密,最多拥有 6 个不同的 LMK。如果未设置配对设备的 LMK,则动作帧不进行加密。
目前,不支持加密组播供应商特定的动作帧。
初始化和反初始化¶
调用 esp_now_init()
初始化 ESP-NOW,调用 esp_now_deinit()
反初始化 ESP-NOW。ESP-NOW 数据必须在 Wi-Fi 启动后传输,因此建议在初始化 ESP-NOW 之前启动 Wi-Fi,并在反初始化 ESP-NOW 之后停止 Wi-Fi。
当调用 esp_now_deinit()
时,配对设备的所有信息都将被删除。
添加配对设备¶
在将数据发送到其他设备之前,请先调用 esp_now_add_peer()
将其添加到配对设备列表中。如果启用了加密,则必须设置 LMK。ESP-NOW 数据可以从 Station 或 Softap 接口发送。确保在发送 ESP-NOW 数据之前已启用该接口。
配对设备的最大数量是 20,其中加密设备的数量不超过 16,默认值是 6。
在发送广播数据之前必须添加具有广播 MAC 地址的设备。配对设备的信道范围是从 0 ~14。如果信道设置为 0,数据将在当前信道上发送。否则,必须使用本地设备所在的通道。
发送 ESP-NOW 数据¶
调用 esp_now_send()
发送 ESP-NOW 数据,调用 esp_now_register_send_cb()
注册发送回调函数。如果 MAC 层成功接收到数据,则该函数将返回 ESP_NOW_SEND_SUCCESS 事件。否则,它将返回 ESP_NOW_SEND_FAIL。ESP-NOW 数据发送失败可能有几种原因,比如目标设备不存在、设备的信道不相同、动作帧在传输过程中丢失等。应用层并不一定可以总能接收到数据。如果需要,应用层可在接收 ESP-NOW 数据时发回一个应答 (ACK) 数据。如果接收 ACK 数据超时,则将重新传输 ESP-NOW 数据。可以为 ESP-NOW 数据设置序列号,从而删除重复的数据。
如果有大量 ESP-NOW 数据要发送,则调用 esp_now_send()
一次性发送不大于 250 字节的数据。
请注意,两个 ESP-NOW 数据包的发送间隔太短可能导致回调函数返回混乱。因此,建议在等到上一次回调函数返回 ACK 后再发送下一个 ESP-NOW 数据。发送回调函数从高优先级的 Wi-Fi 任务中运行。因此,不要在回调函数中执行冗长的操作。相反,将必要的数据发布到队列,并交给优先级较低的任务处理。
接收 ESP-NOW 数据¶
调用 esp_now_register_recv_cb()
注册接收回调函数。当接收 ESP-NOW 数据时,需要调用接收回调函数。接收回调函数也在 Wi-Fi 任务任务中运行。因此,不要在回调函数中执行冗长的操作。
相反,将必要的数据发布到队列,并交给优先级较低的任务处理。
API 参考¶
Header File¶
Functions¶
-
esp_err_t
esp_now_init
(void)¶ Initialize ESPNOW function.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_INTERNAL : Internal error
-
esp_err_t
esp_now_get_version
(uint32_t *version)¶ Get the version of ESPNOW.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_ARG : invalid argument
- Parameters
version
: ESPNOW version
-
esp_err_t
esp_now_register_recv_cb
(esp_now_recv_cb_t cb)¶ Register callback function of receiving ESPNOW data.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_INTERNAL : internal error
- Parameters
cb
: callback function of receiving ESPNOW data
-
esp_err_t
esp_now_unregister_recv_cb
(void)¶ Unregister callback function of receiving ESPNOW data.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
-
esp_err_t
esp_now_register_send_cb
(esp_now_send_cb_t cb)¶ Register callback function of sending ESPNOW data.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_INTERNAL : internal error
- Parameters
cb
: callback function of sending ESPNOW data
-
esp_err_t
esp_now_unregister_send_cb
(void)¶ Unregister callback function of sending ESPNOW data.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
-
esp_err_t
esp_now_send
(const uint8_t *peer_addr, const uint8_t *data, size_t len)¶ Send ESPNOW data.
- Attention
1. If peer_addr is not NULL, send data to the peer whose MAC address matches peer_addr
- Attention
2. If peer_addr is NULL, send data to all of the peers that are added to the peer list
- Attention
3. The maximum length of data must be less than ESP_NOW_MAX_DATA_LEN
- Attention
4. The buffer pointed to by data argument does not need to be valid after esp_now_send returns
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_ARG : invalid argument
ESP_ERR_ESPNOW_INTERNAL : internal error
ESP_ERR_ESPNOW_NO_MEM : out of memory, when this happens, you can delay a while before sending the next data
ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
ESP_ERR_ESPNOW_IF : current WiFi interface doesn’t match that of peer
- Parameters
peer_addr
: peer MAC addressdata
: data to sendlen
: length of data
-
esp_err_t
esp_now_add_peer
(const esp_now_peer_info_t *peer)¶ Add a peer to peer list.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_ARG : invalid argument
ESP_ERR_ESPNOW_FULL : peer list is full
ESP_ERR_ESPNOW_NO_MEM : out of memory
ESP_ERR_ESPNOW_EXIST : peer has existed
- Parameters
peer
: peer information
-
esp_err_t
esp_now_del_peer
(const uint8_t *peer_addr)¶ Delete a peer from peer list.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_ARG : invalid argument
ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
- Parameters
peer_addr
: peer MAC address
-
esp_err_t
esp_now_mod_peer
(const esp_now_peer_info_t *peer)¶ Modify a peer.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_ARG : invalid argument
ESP_ERR_ESPNOW_FULL : peer list is full
- Parameters
peer
: peer information
-
esp_err_t
esp_now_get_peer
(const uint8_t *peer_addr, esp_now_peer_info_t *peer)¶ Get a peer whose MAC address matches peer_addr from peer list.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_ARG : invalid argument
ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
- Parameters
peer_addr
: peer MAC addresspeer
: peer information
-
esp_err_t
esp_now_fetch_peer
(bool from_head, esp_now_peer_info_t *peer)¶ Fetch a peer from peer list. Only return the peer which address is unicast, for the multicast/broadcast address, the function will ignore and try to find the next in the peer list.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_ARG : invalid argument
ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
- Parameters
from_head
: fetch from head of list or notpeer
: peer information
-
bool
esp_now_is_peer_exist
(const uint8_t *peer_addr)¶ Peer exists or not.
- Return
true : peer exists
false : peer not exists
- Parameters
peer_addr
: peer MAC address
-
esp_err_t
esp_now_get_peer_num
(esp_now_peer_num_t *num)¶ Get the number of peers.
- Return
ESP_OK : succeed
ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
ESP_ERR_ESPNOW_ARG : invalid argument
- Parameters
num
: number of peers
Structures¶
-
struct
esp_now_peer_info
¶ ESPNOW peer information parameters.
Public Members
-
uint8_t
peer_addr
[ESP_NOW_ETH_ALEN
]¶ ESPNOW peer MAC address that is also the MAC address of station or softap
-
uint8_t
lmk
[ESP_NOW_KEY_LEN
]¶ ESPNOW peer local master key that is used to encrypt data
-
uint8_t
channel
¶ Wi-Fi channel that peer uses to send/receive ESPNOW data. If the value is 0, use the current channel which station or softap is on. Otherwise, it must be set as the channel that station or softap is on.
-
wifi_interface_t
ifidx
¶ Wi-Fi interface that peer uses to send/receive ESPNOW data
-
bool
encrypt
¶ ESPNOW data that this peer sends/receives is encrypted or not
-
void *
priv
¶ ESPNOW peer private data
-
uint8_t
-
struct
esp_now_peer_num
¶ Number of ESPNOW peers which exist currently.
Macros¶
-
ESP_ERR_ESPNOW_BASE
¶ ESPNOW error number base.
-
ESP_ERR_ESPNOW_NOT_INIT
¶ ESPNOW is not initialized.
-
ESP_ERR_ESPNOW_ARG
¶ Invalid argument
-
ESP_ERR_ESPNOW_NO_MEM
¶ Out of memory
-
ESP_ERR_ESPNOW_FULL
¶ ESPNOW peer list is full
-
ESP_ERR_ESPNOW_NOT_FOUND
¶ ESPNOW peer is not found
-
ESP_ERR_ESPNOW_INTERNAL
¶ Internal error
-
ESP_ERR_ESPNOW_EXIST
¶ ESPNOW peer has existed
-
ESP_ERR_ESPNOW_IF
¶ Interface error
-
ESP_NOW_ETH_ALEN
¶ Length of ESPNOW peer MAC address
-
ESP_NOW_KEY_LEN
¶ Length of ESPNOW peer local master key
-
ESP_NOW_MAX_TOTAL_PEER_NUM
¶ Maximum number of ESPNOW total peers
-
ESP_NOW_MAX_ENCRYPT_PEER_NUM
¶ Maximum number of ESPNOW encrypted peers
-
ESP_NOW_MAX_DATA_LEN
¶ Maximum length of ESPNOW data which is sent very time
Type Definitions¶
-
typedef struct esp_now_peer_info
esp_now_peer_info_t
¶ ESPNOW peer information parameters.
-
typedef struct esp_now_peer_num
esp_now_peer_num_t
¶ Number of ESPNOW peers which exist currently.
-
typedef void (*
esp_now_recv_cb_t
)(const uint8_t *mac_addr, const uint8_t *data, int data_len)¶ Callback function of receiving ESPNOW data.
- Parameters
mac_addr
: peer MAC addressdata
: received datadata_len
: length of received data
-
typedef void (*
esp_now_send_cb_t
)(const uint8_t *mac_addr, esp_now_send_status_t status)¶ Callback function of sending ESPNOW data.
- Parameters
mac_addr
: peer MAC addressstatus
: status of sending ESPNOW data (succeed or fail)