【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打印信息:

ESP-NOW-CONTROLLER

slave打印信息:

ESP-NOW-SLAVE

其他

加密發送消息暫時還沒調試好,發送消息對方會收不到。

本教程相關代碼

備註:CSDN版本上傳後不再更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章