ESPNOW
帧格式
默认比特率为1Mbps
-----------------------------------------------------------------------
··MAC 报头··|··分类代码··|··组织标识符··|··随机值··|··供应商特定内容··|··FCS··
-----------------------------------------------------------------------
··24 字节······1 字节········3 字节······4 字节······7~255 字节······4···
-----------------------------------------------------------------------
- 分类代码:分类代码:分类代码字段用于指示各个供应商的类别
- 组织标识符:包含一个唯一的标识符,为乐鑫指定的MAC地址的前三个字节
- 随机值:防止重放攻击
- 供应商特定内容:供应商特定内容包含供应商特定字段,如下所示:
-----------------------------------------------------------------------
|···元素 ID···|···长度···|···组织标识符···|···类型···|···版本···|···正文···|
-----------------------------------------------------------------------
···1 字节········1 字节·······3 字节········1 字节·····1 字节·· ·0-250 字节
-----------------------------------------------------------------------
- 元素 ID:元素 ID 字段可用于指示特定于供应商的元素。
- 长度:长度是组织标识符、类型、版本和正文的总长度。
- 组织标识符:组织标识符包含一个唯一标识符 (比如 0x18fe34),为乐鑫指定的 MAC 地址的前三个字节。
- 类型:类型字段设置为 4,代表 ESP-NOW。
- 版本:版本字段设置为 ESP-NOW 的版本。
- 正文:正文包含 ESP-NOW 数据。
初始化和反初始化
esp_now_init()&&esp_now_deinit():
ESP-NOW 数据必须在 Wi-Fi 启动后传输,因此建议在初始化 ESP-NOW 之前启动 Wi-Fi,并在反初始化 ESP-NOW 之后停止 Wi-Fi, 当调用 esp_now_deinit() 时,配对设备的所有信息都将被删除.
添加配对设备
- 在将数据发送到其他设备之前,请先调用
esp_now_add_peer()
将其添加到配对设备列表中。配对设备的最大数量是 20。如果启用了加密,则必须设置 LMK。ESP-NOW 数据可以从 Station 或 Softap 接口发送。 确保在发送 ESP-NOW 数据之前已启用该接口。在发送广播数据之前必须添加具有广播 MAC 地址的设备。配对设备的信道范围是从 0 ~14。如果信道设置为 0,数据将在当前信道上发送。否则,必须使用本地设备所在的通道。
发送 ESP-NOW 数据
- 调用
esp_now_send()
发送 ESP-NOW 数据,调用esp_now_register_send_cb
注册发送回调函数。如果 MAC 层成功接收到数据,则该函数将返回ESP_NOW_SEND_SUCCESS
事件。否则,它将返回ESP_NOW_SEND_FAIL
。ESP-NOW 数据发送失败可能有几种原因,比如目标设备不存在、设备的信道不相同、动作帧在传输过程中丢失等。应用层并不一定可以总能接收到数据。如果需要,应用层可在接收 ESP-NOW 数据时发回一个应答 (ACK) 数据。如果接收 ACK 数据超时,则将重新传输 ESP-NOW 数据。可以为 ESP-NOW 数据设置序列号,从而删除重复的数据。 - 如果有大量 ESP-NOW 数据要发送,则调用
esp_now_send()
一次性发送不大于 250 字节的数据。 请注意,两个 ESP-NOW 数据包的发送间隔太短可能导致回调函数返回混乱。因此,建议在等到上一次回调函数返回 ACK 后再发送下一个 ESP-NOW 数据。发送回调函数从高优先级的 Wi-Fi 任务中运行。因此,不要在回调函数中执行冗长的操作。相反,将必要的数据发布到队列,并交给优先级较低的任务处理。
接收 ESP-NOW 数据
- 调用
esp_now_register_recv_cb
注册接收回调函数。当接收 ESP-NOW 数据时,需要调用接收回调函数。接收回调函数也在 Wi-Fi 任务任务中运行。因此,不要在回调函数中执行冗长的操作。 相反,将必要的数据发布到队列,并交给优先级较低的任务处理。
读取Mac地址
#include <WiFi.h>
void setup() {
Serial.begin(9600);
Serial.println();
#ifdef ESP8266
Serial.print("ESP8266 Board MAC Address: ");
Serial.println(WiFi.macAddress());
#elif defined ESP32
WiFi.mode(WIFI_MODE_STA);
Serial.print("ESP32 Board MAC Address: ");
Serial.println(WiFi.macAddress());
#endif
}
void loop() {
}
ESPNOW发送
#include <WiFi.h>
#include <esp_now.h>
// 设置掌控板声音传感器与光线传感器引脚编号
const int soundPin = 36;
const int lightPin = 39;
// 设置数据结构体
typedef struct struct_message {
String board_name;
double light;
double sound;
} struct_message;
struct_message myData;
// 接收设备的 MAC 地址
uint8_t broadcastAddress[] = {0x24, 0x6F, 0x28, 0x88, 0x62, 0x80};
// 数据发送回调函数
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
char macStr[18];
Serial.print("Packet to: ");
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);
Serial.print("Send status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
Serial.println();
}
void setup() {
Serial.begin(9600);
// 初始化 ESP-NOW
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// 设置发送数据回调函数
esp_now_register_send_cb(OnDataSent);
// 绑定数据接收端
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// 检查设备是否配对成功
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
}
void loop() {
// 设置要发送的数据
myData.board_name = "mPython_#1";
myData.light = analogRead(lightPin);
myData.sound = analogRead(soundPin);
// 发送数据
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(1000);
}
ESPNOW接收函数
#include <WiFi.h>
#include <esp_now.h>
// 设置数据结构体
typedef struct struct_message {
String board_name;
double light;
double sound;
} struct_message;
struct_message myData;
// 数据接收回调函数
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&myData, incomingData, sizeof(myData));
Serial.print("board_name: ");
Serial.println(myData.board_name);
Serial.print("light: ");
Serial.println(myData.light);
Serial.print("sound:");
Serial.println(myData.sound);
Serial.println();
}
void setup() {
Serial.begin(9600);
// 初始化 ESP-NOW
WiFi.mode(WIFI_STA);
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// 设置接收数据回调函数
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
}