Add an example to show how to use ESPNOW

This commit is contained in:
XiaXiaotian 2017-09-13 20:38:04 +08:00
parent d50aca62c3
commit 3a1d34a7be
7 changed files with 575 additions and 0 deletions

View file

@ -20,6 +20,12 @@ shows how to use wps(Wifi Protected Setup).
See the [README.md](./wps/README.md) file in the project [wps](./wps/).
## espnow
shows how to use espnow.
See the [README.md](./espnow/README.md) file in the project [espnow](./espnow/).
# More
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := espnow_example
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,30 @@
# ESPNOW Example
This example shows how to use ESPNOW of wifi. Example does the following steps:
1. Start WiFi.
2. Initialize ESPNOW.
3. Register ESPNOW sending or receiving callback function.
4. Add ESPNOW peer information.
5. Send and receive ESPNOW data.
In order to get the MAC address of the other device, firstly send broadcast ESPNOW data to each other with 'state' set as 0. When receiving
broadcast ESPNOW data with 'state' as 0, add the device from which the data comes to the peer list. Then start sending broadcast ESPNOW
data with 'state' set as 1. When receiving broadcast ESPNOW data with 'state' as 1, compare the local magic number with that in the data.
If the local one is bigger than that one, stop sending broadcast ESPNOW data and start sending unicast ESPNOW data. If receive unicast
ESPNOW data, also stop sending broadcast ESPNOW data. That is what happens in this example. It shows how to send/receive broadcast/unicast
ESPNOW data. In practice, if the MAC address of the other device is known, it's not required to send/receive broadcast ESPNOW data first,
just add the device to the peer list and send/receive unicast ESPNOW data.
There are a lot of "extras" on top of ESPNOW data, such as type, state, sequence number, CRC and magic in this example. These "extras" are
not required to use ESPNOW. They are only used to make this example to run correctly. However, it is recommended that users add some "extras"
to make ESPNOW data more safe and more reliable.
*Note:* The two devices can be set as either station or softap or station+softap mode. If the receiving device is in station mode only
and it connects to an AP, modem sleep should be disabled.
More info in the code [espnow_example_main.c](./main/espnow_example_main.c).

View file

@ -0,0 +1,55 @@
menu "Example Configuration"
choice WIFI_MODE
prompt "WiFi mode"
default STATION_MODE
help
WiFi mode(station or softap).
config STATION_MODE
bool "Station"
config SOFTAP_MODE
bool "Softap"
endchoice
config ESPNOW_PMK
string "ESPNOW primary master key"
default "pmk1234567890123"
help
ESPNOW primary master for the example to use. The length of ESPNOW primary master must be 16 bytes.
config ESPNOW_LMK
string "ESPNOW local master key"
default "lmk1234567890123"
help
ESPNOW local master for the example to use. The length of ESPNOW local master must be 16 bytes.
config ESPNOW_CHANNEL
int "Channel"
default 1
range 1 13
help
The channel on which sending and receiving ESPNOW data.
config ESPNOW_SEND_COUNT
int "Send count"
default 100
range 1 65535
help
Total count of unicast ESPNOW data to be sent.
config ESPNOW_SEND_DELAY
int "Send delay"
default 1000
range 0 65535
help
Delay between sending two ESPNOW data, unit: ms.
config ESPNOW_SEND_LEN
int "Send len"
range 10 250
default 200
help
Length of ESPNOW data to be sent, unit: byte.
endmenu

View file

@ -0,0 +1,7 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,82 @@
/* ESPNOW Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef ESPNOW_EXAMPLE_H
#define ESPNOW_EXAMPLE_H
/* ESPNOW can work in both station and softap mode. It is configured in menuconfig. */
#if CONFIG_STATION_MODE
#define ESPNOW_WIFI_MODE WIFI_MODE_STA
#define ESPNOW_WIFI_IF ESP_IF_WIFI_STA
#else
#define ESPNOW_WIFI_MODE WIFI_MODE_AP
#define ESPNOW_WIFI_IF ESP_IF_WIFI_AP
#endif
#define ESPNOW_QUEUE_SIZE 6
#define IS_BROADCAST_ADDR(addr) (memcmp(addr, example_broadcast_mac, ESP_NOW_ETH_ALEN) == 0)
typedef enum {
EXAMPLE_ESPNOW_SEND_CB,
EXAMPLE_ESPNOW_RECV_CB,
} example_espnow_event_id_t;
typedef struct {
uint8_t mac_addr[ESP_NOW_ETH_ALEN];
esp_now_send_status_t status;
} example_espnow_event_send_cb_t;
typedef struct {
uint8_t mac_addr[ESP_NOW_ETH_ALEN];
uint8_t *data;
int data_len;
} example_espnow_event_recv_cb_t;
typedef union {
example_espnow_event_send_cb_t send_cb;
example_espnow_event_recv_cb_t recv_cb;
} example_espnow_event_info_t;
/* When ESPNOW sending or receiving callback function is called, post event to ESPNOW task. */
typedef struct {
example_espnow_event_id_t id;
example_espnow_event_info_t info;
} example_espnow_event_t;
enum {
EXAMPLE_ESPNOW_DATA_BROADCAST,
EXAMPLE_ESPNOW_DATA_UNICAST,
EXAMPLE_ESPNOW_DATA_MAX,
};
/* User defined field of ESPNOW data in this example. */
typedef struct {
uint8_t type; //Broadcast or unicast ESPNOW data.
uint8_t state; //Indicate that if has received broadcast ESPNOW data or not.
uint16_t seq_num; //Sequence number of ESPNOW data.
uint16_t crc; //CRC16 value of ESPNOW data.
uint32_t magic; //Magic number which is used to determine which device to send unicast ESPNOW data.
uint8_t payload[0]; //Real payload of ESPNOW data.
} __attribute__((packed)) example_espnow_data_t;
/* Parameters of sending ESPNOW data. */
typedef struct {
bool unicast; //Send unicast ESPNOW data.
bool broadcast; //Send broadcast ESPNOW data.
uint8_t state; //Indicate that if has received broadcast ESPNOW data or not.
uint32_t magic; //Magic number which is used to determine which device to send unicast ESPNOW data.
uint16_t count; //Total count of unicast ESPNOW data to be sent.
uint16_t delay; //Delay between sending two ESPNOW data, unit: ms.
int len; //Length of ESPNOW data to be sent, unit: byte.
uint8_t *buffer; //Buffer pointing to ESPNOW data.
uint8_t dest_mac[ESP_NOW_ETH_ALEN]; //MAC address of destination device.
} example_espnow_send_param_t;
#endif

View file

@ -0,0 +1,386 @@
/* ESPNOW Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
This example shows how to use ESPNOW.
Prepare two device, one for sending ESPNOW data and another for receiving
ESPNOW data.
*/
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "nvs_flash.h"
#include "esp_event_loop.h"
#include "tcpip_adapter.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_now.h"
#include "rom/ets_sys.h"
#include "rom/crc.h"
#include "espnow_example.h"
static const char *TAG = "espnow_example";
static xQueueHandle example_espnow_queue;
static uint8_t example_broadcast_mac[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static uint16_t s_example_espnow_seq[EXAMPLE_ESPNOW_DATA_MAX] = { 0, 0 };
static void example_espnow_deinit(example_espnow_send_param_t *send_param);
static esp_err_t example_event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
ESP_LOGI(TAG, "WiFi started");
break;
default:
break;
}
return ESP_OK;
}
/* WiFi should start before using ESPNOW */
static void example_wifi_init(void)
{
tcpip_adapter_init();
ESP_ERROR_CHECK( esp_event_loop_init(example_event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
ESP_ERROR_CHECK( esp_wifi_set_mode(ESPNOW_WIFI_MODE) );
ESP_ERROR_CHECK( esp_wifi_start());
/* In order to simplify example, channel is set after WiFi started.
* This is not necessary in real application if the two devices have
* been already on the same channel.
*/
ESP_ERROR_CHECK( esp_wifi_set_channel(CONFIG_ESPNOW_CHANNEL, 0) );
}
/* ESPNOW sending or receiving callback function is called in WiFi task.
* Users should not do lengthy operations from this task. Instead, post
* necessary data to a queue and handle it from a lower priority task. */
static void example_espnow_send_cb(const uint8_t *mac_addr, esp_now_send_status_t status)
{
example_espnow_event_t evt;
example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb;
if (mac_addr == NULL) {
ESP_LOGE(TAG, "Send cb arg error");
return;
}
evt.id = EXAMPLE_ESPNOW_SEND_CB;
memcpy(send_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
send_cb->status = status;
if (xQueueSend(example_espnow_queue, &evt, portMAX_DELAY) != pdTRUE) {
ESP_LOGW(TAG, "Send send queue fail");
}
}
static void example_espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len)
{
example_espnow_event_t evt;
example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb;
if (mac_addr == NULL || data == NULL || len <= 0) {
ESP_LOGE(TAG, "Receive cb arg error");
return;
}
evt.id = EXAMPLE_ESPNOW_RECV_CB;
memcpy(recv_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
recv_cb->data = malloc(len);
if (recv_cb->data == NULL) {
ESP_LOGE(TAG, "Malloc receive data fail");
return;
}
memcpy(recv_cb->data, data, len);
recv_cb->data_len = len;
if (xQueueSend(example_espnow_queue, &evt, portMAX_DELAY) != pdTRUE) {
ESP_LOGW(TAG, "Send receive queue fail");
free(recv_cb->data);
}
}
/* Parse received ESPNOW data. */
int example_espnow_data_parse(uint8_t *data, uint16_t data_len, uint8_t *state, uint16_t *seq, int *magic)
{
example_espnow_data_t *buf = (example_espnow_data_t *)data;
uint16_t crc, crc_cal = 0;
if (data_len < sizeof(example_espnow_data_t)) {
ESP_LOGE(TAG, "Receive ESPNOW data too short, len:%d", data_len);
return -1;
}
*state = buf->state;
*seq = buf->seq_num;
*magic = buf->magic;
crc = buf->crc;
buf->crc = 0;
crc_cal = crc16_le(UINT16_MAX, (uint8_t const *)buf, data_len);
if (crc_cal == crc) {
return buf->type;
}
return -1;
}
/* Prepare ESPNOW data to be sent. */
void example_espnow_data_prepare(example_espnow_send_param_t *send_param)
{
example_espnow_data_t *buf = (example_espnow_data_t *)send_param->buffer;
int i = 0;
assert(send_param->len >= sizeof(example_espnow_data_t));
buf->type = IS_BROADCAST_ADDR(send_param->dest_mac) ? EXAMPLE_ESPNOW_DATA_BROADCAST : EXAMPLE_ESPNOW_DATA_UNICAST;
buf->state = send_param->state;
buf->seq_num = s_example_espnow_seq[buf->type]++;
buf->crc = 0;
buf->magic = send_param->magic;
for (i = 0; i < send_param->len - sizeof(example_espnow_data_t); i++) {
buf->payload[i] = (uint8_t)esp_random();
}
buf->crc = crc16_le(UINT16_MAX, (uint8_t const *)buf, send_param->len);
}
static void example_espnow_task(void *pvParameter)
{
example_espnow_event_t evt;
uint8_t recv_state = 0;
uint16_t recv_seq = 0;
int recv_magic = 0;
bool is_broadcast = false;
int ret;
vTaskDelay(5000 / portTICK_RATE_MS);
ESP_LOGI(TAG, "Start sending broadcast data");
/* Start sending broadcast ESPNOW data. */
example_espnow_send_param_t *send_param = (example_espnow_send_param_t *)pvParameter;
if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
ESP_LOGE(TAG, "Send error");
example_espnow_deinit(send_param);
vTaskDelete(NULL);
}
while (xQueueReceive(example_espnow_queue, &evt, portMAX_DELAY) == pdTRUE) {
switch (evt.id) {
case EXAMPLE_ESPNOW_SEND_CB:
{
example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb;
is_broadcast = IS_BROADCAST_ADDR(send_cb->mac_addr);
ESP_LOGD(TAG, "Send data to "MACSTR", status1: %d", MAC2STR(send_cb->mac_addr), send_cb->status);
if (is_broadcast && (send_param->broadcast == false)) {
break;
}
if (!is_broadcast) {
send_param->count--;
if (send_param->count == 0) {
ESP_LOGI(TAG, "Send done");
example_espnow_deinit(send_param);
vTaskDelete(NULL);
}
}
/* Delay a while before sending the next data. */
if (send_param->delay > 0) {
vTaskDelay(send_param->delay/portTICK_RATE_MS);
}
ESP_LOGI(TAG, "send data to "MACSTR"", MAC2STR(send_cb->mac_addr));
memcpy(send_param->dest_mac, send_cb->mac_addr, ESP_NOW_ETH_ALEN);
example_espnow_data_prepare(send_param);
/* Send the next data after the previous data is sent. */
if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
ESP_LOGE(TAG, "Send error");
example_espnow_deinit(send_param);
vTaskDelete(NULL);
}
break;
}
case EXAMPLE_ESPNOW_RECV_CB:
{
example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb;
ret = example_espnow_data_parse(recv_cb->data, recv_cb->data_len, &recv_state, &recv_seq, &recv_magic);
free(recv_cb->data);
if (ret == EXAMPLE_ESPNOW_DATA_BROADCAST) {
ESP_LOGI(TAG, "Receive %dth broadcast data from: "MACSTR", len: %d", recv_seq, MAC2STR(recv_cb->mac_addr), recv_cb->data_len);
/* If MAC address does not exist in peer list, add it to peer list. */
if (esp_now_is_peer_exist(recv_cb->mac_addr) == false) {
esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t));
if (peer == NULL) {
ESP_LOGE(TAG, "Malloc peer information fail");
example_espnow_deinit(send_param);
vTaskDelete(NULL);
}
memset(peer, 0, sizeof(esp_now_peer_info_t));
peer->channel = CONFIG_ESPNOW_CHANNEL;
peer->ifidx = ESPNOW_WIFI_IF;
peer->encrypt = true;
memcpy(peer->lmk, CONFIG_ESPNOW_LMK, ESP_NOW_KEY_LEN);
memcpy(peer->peer_addr, recv_cb->mac_addr, ESP_NOW_ETH_ALEN);
ESP_ERROR_CHECK( esp_now_add_peer(peer) );
free(peer);
}
/* Indicates that the device has received broadcast ESPNOW data. */
if (send_param->state == 0) {
send_param->state = 1;
}
/* If receive broadcast ESPNOW data which indicates that the other device has received
* broadcast ESPNOW data and the local magic number is bigger than that in the received
* broadcast ESPNOW data, stop sending broadcast ESPNOW data and start sending unicast
* ESPNOW data.
*/
if (recv_state == 1) {
/* The device which has the bigger magic number sends ESPNOW data, the other one
* receives ESPNOW data.
*/
if (send_param->unicast == false && send_param->magic >= recv_magic) {
ESP_LOGI(TAG, "Start sending unicast data");
ESP_LOGI(TAG, "send data to "MACSTR"", MAC2STR(recv_cb->mac_addr));
/* Start sending unicast ESPNOW data. */
memcpy(send_param->dest_mac, recv_cb->mac_addr, ESP_NOW_ETH_ALEN);
example_espnow_data_prepare(send_param);
if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
ESP_LOGE(TAG, "Send error");
example_espnow_deinit(send_param);
vTaskDelete(NULL);
}
else {
send_param->broadcast = false;
send_param->unicast = true;
}
}
}
}
else if (ret == EXAMPLE_ESPNOW_DATA_UNICAST) {
ESP_LOGI(TAG, "Receive %dth unicast data from: "MACSTR", len: %d", recv_seq, MAC2STR(recv_cb->mac_addr), recv_cb->data_len);
/* If receive unicast ESPNOW data, also stop sending broadcast ESPNOW data. */
send_param->broadcast = false;
}
else {
ESP_LOGI(TAG, "Receive error data from: "MACSTR"", MAC2STR(recv_cb->mac_addr));
}
break;
}
default:
ESP_LOGE(TAG, "Callback type error: %d", evt.id);
break;
}
}
}
static esp_err_t example_espnow_init(void)
{
example_espnow_send_param_t *send_param;
example_espnow_queue = xQueueCreate(ESPNOW_QUEUE_SIZE, sizeof(example_espnow_event_t));
if (example_espnow_queue == NULL) {
ESP_LOGE(TAG, "Create mutex fail");
return ESP_FAIL;
}
/* Initialize ESPNOW and register sending and receiving callback function. */
ESP_ERROR_CHECK( esp_now_init() );
ESP_ERROR_CHECK( esp_now_register_send_cb(example_espnow_send_cb) );
ESP_ERROR_CHECK( esp_now_register_recv_cb(example_espnow_recv_cb) );
/* Set primary master key. */
ESP_ERROR_CHECK( esp_now_set_pmk((uint8_t *)CONFIG_ESPNOW_PMK) );
/* Add broadcast peer information to peer list. */
esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t));
if (peer == NULL) {
ESP_LOGE(TAG, "Malloc peer information fail");
vSemaphoreDelete(example_espnow_queue);
esp_now_deinit();
return ESP_FAIL;
}
memset(peer, 0, sizeof(esp_now_peer_info_t));
peer->channel = CONFIG_ESPNOW_CHANNEL;
peer->ifidx = ESPNOW_WIFI_IF;
peer->encrypt = false;
memcpy(peer->peer_addr, example_broadcast_mac, ESP_NOW_ETH_ALEN);
ESP_ERROR_CHECK( esp_now_add_peer(peer) );
free(peer);
/* Initialize sending parameters. */
send_param = malloc(sizeof(example_espnow_send_param_t));
memset(send_param, 0, sizeof(example_espnow_send_param_t));
if (send_param == NULL) {
ESP_LOGE(TAG, "Malloc send parameter fail");
vSemaphoreDelete(example_espnow_queue);
esp_now_deinit();
return ESP_FAIL;
}
send_param->unicast = false;
send_param->broadcast = true;
send_param->state = 0;
send_param->magic = esp_random();
send_param->count = CONFIG_ESPNOW_SEND_COUNT;
send_param->delay = CONFIG_ESPNOW_SEND_DELAY;
send_param->len = CONFIG_ESPNOW_SEND_LEN;
send_param->buffer = malloc(CONFIG_ESPNOW_SEND_LEN);
if (send_param->buffer == NULL) {
ESP_LOGE(TAG, "Malloc send buffer fail");
free(send_param);
vSemaphoreDelete(example_espnow_queue);
esp_now_deinit();
return ESP_FAIL;
}
memcpy(send_param->dest_mac, example_broadcast_mac, ESP_NOW_ETH_ALEN);
example_espnow_data_prepare(send_param);
xTaskCreate(example_espnow_task, "example_espnow_task", 2048, send_param, 4, NULL);
return ESP_OK;
}
static void example_espnow_deinit(example_espnow_send_param_t *send_param)
{
free(send_param->buffer);
free(send_param);
vSemaphoreDelete(example_espnow_queue);
esp_now_deinit();
}
void app_main()
{
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK( nvs_flash_erase() );
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
example_wifi_init();
example_espnow_init();
}