【ESP8266】ESP8266使用ESP-NOW入门教程

前言

ESP8266有很多好玩的技术,比如sniffer、smartconfig。这一次就介绍的是ESP-NOW。

那么什么是ESP-NOW呢?根据官方文档介绍

ESP-NOW 是一种短数据传输、无连接的快速通信技术,适用于智能灯、遥控控制、传感器数据回传等场景。

下面会简单介绍ESP-NOW,并放出使用示例代码(基于ESP8266 SDK 2.0),想更深入研究请参考官方文档。

面向的读者

本文主要面向ESP8266开发者,阅读本文需要有一定的ESP8266开发基础,比如开发环境的搭建、SDK的开发等。

开发平台和工具

  • Windows 10 x64
  • ESP8266 IDE 2.0
  • NodeMCU(4MB Flash)
  • ESP8266_NONOS_SDK 2.0.0

ESP-NOW

特性

ESP-NOW 支持下面特性:

  • 单播包加密/不加密通信
  • 加密和非加密配对设备混合
  • 可携带最长250字节的用户数据(payload)
  • 支持设置发送回调函数

限制

  • 暂时不支持广播包
  • 加密配对有数量限制(具体参考文档)
  • 用户字节限制为250字节

ESP-NOW 的 Role

在配置 ESP-NOW 的时候需要给设备配置role,ESP-NOW有如下role:

  • IDLE。不设置角色,不允许发送数据。
  • CONTROLLER。控制方。
  • SLAVE。被控制方。
  • COMBO。控制方&被控制方。

其中,ESP8266的Wi-Fi模式有station和softAP。当ESP-NOW作为CONTROLLER时,数据优先从station接口发出。当作为SLAVE时,数据优先从softAP接口发出

示例代码

博主自己编写的示例代码user_esp_now.c如下:

/*
 * user_esp_now.c
 *
 *  Created on: 2017年7月8日
 *      Author: Administrator
 */

#include "ets_sys.h"
#include "osapi.h"
#include "user_interface.h"
#include "espnow.h"

#define CONTROLLER
//#define SLAVE

// 是否使用加密
#define SECURITY    0

#if SECURITY
u8 g_key[16]= {0x12, 0x34, 0x56, 0x78,
            0x12, 0x34, 0x56, 0x78,
            0x12, 0x34, 0x56, 0x78,
            0x12, 0x34, 0x56, 0x78};
#endif

// 使用虚拟的MAC地址,station设置MAC似乎不行
u8 controller_mac[6] = {0xA0, 0x20, 0xA6, 0xAA, 0xAA, 0xAA};
u8 slave_mac[6] = {0xA2, 0x20, 0xA6, 0x55, 0x55, 0x55};


#define ESP_NOW_SEND_INTERVAL   3000
static os_timer_t g_esp_now_timer;

/*
 * 函数:user_esp_now_recv_cb
 * 说明:ESP-NOW接收回调函数
 */
static void ICACHE_FLASH_ATTR
user_esp_now_recv_cb(u8 *macaddr, u8 *data, u8 len)
{
    int i;
    static u16 ack_count=0;
    u8 ack_buf[16];
    u8 recv_buf[17];
    os_printf("now from[");

    for(i = 0; i < 6; i++){
        os_printf("%02X ", macaddr[i]);
    }
    os_printf(" len: %d]:", len);

    os_bzero(recv_buf, 17);
    os_memcpy(recv_buf, data, len<17?len:16);
    os_printf(recv_buf);
    os_printf("\r\n");

    if (os_strncmp(data, "ACK", 3) == 0){
        return;
    }else{

    }

    os_sprintf(ack_buf, "ACK[%08x]", ack_count++);
    esp_now_send(macaddr, ack_buf, os_strlen(ack_buf));
}

/*
 * 函数:user_esp_now_send_cb
 * 说明:ESP-NOW发送回调函数
 */
void ICACHE_FLASH_ATTR
user_esp_now_send_cb(u8 *mac_addr, u8 status)
{
    int i;
    for(i = 0; i < 6; i++){
        os_printf("%02X ", mac_addr[i]);
    }

    if(1==status){
        os_printf("SEND FAIL!\r\n");
    }else if(0==status){
        os_printf("SEND SUCCESSFUL!\r\n");
    }
}

/*
 * 函数:user_esp_now_send
 * 说明:ESP-NOW数据发送
 */
void ICACHE_FLASH_ATTR
user_esp_now_send(u8 *mac_addr, u8 *data, u8 len)
{
    /* the demo will send to two devices which added by esp_now_add_peer() */
    //u8 result = esp_now_send(NULL, data, len);

    /* send to the specified mac_addr */
    u8 result = esp_now_send(mac_addr, data, len);
}

/*
 * 函数:user_esp_now_timer_cb
 * 说明:定时器回调函数
 */
static void ICACHE_FLASH_ATTR
user_esp_now_timer_cb(void* arg)
{
    u8 *mac = arg;
    u8* send_data = "Hello World!";
    int result = esp_now_is_peer_exist(mac);
    //os_printf("peer_exist = %d\r\n", result);
    user_esp_now_send(mac, send_data, os_strlen(send_data));
}

/*
 * 函数:user_esp_now_timer_init
 * 说明:定时发送 ESP_NOW 数据包
 */
void ICACHE_FLASH_ATTR
user_esp_now_timer_init(u8 *mac)
{
    os_timer_disarm(&g_esp_now_timer);
    os_timer_setfn(&g_esp_now_timer, (os_timer_func_t *)user_esp_now_timer_cb, mac);
    os_timer_arm(&g_esp_now_timer, ESP_NOW_SEND_INTERVAL, 1);
}

/*
 * 函数:user_esp_now_init
 * 说明:ESP-NOW初始化
 */
void ICACHE_FLASH_ATTR
user_esp_now_init(void)
{
    int result;

    if (esp_now_init()==0) {
        os_printf("esp_now init ok\n");

        // 注册 ESP-NOW 收包的回调函数
        esp_now_register_recv_cb(user_esp_now_recv_cb);
        // 注册发包回调函数
        esp_now_register_send_cb(user_esp_now_send_cb);

#if SECURITY
        // 设置主密钥
        //esp_now_set_kok(g_key, 16);
#endif

        /* role
         * ESP_NOW_ROLE_IDLE - 空闲
         * ESP_NOW_ROLE_CONTROLLER - 主机
         * ESP_NOW_ROLE_SLAVE - 从机
         * ESP_NOW_ROLE_COMBO - 主/从机
         */
#if defined(SLAVE)
        os_printf("==================\r\n");
        os_printf("SLAVE\r\n");
        os_printf("==================\r\n");
        esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
        //esp_now_add_peer(controller_mac, ESP_NOW_ROLE_CONTROLLER, 1, NULL, 16);
#if SECURITY
        esp_now_set_peer_key(controller_mac, g_key, 16);
#endif


#elif defined(CONTROLLER)
        os_printf("==================\r\n");
        os_printf("CONTROLLER\r\n");
        os_printf("==================\r\n");
        esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
        esp_now_add_peer(slave_mac, ESP_NOW_ROLE_SLAVE, 1, NULL, 16);

#if SECURITY
        esp_now_set_peer_key(slave_mac, g_key, 16);
#endif

        // 不需要连接Wi-Fi
        wifi_station_disconnect();
        //wifi_station_connect();
        user_esp_now_timer_init(slave_mac);

#endif

    } else {
        os_printf("esp_now init failed\n");
    }
}

/************************************************************************/

/*
 * 函数:user_esp_now_set_mac_current
 * 说明:设置虚拟MAC地址,必须在 user_init() 函数里调用
 */
void ICACHE_FLASH_ATTR
user_esp_now_set_mac_current(void)
{
#if defined(SLAVE)
    wifi_set_macaddr(SOFTAP_IF, slave_mac);
    wifi_set_opmode_current(SOFTAP_MODE);
#elif defined(CONTROLLER)
    // 设置station MAC地址
    wifi_set_macaddr(STATION_IF, controller_mac);
    // 设置为station模式
    wifi_set_opmode_current(STATION_MODE);
#endif
}

以上代码的功能分为两部分:CONTROLLERSLAVE。CONTROLLER是控制器代码,类似于Client/Server的Client,需要设置Wi-FI为station模式,CONTROLLER每个3秒中向SLAVE发送Hello World!字符串。SLAVE为被控制方,类似于server,需要打开softAP模式,这里为了方便使用了虚拟的MAC地址(0xA220A6555555)。另外,softAP和station使用的MAC不同,一般来说station硬件MAC为0xA0开头,softAP的为0xA2开头,如果是使用硬件MAC这一点要注意。

其他说明代码里面的注释都写得很清楚。

其中user_esp_now_set_mac_current()函数必须在user_init()函数里调用。

user_esp_now_init()为 ESP-NOW 初始化函数,可以放在system_init_done_cb()的回调函数里调用。

user_main.c主要代码

#include "ets_sys.h"
#include "osapi.h"
#include "user_interface.h"
#include "driver/uart.h"
#include "espnow.h"
#include "user_esp_now.h"

// 省略……

void ICACHE_FLASH_ATTR
init_done_cb_init(void)
{

    user_esp_now_init();
}

void ICACHE_FLASH_ATTR
user_init(void)
{
    //uart_init(BIT_RATE_115200, BIT_RATE_115200);
    user_esp_now_set_mac_current();
    os_printf("SDK version:%s\n", system_get_sdk_version());

    // 系统初始化后回调
    system_init_done_cb(init_done_cb_init);
}

使用步骤

准备两个ESP8266,分别烧录CONTROLLERSLAVE的代码。为了方便,SLAVE使用了虚拟了MAC地址(softAP),所以不用在CONTROLLER里修改为硬件MAC地址。

重启上电,波特率76800,可以看到下面的打印信息。CONTROLLER的station并不需要连接SLAVE的softAP。

controller打印信息:

【ESP8266】ESP8266使用ESP-NOW入门教程_第1张图片

slave打印信息:

【ESP8266】ESP8266使用ESP-NOW入门教程_第2张图片

其他

加密发送消息暂时还没调试好,发送消息对方会收不到。

本教程相关代码

  • Github:https://github.com/AngelLiang/ESP8266-Demos (esp-now_app文件夹)
  • CSDN:http://download.csdn.net/detail/u012163234/9893267

备注:CSDN版本上传后不再更新。

你可能感兴趣的:(ESP8266)