采用ESP-NOW实现ESP32/ESP8266之间的通信

注意:本文支持ESP32和ESP32、ESP8266和ESP8266、ESP32和ESP8266之间的通信

介绍 ESP-NOW

Espressif 网站称,ESP-NOW 是“由 Espressif 开发的一种协议,可让多个设备在不使用 Wi-Fi
的情况下相互通信。该协议类似于低功耗 2.4GHz 无线连接
(…)。在通信之前,设备之间需要进行配对。配对完成后,连接是安全的、点对点的,不需要握手。”

ESP-NOW - ESP32 徽标

这意味着设备相互配对后,连接是持久的。换句话说,如果您的其中一块板突然断电或重置,当它重新启动时,它将自动连接到其对等板以继续通信。

ESP-NOW 支持以下功能:

1. 加密和未加密的单播通信;
2. 混合加密和未加密的对等设备;
3. 最大可承载250字节的有效负载;
4. 可设置发送回调函数,通知应用层发送成功或失败。

ESP-NOW 技术还存在以下局限性:

1. 有限的加密对等点。Station模式下最多支持10个加密节点;SoftAP或SoftAP+Station模式下最多6个;
2. 支持多个未加密的对等体,但总数量应小于 20 个,包括加密的对等体;
有效负载限制为 250 字节。
3. 简而言之,ESP-NOW 是一种快速通信协议,可用于在 ESP32 板之间交换小消息(最多 250 字节)。

ESP-NOW 用途广泛,您可以在不同的设置中进行单向或双向通信。

ESP-NOW 单向通信

例如,在单向通信中,您可能会遇到如下场景:

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

这种配置非常容易实现,并且非常适合将数据从一个板发送到另一块板,例如传感器读数或控制 GPIO 的 ON 和 OFF 命令。
在这里插入图片描述

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

一块 ESP32 板向不同的 ESP32 板发送相同或不同的命令。这种配置非常适合构建遥控器之类的东西。您可以在房子周围放置多个 ESP32 板,这些板由一个主 ESP32 板控制。
在这里插入图片描述

一个 ESP32“从机”从多个“主机”接收数据

如果您想将多个传感器节点的数据收集到一块 ESP32 板上,那么这种配置是理想的选择。例如,可以将其配置为网络服务器来显示来自所有其他板的数据。
在这里插入图片描述

注意:在 ESP-NOW
文档中,没有“发送者/主站”和“接收者/从站”之类的东西。每个板都可以是发送者或接收者。然而,为了使事情清楚,我们将使用术语“发送者”和“接收者”或“主”和“从”。

ESP-NOW 双向通信

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

例如,您可以让两个板相互通信。
在这里插入图片描述
您可以向此配置添加更多板,并拥有看起来像网络的东西(所有 ESP32 板相互通信)。
在这里插入图片描述
总之,ESP-NOW 非常适合构建一个网络,您可以在该网络中让多个 ESP32 板相互交换数据。

ESP32:获取板MAC地址

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

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

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

#include "WiFi.h" 		  //esp32
//#include <ESP8266WiFi.h> //esp8266
 
void setup(){
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  Serial.println(WiFi.macAddress());
}
 
void loop(){

}

上传代码后,以 115200 波特率打开串行监视器,然后按 ESP32 RST/EN 按钮。MAC 地址应打印如下:
在这里插入图片描述

保存您的主板 MAC 地址,因为您需要它通过 ESP-NOW 将数据发送到正确的主板。

ESP-NOW 单向点对点通信

为了帮助您开始使用 ESP-NOW 无线通信,我们将构建一个简单的项目,展示如何从一个 ESP32 向另一个 ESP32 发送消息。一个 ESP32 将作为“发送器”,另一个 ESP32 将作为“接收器”。
在这里插入图片描述
我们将发送一个包含char、int、float和boolean类型变量的结构。然后,您可以修改结构以发送适合您的项目的变量类型(例如传感器读数或用于打开或关闭某些内容的布尔变量)。

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

以下是我们应该在发件人草图中包含的内容:

1. 初始化 ESP-NOW;
2. 发送数据时注册回调函数 –数据发送时发送消息时将执行该函数。这可以告诉我们消息是否已成功发送;
3. 添加对等设备(接收器)。为此,您需要知道接收方的MAC地址;
4. 向对端设备发送消息。

在接收器侧,草图应包括:

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

ESP-NOW 使用回调函数,这些函数在设备接收消息或发送消息时调用(您会看到消息是否成功传送或失败)。

ESP-NOW 有用的功能

以下是最基本的 ESP-NOW 功能的摘要:

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

ESP32 发送器草图 (ESP-NOW)

这是ESP32 发送器板的代码。将代码复制到您的 Arduino IDE,但先不要上传。您需要进行一些修改才能使其适合您。

#include <esp_now.h>		//esp32
#include <WiFi.h>			//esp32
//#include <espnow.h>		//esp8266
//#include <ESP8266WiFi.h> //esp8266

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  bool d;
} struct_message;

// Create a struct_message called myData
struct_message myData;

esp_now_peer_info_t peerInfo;

// callback when data is sent
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() {
  // Init Serial Monitor
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  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);
}

代码如何工作

首先,包括esp_now.h和WiFi.h图书馆。

#include <esp_now.h>		//esp32
#include <WiFi.h>			//esp32
//#include <espnow.h>		//esp8266
//#include <ESP8266WiFi.h> //esp8266

在下一行中,您应该插入 ESP32 接收器 MAC 地址。

uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0x07, 0x0D, 0x64};

在我们的例子中,接收者 MAC 地址是:30:AE:A4:07:0D:64,但您需要将该变量替换为您自己的 MAC 地址。

然后,创建一个包含我们要发送的数据类型的结构。我们称这个结构为结构消息它包含 4 种不同的变量类型。您可以更改此设置以发送其他变量类型。

typedef struct struct_message {
  char a[32];
  int b;
  float c;
  bool d;
} struct_message;

然后,创建一个新类型的变量结构消息它被称作我的数据这将存储变量的值。

struct_message myData;

创建类型变量esp_now_peer_info_t存储有关对等点的信息。

esp_now_peer_info_t peerInfo;

接下来,定义OnDataSent()功能。这是发送消息时将执行的回调函数。在这种情况下,该函数只是打印消息是否成功传递。

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");
}

在里面设置(),初始化串行监视器以进行调试:

Serial.begin(115200);

将设备设置为 Wi-Fi 站:

WiFi.mode(WIFI_STA);

初始化 ESP-NOW:

if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}

成功初始化 ESP-NOW 后,注册发送消息时将调用的回调函数。在这种情况下,我们注册OnDataSent()之前创建的函数。

esp_now_register_send_cb(OnDataSent);

之后,我们需要与另一个 ESP-NOW 设备配对来发送数据。这就是我们在接下来的几行中要做的事情:

//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;
}

在里面环形(),我们将每 2 秒通过 ESP-NOW 发送一条消息(您可以更改此延迟时间)。

首先,我们设置变量值如下:

strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = false;

请记住我的数据是一个结构体。在这里,我们分配要在结构内部发送的值。例如,第一行分配一个 char,第二行分配一个随机 Int 数、一个 Float 和一个布尔变量。

我们创建这种结构是为了向您展示如何发送最常见的变量类型。您可以更改结构以发送其他数据类型。

最后发送消息如下:

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");
}

这环形()每 2000 毫秒(2 秒)执行一次。

delay(2000);

ESP32 接收器草图 (ESP-NOW)

将以下代码上传到您的ESP32 接收器板。

#include <esp_now.h>		//esp32
#include <WiFi.h>			//esp32
//#include <espnow.h>		//esp8266
//#include <ESP8266WiFi.h> //esp8266

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
    char a[32];
    int b;
    float c;
    bool d;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// callback function that will be executed when data is received
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() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);
}
 
void loop() {

}

代码如何工作

与发送方类似,首先包含库:

#include <esp_now.h>
#include <WiFi.h>

创建一个结构来接收数据。该结构应该与发送方草图中定义的结构相同。

typedef struct struct_message {
  char a[32];
  int b;
  float c;
  bool d;
} struct_message;

创建一个结构消息变量称为我的数据

struct_message myData;

创建一个回调函数,当 ESP32 通过 ESP-NOW 接收数据时将调用该函数。该函数称为onDataRecv()并且应该接受如下几个参数:

void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {

我们复制一下内容传入数据数据变量放入我的数据多变的。

memcpy(&myData, incomingData, sizeof(myData));

现在我的数据结构体内部包含多个变量,其中包含 ESP32 发送器发送的值。访问变量A,例如,我们只需要调用myData.a

在本示例中,我们只是打印接收到的数据,但在实际应用中,您可以在显示屏上打印数据等。

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();

在里面设置(),初始化串行监视器。

Serial.begin(115200);

将设备设置为 Wi-Fi 站。

WiFi.mode(WIFI_STA);

初始化 ESP-NOW:

if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}

注册接收数据时将调用的回调函数。在这种情况下,我们注册OnDataRecv()之前创建的函数。

esp_now_register_recv_cb(OnDataRecv);

测试 ESP-NOW 通信

将发送器草图上传到发送器 ESP32 板,将接收器草图上传到接收器 ESP32 板。

现在,打开两个 Arduino IDE 窗口。一个用于接收方,另一个用于发送方。打开每个板的串行监视器。每块板应该有不同的 COM 端口。

这是您应该在发送方获得的信息。
在这里插入图片描述
这就是您应该在接收方得到的。请注意,Int 变量在收到的每个读数之间会发生变化(因为我们将其设置为发送方的随机数)。
在这里插入图片描述
我测试了两块板之间的通信范围,在空旷的场地中我们能够稳定通信达220米(约722英尺)。在此实验中,两个 ESP32 板载天线相互指向。

  • 26
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值