ESP-NOW 是乐鑫自主研发的无连接通信协议,具有短数据包传输功能。该协议使多个设备能够以简单的方式相互通信。
ESP-NOW 功能
ESP-NOW 支持以下功能:
- 加密和未加密的单播通信;
- 混合加密和未加密的对等设备;
- 最多可携带 250 字节 的有效载荷;
- 发送回调功能,可以设置用于通知应用层传输成功或失败。
ESP-NOW 技术还存在以下局限性:
- 有限的加密对等体。Station 模式最多支持 10 个加密对等体,在 SoftAP 或 SoftAP + Station 模式下最多 6 个;
- 支持多个未加密的对等体,但其总数应小于 20 个,包括加密的对等体;
- 有效负载限制为 250 字节。
简单来说,ESP-NOW 是一种快速通信协议,可用于在 ESP32 开发板之间交换小消息(最大 250 字节)。
ESP-NOW 通信
1、 单向通信,这种配置非常容易实现,适合将数据从一块板发送到另一块板
2、一对多通信,一个 ESP32 向不同的 ESP32 发送相同或不同的命令,适合构建类似遥控器的东西
3、多对一通信,将多个传感器节点的数据收集到一个 ESP32 中。可以配置为Web服务器,以显示来自所有其他板的数据
ESP32 获取 MAC 地址
要通过 ESP-NOW 进行通信,需要知道 ESP32 的 MAC 地址,这样就可以知道要将数据发送到哪个设备。
每个 ESP32 都有唯一的 MAC 地址,这就是我们识别每块开发板的方式,以便使用 ESP-NOW 向其发送数据。
// 次示例返回 MAC 地址
#include <Arduino.h>
#include <WiFi.h>
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_MODE_STA);
Serial.println(WiFi.macAddress());
}
void loop()
{
}
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 接收数据时触发 |
发送设备内容:
- 初始化 ESP-NOW;
- 注册发送回调函数 OnDataSent,函数将在发送消息时执行,告诉我们消息是否成功传递;
- 添加对等设备(接收 ESP32),需要知道接收方 MAC 地址;
- 向对等设备发送消息。
接收设备内容:
- 初始化 ESP-NOW;
- 注册接收回调函数 OnDataRecv,在收到消息时将执行;
- 在回调函数中,将消息保存到变量中,以使用该信息执行任何任务。
// 发送设备代码
// 发送四个类型的数据
#include <Arduino.h>
#include <esp_now.h>
#include <WiFi.h>
// CC:7B:5C:25:7B:BC
// 08:D1:F9:EB:52:E8
uint8_t broadcastAddress[] = {0xCC, 0x7B, 0x5C, 0x25, 0x7B, 0xBC};
// 发送的数据结构
typedef struct struct_message
{
char a[32];
int b;
float c;
bool d;
} struct_message;
struct_message myData;
esp_now_peer_info_t peerInfo;
// 发送数据回调函数
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
// 打印消息是否成功传递
Serial.print("Last Packet Send Status:");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_STA); // Wi-Fi Station
if (esp_now_init() != ESP_OK)
{
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_register_send_cb(OnDataSent); // 注册发送数据的回调函数
memcpy(peerInfo.peer_addr, broadcastAddress, 6); // 复制 MAC 地址
peerInfo.channel = 0; // 使用当前打开的通道
peerInfo.encrypt = false; // 未加密
// 添加以上列表
if (esp_now_add_peer(&peerInfo) != ESP_OK)
{
Serial.println("Failed to add peer");
return;
}
}
void loop()
{
// 设置发送信息
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1, 20);
myData.c = 1.2;
myData.d = false;
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(5000);
}
// 接收设备代码
#include <Arduino.h>
#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;
// 接收数据回调函数
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
if (esp_now_init() != ESP_OK)
{
Serial.println("Error initializing ESP-NOW");
return;
}
// 注册接收数据回调函数
esp_now_register_recv_cb(OnDataRecv);
}
void loop()
{
}