• ESP32 入门笔记07: ESP-NOW (ESP32 for Arduino)



    【B站乐鑫】 ESP-NOW 无线通信方案


    简介

    ESP-NOW 是一种快速、无连接的通信技术,具有短数据包传输的特点。

    ESP-NOW 是乐鑫开发的一种“协议”,它使多个设备无需使用 Wi-Fi 即可相互通信。该协议类似于低功耗 2.4GHz 无线连接 。在通信之前需要设备之间的配对。配对完成后,连接是安全的点对点连接,不需要握手

    这意味着在设备彼此配对后,连接是持久的。换句话说,如果你的一块板突然断电或复位,当它重新启动时,它会自动连接到它的对端以继续通信。

    ESP-NOW 是智能灯、遥控设备、传感器和其他应用的理想选择。

    ESP-NOW 支持以下功能:

    • 加密和未加密的单播通信;
    • 混合加密和未加密的对等设备;
    • 最多可携带250字节的有效载荷
    • 发送回调函数,可设置通知应用层发送成功或失败。

    ESP-NOW 技术也有以下限制:

    • 有限的加密对等体。Station 模式最多支持10个加密节点;SoftAP 或SoftAP+Station 模式下最多 6 个;
    • 支持多个未加密的 peer,但包括加密的 peer 在内,总数应小于20;
    • 有效负载限制为 250 字节。

    简单来说,ESP-NOW 是一种快速通信协议,可用于在 ESP32 开发板之间交换小消息(最多 250 字节)。


    ESP-NOW 通信方式

    1. ESP-NOW 单向通信

    一块 ESP32 板向另一块 ESP32 板发送数据

    这种配置非常容易实现,并且非常适合将数据从一块板发送到另一块板,例如传感器读数或打开和关闭命令以控制 GPIO。

    在这里插入图片描述

    一个ESP32“主设备” 向多个 ESP32“从设备”发送数据

    一块 ESP32 开发板向不同的 ESP32 开发板发送相同或不同的命令。此配置非常适合构建类似遥控器的东西。可以在房子周围安装多个 ESP32 板,这些板由一个主 ESP32 板控制。

    在这里插入图片描述

    注意:在 ESP-NOW 文档中没有sender/masterreceiver/slave这样的东西。每个板都可以是发送者或接收者。但是,为了清楚起见,我们将使用术语“发送方”和“接收方”或“主控方”和“从属方”。


    2. ESP-NOW 双向通信

    使用 ESP-NOW,每个板可以同时作为发送器和接收器。因此,可以在板之间建立双向通信。

    例如,可以让两个板相互通信。

    在这里插入图片描述

    可以在此配置中添加更多电路板,并拥有一个看起来像网络的东西(所有 ESP32 电路板都相互通信)。

    在这里插入图片描述


    ESP32获取开发板 MAC 地址

    要通过 ESP-NOW 进行通信,您需要知道ESP32 接收器的 MAC 地址。这就是您知道要将数据发送到哪个设备的方式。

    什么是 MAC 地址?

    MAC地址代表媒体访问控制地址,它是识别网络上每个设备的硬件唯一标识符。

    MAC 地址由六组 两位 十六进制数字组成,以冒号分隔,例如:30:AE:A4:07:0D:64

    MAC 地址由制造商分配,但您也可以为开发板提供自定义 MAC 地址。但是,每次板子重置时,它都会返回到其原始 MAC 地址。因此,您需要在每个草图中包含设置自定义 MAC 地址的代码。

    每个 ESP32 都有一个唯一的 MAC 地址,这就是我们识别每个开发板以使用 ESP-NOW 向其发送数据的方式(了解如何获取和更改 ESP32 MAC 地址)。

    获取开发板的 MAC 地址

    要获取开发板的 MAC 地址,请上传以下代码。

    #ifdef ESP32
      #include 
    #else
      #include 
    #endif
    
    void setup(){
      Serial.begin(115200);
      Serial.println();
      Serial.print("ESP Board MAC Address:  ");
      Serial.println(WiFi.macAddress());
    }
     
    void loop(){
    Serial.println(WiFi.macAddress());
    delay(1000);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    重置自定义 MAC 地址

    在某些应用程序中,为您的开发板提供自定义 MAC 地址可能很有用。但是,如前所述,这不会覆盖制造商设置的 MAC 地址。因此,每次您重置电路板或上传新代码时,它都会恢复为默认 MAC 地址。

    #include 
    #include 
    
    // 设置你自定义的 MAC 地址 94:E6:86:09:1F:70  | 40:22:D8:5C:24:B0
    uint8_t newMACAddress[] = {0x32, 0xAE, 0xA4, 0x07, 0x0D, 0x66};
    
    void setup(){
      Serial.begin(115200);
      Serial.println();
      
      WiFi.mode(WIFI_STA);
      
      Serial.print("[OLD] ESP32 Board MAC Address:  ");
      Serial.println(WiFi.macAddress());
      
      // ESP32 Board add-on before version < 1.0.5
      //esp_wifi_set_mac(ESP_IF_WIFI_STA, &newMACAddress[0]);
      
      // ESP32 Board add-on after version > 1.0.5
      esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);
      
      Serial.print("[NEW] ESP32 Board MAC Address:  ");
      Serial.println(WiFi.macAddress());
    }
     
    void loop(){
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    上传代码后,以 115200 的波特率打开串口监视器。重新启动 ESP32,你应该得到它的旧 MAC 地址和新 MAC 地址。

    在这里插入图片描述

    实验:ESP-NOW 单向/点对点通信

    构建一个简单的项目,展示如何将消息从一个 ESP32 发送到另一个。

    • sender :一个 ESP32 将成为“发送端”;

    • receiver: 另一个 ESP32 将成为“接收端”。

    在这里插入图片描述

    发送一个结构,其中包含类型为char、int、float和boolean的变量。然后,您可以修改结构以发送适合您的项目的任何变量类型(例如传感器读数,或用于打开或关闭某些东西的布尔变量)。

    为了更好地理解,我们将 ESP32 #1 称为“发送方”,将 ESP32 #2 称为“接收方”。

    发送端 程序中包含的内容:

    • 初始化 ESP-NOW;
    • 发送数据时注册一个回调函数——数据发送发送消息时将执行函数。这可以告诉我们消息是否成功传递;
    • 添加对等设备(接收器)。为此,您需要知道接收方的 MAC 地址;
    • 向对等设备发送消息。

    接收端 程序中包括:

    • 初始化 ESP-NOW;
    • 注册接收回调函数(数据接收). 这是一个将在收到消息时执行的函数。
    • 在该回调函数中,将消息保存到变量中以使用该信息执行任何任务。

    ESP-NOW 与设备接收消息或发送消息时调用的回调函数一起使用(您会收到消息是成功传递还是失败)。

    ESP-NOW 函数功能

    函数名称描述
    esp_now_init()初始化 ESP-NOW。在初始化 ESP-NOW 之前,您必须先初始化 Wi-Fi。
    esp_now_add_peer()调用此函数以配对设备并将peer MAC 地址作为参数传递。
    esp_now_send()使用 ESP-NOW 发送数据
    esp_now_register_send_cb()注册发送数据时触发的回调函数。发送消息时,将调用一个函数——此函数返回传递是否成功。
    esp_now_register_rcv_cb()注册接收数据时触发的回调函数。当通过 ESP-NOW 接收到数据时,将调用一个函数。

    有关这些功能的更多信息,请阅读ESP-IDF 编程指南中的 ESP-NOW 文档

    ESP32 发送器

    // Sender发送端
    #include 
    #include 
    
    //  ESP32 接收器 MAC 地址
    uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //将该变量替换为您自己的 MAC 地址。
    
    // 创建一个包含我们要发送的数据类型的结构体
    typedef struct struct_message {
      char a[32];
      int b;
      float c;
      bool d;
    } struct_message;
    
    // 创建struct_message结构体类型的变量存储数据
    struct_message myData;
    // 创建esp_now_peer_info_t类型变量存储有关peer方的信息。
    esp_now_peer_info_t peerInfo;
    
    // 数据发送回调函数-此函数仅打印消息是否成功发送
    void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
      Serial.print("\r\nLast Packet Send Status:\t");
      Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
    }
     
    void setup() {
      Serial.begin(115200);
      WiFi.mode(WIFI_STA); // 设置为WiFi站点
    
      // 初始化 ESP-NOW
      if (esp_now_init() != ESP_OK) {
        Serial.println("Error initializing ESP-NOW");
        return;
      }
    
      // 注册发送消息时调用的回调函数 OnDataSent
      esp_now_register_send_cb(OnDataSent);
      
      // Register peer
      memcpy(peerInfo.peer_addr, broadcastAddress, 6);
      peerInfo.channel = 0;  
      peerInfo.encrypt = false;
      
      // Add peer        
      if (esp_now_add_peer(&peerInfo) != ESP_OK){
        Serial.println("Failed to add peer");
        return;
      }
    }
     
    void loop() {
      // Set values to send
      strcpy(myData.a, "THIS IS A CHAR");
      myData.b = random(1,20);
      myData.c = 1.2;
      myData.d = false;
      
      // Send message via ESP-NOW
      esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
       
      if (result == ESP_OK) {
        Serial.println("Sent with success");
      }
      else {
        Serial.println("Error sending the data");
      }
      delay(2000);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    ESP32 接收器

    // Receiver接收端
    
    #include 
    #include 
    
    // 创建一个结构体接收数据
    typedef struct struct_message {
        char a[32];
        int b;
        float c;
        bool d;
    } struct_message;
    
    // 
    struct_message myData;
    
    // 创建一个回调函数,当 ESP32 通过 ESP-NOW 接收到数据时将被调用
    void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
      memcpy(&myData, incomingData, sizeof(myData));
      Serial.print("Bytes received: ");
      Serial.println(len);
      Serial.print("Char: ");
      Serial.println(myData.a);
      Serial.print("Int: ");
      Serial.println(myData.b);
      Serial.print("Float: ");
      Serial.println(myData.c);
      Serial.print("Bool: ");
      Serial.println(myData.d);
      Serial.println();
    }
     
    void setup() {
      Serial.begin(115200);
      WiFi.mode(WIFI_STA);// 将设备设置为 Wi-Fi Station
    
      // 初始化 ESP-NOW
      if (esp_now_init() != ESP_OK) {
        Serial.println("Error initializing ESP-NOW");
        return;
      }
      
      // 注册在接收到数据时调用的回调函数 OnDataRecv
      esp_now_register_recv_cb(OnDataRecv);
    }
     
    void loop() {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    还有更多与 ESP-NOW 相关的功能可以使用。

    例如:管理peers、删除peers、扫描从属设备等……在 Arduino IDE 中转到文件>示例> ESP32 > ESPNow 并选择其中一个示例查看完整示例。


    参考资料

  • 相关阅读:
    P3916 图的遍历
    富格林:揭露黑幕平台保障安全
    unity 2019 内置渲染管线 光照与Lighting面板 参数详解
    第十六章《正则表达式》第5节:正则表达式实战应用
    elementui表单的验证问题
    UML统一建模语言(UML类图)
    Spring Boot+Netty+Websocket实现后台向前端推送信息
    Ubuntu18.04版本下配置ORB-SLAM3和数据集测试方法
    写了个简单爬虫,分析 Boss 直聘自动驾驶岗位
    进阶高级,接口+接口自动化测试疑难解答,一篇带你策底打通...
  • 原文地址:https://blog.csdn.net/Naiva/article/details/127980364