【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/master
和receiver/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 <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(){
}
重置自定义 MAC 地址
在某些应用程序中,为您的开发板提供自定义 MAC 地址可能很有用。但是,如前所述,这不会覆盖制造商设置的 MAC 地址。因此,每次您重置电路板或上传新代码时,它都会恢复为默认 MAC 地址。
#include <WiFi.h>
#include <esp_wifi.h>
// 设置你自定义的 MAC 地址
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(){
}
上传代码后,以 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 <esp_now.h>
#include <WiFi.h>
// 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);
}
ESP32 接收器
// Receiver接收端
#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 接收到数据时将被调用
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() {
}
还有更多与 ESP-NOW 相关的功能可以使用。
例如:管理peers、删除peers、扫描从属设备等……在 Arduino IDE 中转到文件>示例> ESP32 > ESPNow 并选择其中一个示例查看完整示例。
参考资料
- [1] 【RNT】Getting Started with ESP-NOW (ESP32 with Arduino IDE)
- [2] 【ESPRESS】ESP-NOW(用户指南) | 【ESPRESS】ESP-NOW编程指南
- [3] 【RNT】ESP32 开发板之间的 ESP-NOW 双向通信
- [4] 【RNT】带有 ESP32 的 ESP-NOW:将数据发送到多个板(一对多)
- [5] 【RNT】带有 ESP32 的 ESP-NOW:从多个板接收数据(多对一)
- [6] 【RNT】ESP32:ESP-NOW Web 服务器传感器仪表板(ESP-NOW + Wi-Fi)