在本文中,我们将展示如何使用 ESP-NOW 在使用 Arduino IDE 编程的 ESP8266 NodeMCU 板之间交换数据。ESP-NOW 是乐鑫开发的无连接通信协议,具有短包传输的特点,可与 ESP8266 和ESP32 开发板配合使用。
注意:我们有一个类似的 ESP32 指南:
介绍 ESP-NOW
Espressif 网站称,ESP-NOW 是“ Espressif 开发的一种协议”,可让多个设备在不使用 Wi-Fi 的情况下相互通信。该协议类似于低功耗 2.4GHz 无线连接 (…)。在通信之前,设备之间需要进行配对。配对完成后,连接是安全的、点对点的,不需要握手。”
这意味着设备相互配对后,连接是持久的。换句话说,如果您的其中一块板突然断电或重置,当它重新启动时,它将自动连接到其对等板以继续通信。
ESP-NOW 支持以下功能:
- 加密和未加密的单播通信;
- 混合加密和未加密的对等设备;
- 最大可承载250字节的有效载荷;
- 可设置发送回调函数,通知应用层
发送成功或失败。
ESP-NOW 技术还存在以下局限性:
- 有限的加密对等点。Station模式下最多支持10个加密节点;SoftAP或SoftAP+Station模式下最多6个;
- 支持多个未加密的对等体,但总数量应小于 20 个,包括加密的对等体;
- 有效负载限制为 250 字节。
简而言之,ESP-NOW 是一种快速通信协议,可用于在 ESP8266 板之间交换小消息(最多 250 字节)。
ESP-NOW 用途广泛,您可以在不同的设置中进行单向或双向通信。
ESP-NOW 单向通信
例如,在单向通信中,您可能会遇到如下场景:
- 一块 ESP8266 板向另一块 ESP8266 板发送数据
这种配置非常容易实现,并且非常适合将数据从一个板发送到另一块板,例如传感器读数或控制 GPIO 的 ON 和 OFF 命令。
- 一个“主”ESP8266 向多个 ESP8266“从”发送数据
一块 ESP8266 板向不同的 ESP8266 板发送相同或不同的命令。这种配置非常适合构建遥控器之类的东西。您可以在房子周围放置多块 ESP8266 板,这些板由一块主 ESP8266 板控制。
- 一个 ESP8266“从机”从多个“主机”接收数据
如果您想要将多个传感器节点的数据收集到一块 ESP8266 板中,则此配置非常理想。例如,可以将其配置为网络服务器来显示来自所有其它板的数据。
注意:在 ESP-NOW 文档中,没有“发送者/主站”和“接收者/从站”之类的东西。每个板都可以是发送者或接收者。然而,为了使事情清楚,我们将使用术语“发送者”和“接收者”或“主”和“从”。
ESP-NOW 双向通信
使用 ESP-NOW,每个板可以同时作为发送器和接收器。因此,您可以在板之间建立双向通信。
例如,您可以让两个板相互通信。
您可以向此配置添加更多板,并拥有看起来像网络的东西(所有 ESP8266 板相互通信)。
总之,ESP-NOW 非常适合构建一个网络,您可以在该网络中让多个 ESP8266 板相互交换数据。
ESP8266:获取板MAC地址
要通过 ESP-NOW 进行通信,您需要知道 ESP8266 接收器的 MAC 地址。这样您就知道要将信息发送到哪个设备。
每个 ESP8266 都有一个唯一的 MAC 地址,这就是我们如何识别每个板以使用 ESP-NOW 向其发送数据。
要获取主板的 MAC 地址,请上传以下代码。
// Complete Instructions to Get and Change ESP MAC Address: https://www.makersns.com/get-change-esp32-esp8266-mac-address-arduino/
#include <ESP8266WiFi.h>
void setup(){
Serial.begin(115200);
Serial.println();
Serial.print("ESP8266 Board MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop(){
}
上传代码后,以 115200 波特率打开串行监视器,然后按 ESP8266 RESET 按钮。MAC 地址应打印如下:
保存您的主板 MAC 地址,因为您需要它通过 ESP-NOW 将数据发送到正确的主板。
ESP-NOW 与 ESP8266 的单向点对点通信
为了帮助您开始使用 ESP-NOW 无线通信,我们将构建一个简单的项目,展示如何从一个 ESP8266 向另一个 ESP8266 发送消息。一个 ESP8266 将充当“发送器”,另一个 ESP8266 将充当“接收器”。
我们将发送一个包含char、int、float、String和boolean类型变量的结构。然后,您可以修改结构以发送适合您的项目的变量类型(例如传感器读数或用于打开或关闭某些内容的布尔变量)。
为了更好地理解,我们将 ESP8266 #1 称为“发送器”,将 ESP8266 #2 称为“接收器”。
以下是我们应该在发件人程序中包含的内容:
- 初始化 ESP-NOW;
- 发送数据时注册回调函数 –数据发送时发送消息时将执行该函数。这可以告诉我们消息是否已成功发送;
- 添加对等设备(接收器)。为此,您需要知道接收方的MAC地址;
- 向对端设备发送消息。
在接收器侧,程序应包括:
- 初始化 ESP-NOW;
- 注册接收回调函数(数据接收时)。这是收到消息时将执行的函数。
- 在该回调函数内,将消息保存到变量中,以使用该信息执行任何任务。
ESP-NOW 使用回调函数,这些函数在设备接收消息或发送消息时调用(您会看到消息是否成功传送或失败)。
ESP-NOW 有用的功能
以下是最重要的 ESP-NOW 功能的摘要:
函数名称和描述 |
esp_now_init()初始化 ESP-NOW。您必须在初始化 ESP-NOW 之前初始化 Wi-Fi。如果成功则返回 0。 |
esp_now_set_self_role(role) 角色可以是:ESP_NOW_ROLE_IDLE = 0,ESP_NOW_ROLE_CONTROLLER,ESP_NOW_ROLE_SLAVE,ESP_NOW_ROLE_COMBO,ESP_NOW_ROLE_MAX。 |
esp_now_add_peer(uint8 mac_addr, uint8 role, uint8 channel, uint8 key, uint8 key_len)调用此函数以配对设备。 |
esp_now_send(uint8 mac_address,uint8 data,int len)使用ESP-NOW发送数据。 |
esp_now_register_send_cb()注册发送数据时触发的回调函数。发送消息时,会调用一个函数 – 该函数返回发送是否成功。 |
esp_now_register_rcv_cb()注册一个接收数据时触发的回调函数。当通过 ESP-NOW 接收数据时,将调用一个函数。 |
有关这些功能的更多信息:
ESP8266 NodeMCU 发送器程序 (ESP-NOW)
以下是 ESP8266 NodeMCU 发送器板的代码。将代码复制到您的 Arduino IDE,但先不要上传。您需要进行一些修改才能使其适合您。
#include <ESP8266WiFi.h>
#include <espnow.h>
// REPLACE WITH 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;
String d;
bool e;
} struct_message;
// Create a struct_message called myData
struct_message myData;
unsigned long lastTime = 0;
unsigned long timerDelay = 2000; // send readings timer
// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
Serial.print("Last Packet Send Status: ");
if (sendStatus == 0){
Serial.println("Delivery success");
}
else{
Serial.println("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() != 0) {
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_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
// Register peer
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
void loop() {
if ((millis() - lastTime) > timerDelay) {
// Set values to send
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = "Hello";
myData.e = false;
// Send message via ESP-NOW
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
lastTime = millis();
}
}
代码如何工作
首先,包括ESP8266WiFi.h和espnow.h库。
#include <ESP8266WiFi.h>
#include <espnow.h>
在下一行中,您应该插入 ESP8266 接收器 MAC 地址。
uint8_t broadcastAddress[] = {0x5C, 0xCF, 0x7F, 0x99, 0x9A, 0xEA};
在我们的例子中,接收者 MAC 地址是:5C:CF:7F:99:9A:EA,但您需要将该变量替换为您自己的 MAC 地址。
然后,创建一个包含我们要发送的数据类型的结构。我们称这个结构为结构消息它包含 5 种不同的变量类型。您可以更改此设置以发送您想要的任何变量类型。
typedef struct struct_message {
char a[32];
int b;
float c;
String d;
bool e;
} struct_message;
创建一个新类型的变量结构消息它被称作我的数据这将存储变量值。
struct_message myData;
接下来,定义OnDataSent()功能。这是发送消息时将执行的回调函数。在这种情况下,如果消息发送成功或失败, 则仅打印此消息。
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
Serial.print("Last Packet Send Status: ");
if (sendStatus == 0){
Serial.println("Delivery success");
}
else{
Serial.println("Delivery fail");
}
}
在函数 setup() 里面,初始化串行监视器以进行调试:
Serial.begin(115200);
将设备设置为 Wi-Fi 站:
WiFi.mode(WIFI_STA);
初始化 ESP-NOW:
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
设置ESP8266角色:
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
它接受以下角色:
ESP_NOW_ROLE_CONTROLLER,ESP_NOW_ROLE_SLAVE,ESP_NOW_ROLE_COMBO,ESP_NOW_ROLE_MAX。
成功初始化 ESP-NOW 后,注册发送消息时将调用的回调函数。在这种情况下,我们注册OnDataSent()之前创建的函数。
esp_now_register_send_cb(OnDataSent);
然后,与另一个 ESP-NOW 设备配对以发送数据:
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
这esp_now_add_peer按此顺序接受以下参数:mac 地址、角色、wi-fi 通道、密钥和密钥长度。
在loop()函数中,我们将每2秒通过ESP-NOW发送一条消息(您可以在timerDelay变量上更改此延迟时间)。
首先,我们设置变量值如下:
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = "Hello";
myData.e = false;
请记住我的数据是一个结构体。在这里,我们分配要在结构内部发送的值。例如,第一行分配一个 char,第二行分配一个随机 Int 整数、一个 Float、一个 String 和一个 Boolean 变量。
我们创建这种结构是为了向您展示如何发送最常见的变量类型。您可以更改结构以发送任何其它类型的数据。
最后发送消息如下:
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
loop() 每2000毫秒(2秒)执行一次。
if ((millis() - lastTime) > timerDelay) {
// Set values to send
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = "Hello";
myData.e = false;
// Send message via ESP-NOW
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
lastTime = millis();
}
ESP8266 NodeMCU 接收器程序 (ESP-NOW)
将以下代码上传到您的ESP8266 NodeMCU 接收器板。
代码如何工作
与发送方类似,首先包含库:
#include <esp_now.h>
#include <WiFi.h>
创建一个结构来接收数据。该结构应该与发送方程序中定义的结构相同。
typedef struct struct_message {
char a[32];
int b;
float c;
String d;
bool e;
} struct_message;
创建一个结构消息变量称为我的数据。
struct_message myData;
创建一个回调函数,当 ESP8266 通过 ESP-NOW 接收数据时将调用该函数。该函数称为onDataRecv()并且应该接受如下几个参数:
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
我们复制一下内容传入数据数据变量放入我的数据variable。
memcpy(&myData, incomingData, sizeof(myData));
现在我的数据结构体内部包含多个变量,其中包含发送者 ESP8266 发送的值。访问变量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("String: ");
Serial.println(myData.d);
Serial.print("Bool: ");
Serial.println(myData.e);
Serial.println();
在函数 setup() 里面,初始化串行通信以进行调试。
Serial.begin(115200);
将设备设置为Wi-Fi站点。
WiFi.mode(WIFI_STA);
初始化 ESP-NOW:
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
设置ESP8266角色:
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
注册接收数据时将调用的回调函数。在这种情况下,我们注册OnDataRecv()之前创建的函数。
esp_now_register_recv_cb(OnDataRecv);
测试 ESP-NOW 通信
将发送器程序上传到一个板,将接收器程序上传到另一块板。不要忘记在发送者程序上插入接收者 MAC 地址。
现在,打开两个 Arduino IDE 窗口。一个用于接收方,另一个用于发送方。打开每个板的串行监视器。每块板应该有不同的 COM 端口。
这是您应该在发送方获得的信息。
这就是您应该在接收方得到的。请注意,Int 变量在收到的每个读数之间会发生变化(因为我们在发送方将其设置为随机数)。
我们测试了两块板之间的通信范围,在空旷的场地中我们能够获得长达 140 米(约 459 英尺)的稳定通信。在此实验中,两个 ESP8266 板载天线相互指向。
总结
在本教程中,您学习了如何将 ESP-NOW 与 ESP8266 NodeMCU 板结合使用。现在,您可以组合发送器和接收器程序,以便进行双向通信(每个板同时充当服务器和发送器)。您还可以使用更多开发板来实现多个开发板之间的通信。
ESP8266 和 ESP32 板可以使用 ESP-NOW 通信协议相互通信。您只需将正确的程序上传到每个板上即可。要了解如何将 ESP-NOW 与 ESP32 结合使用,您可以阅读我们的ESP32 ESP-NOW 入门指南。