add ESPNOW feature

ESPNOW is a kind of WiFi communication bettween WiFi devices, no
    matter they are connected to each other or not. Once two WiFi
    devices are paired, they can send data to or receive data from
    each other. The data is transmitted in action frame which can
    be encrypted with CCMP method. ESPNOW also support mutilcast frame
    transmitting.
This commit is contained in:
XiaXiaotian 2017-07-12 11:50:39 +08:00
parent 63e1e4e502
commit e4ba31a8b9
6 changed files with 422 additions and 2 deletions

View file

@ -5,7 +5,7 @@
COMPONENT_SRCDIRS := . hwcrypto
LIBS ?=
ifndef CONFIG_NO_BLOBS
LIBS += core rtc net80211 pp wpa smartconfig coexist wps wpa2 phy
LIBS += core rtc net80211 pp wpa smartconfig coexist wps wpa2 espnow phy
endif
#Linker scripts used to link the final application.

View file

@ -0,0 +1,315 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_NOW_H__
#define __ESP_NOW_H__
#include <stdbool.h>
#include "esp_err.h"
#include "esp_wifi_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup WiFi_APIs WiFi Related APIs
* @brief WiFi APIs
*/
/** @addtogroup WiFi_APIs
* @{
*/
/** \defgroup ESPNOW_APIs ESPNOW APIs
* @brief ESP32 ESPNOW APIs
*
*/
/** @addtogroup ESPNOW_APIs
* @{
*/
#define ESP_ERR_ESPNOW_BASE (ESP_ERR_WIFI_BASE + 101) /*!< ESPNOW error number base. */
#define ESP_ERR_ESPNOW_NOT_INIT (ESP_ERR_ESPNOW_BASE) /*!< ESPNOW is not initialized. */
#define ESP_ERR_ESPNOW_ARG (ESP_ERR_ESPNOW_BASE + 1) /*!< Invalid argument */
#define ESP_ERR_ESPNOW_NO_MEM (ESP_ERR_ESPNOW_BASE + 2) /*!< Out of memory */
#define ESP_ERR_ESPNOW_FULL (ESP_ERR_ESPNOW_BASE + 3) /*!< ESPNOW peer list is full */
#define ESP_ERR_ESPNOW_NOT_FOUND (ESP_ERR_ESPNOW_BASE + 4) /*!< ESPNOW peer is not found */
#define ESP_ERR_ESPNOW_INTERNAL (ESP_ERR_ESPNOW_BASE + 5) /*!< Internal error */
#define ESP_ERR_ESPNOW_EXIST (ESP_ERR_ESPNOW_BASE + 6) /*!< ESPNOW peer has existed */
#define ESP_NOW_ETH_ALEN 6 /*!< Length of ESPNOW peer MAC address */
#define ESP_NOW_KEY_LEN 16 /*!< Length of ESPNOW peer local master key */
#define ESP_NOW_MAX_TOTAL_PEER_NUM 20 /*!< Maximum number of ESPNOW total peers */
#define ESP_NOW_MAX_ENCRYPT_PEER_NUM 6 /*!< Maximum number of ESPNOW encrypted peers */
#define ESP_NOW_MAX_DATA_LEN 250 /*!< Maximum length of ESPNOW data which is sent very time */
/**
* @brief Status of sending ESPNOW data .
*/
typedef enum {
ESP_NOW_SEND_SUCCESS = 0, /**< Send ESPNOW data successfully */
ESP_NOW_SEND_FAIL, /**< Send ESPNOW data fail */
} esp_now_send_status_t;
/**
* @brief ESPNOW peer information parameters.
*/
typedef struct esp_now_peer_info {
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 */
} esp_now_peer_info_t;
/**
* @brief Number of ESPNOW peers which exist currently.
*/
typedef struct esp_now_peer_num {
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 */
} esp_now_peer_num_t;
/**
* @brief Callback function of receiving ESPNOW data
* @param mac_addr peer MAC address
* @param data received data
* @param data_len length of received data
*/
typedef void (*esp_now_recv_cb_t)(const uint8_t *mac_addr, const uint8_t *data, int data_len);
/**
* @brief Callback function of sending ESPNOW data
* @param mac_addr peer MAC address
* @param status status of sending ESPNOW data (succeed or fail)
*/
typedef void (*esp_now_send_cb_t)(const uint8_t *mac_addr, esp_now_send_status_t status);
/**
* @brief Initialize ESPNOW function
*
* @return
* - ESP_OK : succeed
* - ESP_ERR_ESPNOW_INTERNAL : Internal error
*/
esp_err_t esp_now_init(void);
/**
* @brief De-initialize ESPNOW function
*
* @return
* - ESP_OK : succeed
*/
esp_err_t esp_now_deinit(void);
/**
* @brief Get the version of ESPNOW
*
* @param version ESPNOW version
*
* @return
* - ESP_OK : succeed
* - ESP_ERR_ESPNOW_ARG : invalid argument
*/
esp_err_t esp_now_get_version(uint32_t *version);
/**
* @brief Register callback function of receiving ESPNOW data
*
* @param cb 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
*/
esp_err_t esp_now_register_recv_cb(esp_now_recv_cb_t cb);
/**
* @brief 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_unregister_recv_cb(void);
/**
* @brief Register callback function of sending ESPNOW data
*
* @param cb 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
*/
esp_err_t esp_now_register_send_cb(esp_now_send_cb_t cb);
/**
* @brief 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_unregister_send_cb(void);
/**
* @brief 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
*
* @param peer_addr peer MAC address
* @param data data to send
* @param len length of data
*
* @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
* - ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
*/
esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len);
/**
* @brief Add a peer to peer list
*
* @param peer peer information
*
* @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
*/
esp_err_t esp_now_add_peer(const esp_now_peer_info_t *peer);
/**
* @brief Delete a peer from peer list
*
* @param peer_addr peer MAC address
*
* @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
*/
esp_err_t esp_now_del_peer(const uint8_t *peer_addr);
/**
* @brief Modify a peer
*
* @param peer peer information
*
* @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_t esp_now_mod_peer(const esp_now_peer_info_t *peer);
/**
* @brief Get a peer whose MAC address matches peer_addr from peer list
*
* @param peer_addr peer MAC address
* @param peer peer information
*
* @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
*/
esp_err_t esp_now_get_peer(const uint8_t *peer_addr, esp_now_peer_info_t *peer);
/**
* @brief Fetch a peer from peer list
*
* @param from_head fetch from head of list or not
* @param peer peer information
*
* @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
*/
esp_err_t esp_now_fetch_peer(bool from_head, esp_now_peer_info_t *peer);
/**
* @brief Peer exists or not
*
* @param peer_addr peer MAC address
*
* @return
* - true : peer exists
* - false : peer not exists
*/
bool esp_now_is_peer_exist(const uint8_t *peer_addr);
/**
* @brief Get the number of peers
*
* @param num number of peers
*
* @return
* - ESP_OK : succeed
* - ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
* - ESP_ERR_ESPNOW_ARG : invalid argument
*/
esp_err_t esp_now_get_peer_num(esp_now_peer_num_t *num);
/**
* @brief Set the primary master key
*
* @param pmk 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
*/
esp_err_t esp_now_set_pmk(const uint8_t *pmk);
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __ESP_NOW_H__ */

@ -1 +1 @@
Subproject commit d8119f2d8b121f7bca072b3cc8e2e3a26b0b7d44
Subproject commit 3fe7f3a0a5f570b22182063fcc6d7dad6875dfbd

View file

@ -27,6 +27,7 @@ INPUT = \
../components/esp32/include/esp_wifi.h \
../components/esp32/include/esp_wifi_crypto_types.h \
../components/esp32/include/esp_smartconfig.h \
../components/esp32/include/esp_now.h \
## Bluetooth - API Reference
## Controller && VHCI
../components/bt/include/bt.h \

View file

@ -0,0 +1,103 @@
ESP-NOW
======
Overview
--------
ESP-NOW is a kind of connectionless WiFi communication protocol which is defined by Espressif. In ESP-NOW, application data is
encapsulated in vendor-specific action frame and then transmitted from one WiFi 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 vendor-specific action frame to transmit ESP-NOW data. The format of vendor-specific action frame is as follows:
.. highlight:: none
::
----------------------------------------------------------------------------------------
| MAC Header | Category Code | Organization Identifier | Vendor Specific Content | FCS |
----------------------------------------------------------------------------------------
1 byte 3 bytes 7~255 bytes
- Category Code: The Category 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.
- Vendor Specific Content: The Vendor Specific Content contains vendor-specific field as follows:
.. highlight:: none
::
-------------------------------------------------------------------------------
| 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 use CCMP method which can be referenced in IEEE Std. 802.11-2012 to protect the vendor-specific action frame. The WiFi
device maintains a Primary Master Key(PMK) and several Local Master Keys(LMK). The lengths of them are 16 bytes. PMK is used
to encrypt LMK with AES-128 algorithm. Call ``esp_now_set_pmk()`` to set PMK. If PMK is not set, a default PMK will be used.
If LMK of the paired device is set, it will be used to encrypt the vendor-specific action frame with CCMP method. The maximum
number of different LMKs is six. Do not support encrypting multicast vendor-specific action frame.
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 WiFi is started, so it is recommended to start WiFi before initializing ESP-NOW and stop WiFi after de-initializing ESP-NOW.
When ``esp_now_deinit()`` is called, all of the information of paired devices will be deleted.
Add Paired Device
-----------------
Before sending data to other device, call ``esp_now_add_peer()`` to add it to the paired device list first. The maximum number of
paired devices is twenty. If security is enabled, the LMK must be set. ESP-NOW data can be sent from station or softap interface.
Make sure that the interface is enabled before sending ESP-NOW data. A device with broadcast MAC address must be added before
sending broadcast data. The range of the channel of paired device 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 MAC layer. Otherwise, it will return
`ESP_NOW_SEND_FAIL`. There are several reasons failing to send ESP-NOW data, for example, the destination device doesn't exist, the
channels of the devices are not the same, the action frame is lost when transmiting 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 timeout
happens, retransmit the ESP-NOW data. A sequence number can also be assigned to ESP-NOW data to drop the duplicated 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 datas 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 previous sending has returned. The sending
callback function runs from a high-priority WiFi task. So, do not do lengthy operations in the callback function. Instead, post
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. When receiving ESP-NOW data, receiving callback function
is called. The receiving callback function also runs from WiFi task. So, do not do lengthy operations in the callback function.
Instead, post necessary data to a queue and handle it from a lower priority task.
API Reference
-------------
.. include:: /_build/inc/esp_now.inc

View file

@ -6,6 +6,7 @@ Wi-Fi API
Wi-Fi <esp_wifi>
Smart Config <esp_smartconfig>
ESPNOW <esp_now>
Example code for this API section is provided in :example:`wifi` directory of ESP-IDF examples.