ESP-NOW

[中文]

Overview

ESP-NOW is a kind of connectionless Wi-Fi communication protocol that is defined by Espressif. In ESP-NOW, application data is encapsulated in a vendor-specific action frame and then transmitted from one Wi-Fi device to another without connection. CTR with CBC-MAC Protocol(CCMP) is used to protect the action frame for security. ESP-NOW is widely used in smart light, remote controlling, sensor, etc.

Frame Format

ESP-NOW uses a vendor-specific action frame to transmit ESP-NOW data. The default ESP-NOW bit rate is 1 Mbps. The format of the vendor-specific action frame is as follows:

------------------------------------------------------------------------------------------------------------
| MAC Header | Category Code | Organization Identifier | Random Values | Vendor Specific Content |   FCS   |
------------------------------------------------------------------------------------------------------------
  24 bytes         1 byte              3 bytes               4 bytes             7~255 bytes        4 bytes
  • Category Code: The Category Code field is set to the value(127) indicating the vendor-specific category.

  • Organization Identifier: The Organization Identifier contains a unique identifier (0x18fe34), which is the first three bytes of MAC address applied by Espressif.

  • Random Value: The Random Value filed is used to prevents relay attacks.

  • Vendor Specific Content: The Vendor Specific Content contains vendor-specific fields as follows:

-------------------------------------------------------------------------------
| Element ID | Length | Organization Identifier | Type | Version |    Body    |
-------------------------------------------------------------------------------
    1 byte     1 byte            3 bytes         1 byte   1 byte   0~250 bytes
  • Element ID: The Element ID field is set to the value (221), indicating the vendor-specific element.

  • Length: The length is the total length of Organization Identifier, Type, Version and Body.

  • Organization Identifier: The Organization Identifier contains a unique identifier(0x18fe34), which is the first three bytes of MAC address applied by Espressif.

  • Type: The Type field is set to the value (4) indicating ESP-NOW.

  • Version: The Version field is set to the version of ESP-NOW.

  • Body: The Body contains the ESP-NOW data.

As ESP-NOW is connectionless, the MAC header is a little different from that of standard frames. The FromDS and ToDS bits of FrameControl field are both 0. The first address field is set to the destination address. The second address field is set to the source address. The third address field is set to broadcast address (0xff:0xff:0xff:0xff:0xff:0xff).

Security

ESP-NOW uses the CCMP method, which is described in IEEE Std. 802.11-2012, to protect the vendor-specific action frame. The Wi-Fi device maintains a Primary Master Key (PMK) and several Local Master Keys (LMK). The lengths of both PMK and LMk are 16 bytes.
  • PMK is used to encrypt LMK with the AES-128 algorithm. Call esp_now_set_pmk() to set PMK. If PMK is not set, a default PMK will be used.

  • LMK of the paired device is used to encrypt the vendor-specific action frame with the CCMP method. The maximum number of different LMKs is six. If the LMK of the paired device is not set, the vendor-specific action frame will not be encrypted.

Encrypting multicast vendor-specific action frame is not supported.

Initialization and De-initialization

Call esp_now_init() to initialize ESP-NOW and esp_now_deinit() to de-initialize ESP-NOW. ESP-NOW data must be transmitted after Wi-Fi is started, so it is recommended to start Wi-Fi before initializing ESP-NOW and stop Wi-Fi after de-initializing ESP-NOW. When esp_now_deinit() is called, all of the information of paired devices will be deleted.

Add Paired Device

Call esp_now_add_peer() to add the device to the paired device list before you send data to this device. If security is enabled, the LMK must be set. You can send ESP-NOW data via both the Station and the SoftAP interface. Make sure that the interface is enabled before sending ESP-NOW data.

The maximum number of paired devices is 20, and the paired encryption devices are no more than 16, the default is 6.

A device with a broadcast MAC address must be added before sending broadcast data. The range of the channel of paired devices is from 0 to 14. If the channel is set to 0, data will be sent on the current channel. Otherwise, the channel must be set as the channel that the local device is on.

Send ESP-NOW Data

Call esp_now_send() to send ESP-NOW data and esp_now_register_send_cb() to register sending callback function. It will return ESP_NOW_SEND_SUCCESS in sending callback function if the data is received successfully on the MAC layer. Otherwise, it will return ESP_NOW_SEND_FAIL. Several reasons can lead to ESP-NOW fails to send data. For example, the destination device doesn’t exist; the channels of the devices are not the same; the action frame is lost when transmitting on the air, etc. It is not guaranteed that application layer can receive the data. If necessary, send back ack data when receiving ESP-NOW data. If receiving ack data timeouts, retransmit the ESP-NOW data. A sequence number can also be assigned to ESP-NOW data to drop the duplicate data.

If there is a lot of ESP-NOW data to send, call esp_now_send() to send less than or equal to 250 bytes of data once a time. Note that too short interval between sending two ESP-NOW data may lead to disorder of sending callback function. So, it is recommended that sending the next ESP-NOW data after the sending callback function of the previous sending has returned. The sending callback function runs from a high-priority Wi-Fi task. So, do not do lengthy operations in the callback function. Instead, post the necessary data to a queue and handle it from a lower priority task.

Receiving ESP-NOW Data

Call esp_now_register_recv_cb() to register receiving callback function. Call the receiving callback function when receiving ESP-NOW. The receiving callback function also runs from the Wi-Fi task. So, do not do lengthy operations in the callback function. Instead, post the necessary data to a queue and handle it from a lower priority task.

API Reference

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_deinit(void)

De-initialize ESPNOW function.

Return

  • ESP_OK : succeed

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 address

  • data: data to send

  • len: 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 address

  • peer: 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 not

  • peer: 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

esp_err_t esp_now_set_pmk(const uint8_t *pmk)

Set the primary master key.

Attention

1. primary master key is used to encrypt local master key

Return

  • ESP_OK : succeed

  • ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized

  • ESP_ERR_ESPNOW_ARG : invalid argument

Parameters
  • pmk: primary master key

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

struct esp_now_peer_num

Number of ESPNOW peers which exist currently.

Public Members

int total_num

Total number of ESPNOW peers, maximum value is ESP_NOW_MAX_TOTAL_PEER_NUM

int encrypt_num

Number of encrypted ESPNOW peers, maximum value is ESP_NOW_MAX_ENCRYPT_PEER_NUM

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 address

  • data: received data

  • data_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 address

  • status: status of sending ESPNOW data (succeed or fail)

Enumerations

enum esp_now_send_status_t

Status of sending ESPNOW data .

Values:

ESP_NOW_SEND_SUCCESS = 0

Send ESPNOW data successfully

ESP_NOW_SEND_FAIL

Send ESPNOW data fail