本教程介绍如何设置 ESP32 开发板,通过 ESP-NOW 通信协议(多对一配置)接收来自多个 ESP32 开发板的数据。如果您想将多个传感器节点的数据收集到一个 ESP32 开发板中,此配置是理想的选择。这些板将使用Arduino IDE进行编程。
我们还有其他与 ESP-NOW 相关的指南,您可能对此感兴趣:
ESP-NOW 入门(ESP32 with Arduino IDE)
ESP-NOW ESP32 开发板之间的双向通信
ESP-NOW with ESP32:向多个开发板发送数据(一对多)
本教程介绍如何设置 ESP32 开发板,通过 ESP-NOW 通信协议(多对一配置)接收来自多个 ESP32 开发板的数据,如下图所示。
注意:在 ESP-NOW 文档中,没有“发送者/主站”和“接收者/从站”之类的东西。每个板都可以是发送方或接收方。但是,为了清楚起见,我们将使用术语“发送方”和“接收方”或“主”和“从属”。
我们将使用 Arduino IDE 对 ESP32 开发板进行编程,因此在继续本教程之前,请确保已在 Arduino IDE 中安装这些开发板。
我们将使用 Arduino IDE 对 ESP32/ESP8266 开发板进行编程,因此在继续本教程之前,请确保已在 Arduino IDE 中安装这些开发板。
在 Arduino IDE 中安装 ESP32 开发板(Windows、Mac OS X 和 Linux)
在Arduino IDE中安装ESP8266板(Windows,Mac OS X,Linux)
要学习本教程,您需要多个 ESP32 开发板。所有 ESP32 型号都可以正常工作。我们尝试了不同型号的 ESP32 开发板,并且都运行良好(ESP32 DOIT 开发板、TTGO T-Journal、ESP32 with OLED 开发板和 ESP32-CAM)。
要通过 ESP-NOW 发送消息,您需要知道接收器板的 MAC 地址。每个开发板都有一个唯一的 MAC 地址(了解如何获取和更改 ESP32 MAC 地址)。
将以下代码上传到每个接收器板以获取其 MAC 地址。
// Complete Instructions to Get and Change ESP MAC Address: https://RandomNerdTutorials.com/get-change-esp32-esp8266-mac-address-arduino/
#ifdef ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
void setup(){
Serial.begin(115200);
Serial.println();
Serial.print("ESP Board MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop(){
}
上传代码后,按 RST/EN 按钮,MAC 地址应显示在串行监视器上。
接收方可以通过其唯一的MAC地址来识别每个发送方。但是,在接收端处理不同的MAC地址以识别哪个板发送了哪个消息可能很棘手。
因此,为了方便起见,我们将用一个唯一的编号(编号) 从 1 开始。如果您有三块板,则一块板的 ID 号为 1,另一块板的 ID 号为 2,最后是 3 号。该 ID 将与其他变量一起发送给接收方。
例如,我们将交换包含板的结构编号数字和两个随机数x和y如下图所示。
将以下代码上传到每个发送方板。别忘了增加编号每个发送板的编号。
/*********
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-now-many-to-one-esp32/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
#include <esp_now.h>
#include <WiFi.h>
// REPLACE WITH THE RECEIVER'S 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 {
int id; // must be unique for each sender board
int x;
int y;
} struct_message;
// Create a struct_message called myData
struct_message myData;
// Create peer interface
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
myData.id = 1;
myData.x = random(0,50);
myData.y = random(0,50);
// 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(10000);
}
包括无线上网和esp_now库。
#include <esp_now.h>
#include <WiFi.h>
在以下行中插入接收方的 MAC 地址。
uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0x15, 0xC7, 0xFC};
然后,创建一个包含我们要发送的数据的结构。我们称这种结构为struct_message它包含三个整数变量:板编号,x和y.您可以更改它以发送所需的任何变量类型(但不要忘记在接收端也进行更改)。
typedef struct struct_message {
int id; // must be unique for each sender board
int x;
int y;
} struct_message;
创建一个类型的新变量struct_message这被称为myData(我的数据)这将存储变量的值。
struct_message myData;
创建类型的变量esp_now_peer_info_t存储有关对等方的信息。
esp_now_peer_info_t peerInfo;
OnDataSent() 回调函数
接下来,定义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");
}
在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;
}
成功初始化 ESP-NOW 后,注册发送消息时将调用的回调函数。在这种情况下,请注册OnDataSent()之前创建的函数。
esp_now_register_send_cb(OnDataSent);
要将数据发送到另一块板(接收器),您需要将其配对为对等体。以下行注册并添加新的对等体。
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;
}
在loop(),我们将每 10 秒通过 ESP-NOW 发送一条消息(您可以更改此延迟时间)。
为每个变量赋值。
myData.id = 1;
myData.x = random(0,50);
myData.y = random(0,50);
不要忘记更改每个发送方板的 ID。
请记住,myData(我的数据)是一种结构。在这里,分配要在结构内发送的值。在本例中,我们只是发送编号和随机值x和y.例如,在实际应用中,这些应替换为命令或传感器读数。
最后,通过 ESP-NOW 发送消息。
// 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");
}
将以下代码上传到 ESP32 接收器板。该代码准备接收来自三个不同板的数据。您可以轻松修改代码,以从不同数量的电路板接收数据。
/*********
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-now-many-to-one-esp32/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
#include <esp_now.h>
#include <WiFi.h>
// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
int id;
int x;
int y;
}struct_message;
// Create a struct_message called myData
struct_message myData;
// Create a structure to hold the readings from each board
struct_message board1;
struct_message board2;
struct_message board3;
// Create an array with all the structures
struct_message boardsStruct[3] = {board1, board2, board3};
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {
char macStr[18];
Serial.print("Packet received from: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.println(macStr);
memcpy(&myData, incomingData, sizeof(myData));
Serial.printf("Board ID %u: %u bytes\n", myData.id, len);
// Update the structures with the new incoming data
boardsStruct[myData.id-1].x = myData.x;
boardsStruct[myData.id-1].y = myData.y;
Serial.printf("x value: %d \n", boardsStruct[myData.id-1].x);
Serial.printf("y value: %d \n", boardsStruct[myData.id-1].y);
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() {
// Acess the variables for each board
/*int board1X = boardsStruct[0].x;
int board1Y = boardsStruct[0].y;
int board2X = boardsStruct[1].x;
int board2Y = boardsStruct[1].y;
int board3X = boardsStruct[2].x;
int board3Y = boardsStruct[2].y;*/
delay(10000);
}
与发送方类似,首先包括库:
#include <esp_now.h>
#include <WiFi.h>
创建一个结构来接收数据。此结构应与发送方草图中定义的结构相同。
typedef struct struct_message {
int id;
int x;
int y;
} struct_message;
创建一个struct_message称为myData(我的数据)这将保存接收到的数据。
struct_message myData;
然后,创建一个struct_message变量,以便我们可以将接收到的数据分配给相应的板。在这里,我们将为三个发送方板创建结构。如果有更多的发送方板,则需要创建更多结构。
struct_message board1;
struct_message board2;
struct_message board3;
创建一个包含所有板结构的数组。如果您使用的是不同数量的板,则需要对其进行更改。
struct_message boardsStruct[3] = {board1, board2, board3};
创建一个回调函数,当 ESP32 通过 ESP-NOW 接收到数据时调用该函数。该函数被调用onDataRecv()并应接受以下几个参数:
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len)
获取板子MAC地址:
char macStr[18];
Serial.print("Packet received from: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.println(macStr);
复制传入数据data 变量添加到myData(我的数据)变量。
memcpy(&myData, incomingData, sizeof(myData));
现在,myData(我的数据)结构包含多个变量,其值由 ESP32 发送方之一发送。我们可以通过其 ID 来识别哪个板发送数据包:myData.id.
这样,我们可以将接收到的值分配给板结构数组:
boardsStruct[myData.id-1].x = myData.x;
boardsStruct[myData.id-1].y = myData.y;
例如,假设您收到一个来自 ID 为 2 的板的数据包。的值myData.id,为 2。
因此,您要更新董事会2结构。这董事会2structure 是索引为 1 的元素板结构数组。这就是我们减去 1 的原因,因为 C 中的数组索引为 0。如果您查看下图,可能会有所帮助。
在setup(),初始化串行监视器。
Serial.begin(115200);
将设备设置为 Wi-Fi Station。
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);
循环中注释的以下行举例说明了如果要访问每个板结构的变量,需要执行的操作。例如,要访问x值董事会1:
int board1X = boardsStruct[0].x;
将发送方代码上传到每个发送方板。不要忘记为每个板子提供不同的 ID。
将接收器代码上传到 ESP32 接收器板。不要忘记修改结构以匹配发送板的数量。
在发件人的串行监视器上,如果邮件正确传递,您应该会收到“传递成功”消息。
在接收器板上,您应该接收来自所有其他板的数据包。在这个测试中,我们从5个不同的电路板接收数据。
🥳🥳🥳现在,我们在本教程中,您学习了如何使用 ESP-NOW(多对一配置)设置 ESP32 以接收来自多个 ESP32 开发板的数据。🛹🛹🛹从而实现对外部世界进行感知,充分认识这个有机与无机的环境,后期会持续分享esp32跑freertos实用案列🥳🥳🥳科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
参考文献: