ESP32学习---ESP-NOW
- 基于Arduino IDE环境
- 获取mac地址
- 单播通讯
- 一对多通讯
- 多对一通讯
- 多对多通讯
- 模块1代码
- 模块2
- 模块3
- 广播通讯
- 基于ESP-IDF框架
乐鑫编程指南中关于ESP-NOW的介绍:https://docs.espressif.com/projects/esp-idf/zh_CN/v5.2.1/esp32/api-reference/network/esp_now.html
乐鑫提供的esp-now仓库:https://github.com/espressif/esp-now
基于Arduino IDE环境
基于arduino环境的ESP-NOW教程:https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/
可以从左侧的目录栏找到对应的ESP-NOW教程的位置,如下所示:
ESP-NOW自动配对功能实现的参考链接:https://randomnerdtutorials.com/esp-now-auto-pairing-esp32-esp8266/
获取mac地址
对于ESP-NOW协议来说mac地址是个不可或缺的数据,模块之间的配对需要用到,这里是获取mac地址的方法:
#include <WiFi.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
WiFi.mode(WIFI_MODE_STA);
Serial.println(WiFi.macAddress());
}
void loop() {
// put your main code here, to run repeatedly:
}
打开串口监视器复位模块显示信息中会包含如下信息即为mac地址
16:38:06.244 -> A0:B7:65:60:E7:B8
单播通讯
一对多通讯
多对一通讯
多对多通讯
这里使用3个模块之间互相传输数据,首先我们先要获取到3个模块的MAC地址,每个模块向外发送数据并同时接收来自于其它两个模块的数据
模块1代码
esp_now_1.ino :
//发送端的程序
#include <WiFi.h>
#include "esp_now.h"
// 1作为发送,2和3作为接收
//接收端的MAC地址 16:38:06.244 -> A0:B7:65:60:E7:B8
uint8_t broadcastAddress1[] = {0xA0,0xB7,0x65,0x4F,0x27,0x30};
uint8_t broadcastAddress2[] = {0xA0,0xB7,0x65,0x60,0xE7,0xB8};
uint8_t broadcastAddress3[] = {0xA0,0xB7,0x65,0x48,0x78,0x9C};//A0:B7:65:48:78:9C
// uint8_t broadcastAddress2[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
// uint8_t broadcastAddress3[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
//发送数据类型
typedef struct {
char a[32];
int b;
float c;
bool d;
} Message_t;
Message_t msg, rcv_msg;
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 OnDataRecv(const uint8_t *mac_addr, const uint8_t *incomingData, int len) {
memcpy(&rcv_msg, incomingData, sizeof(rcv_msg));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Char: ");
Serial.print(rcv_msg.a);
Serial.print("Int: ");
Serial.println(rcv_msg.b);
Serial.print("float: ");
Serial.println(rcv_msg.c);
Serial.print("Bool: ");
Serial.println(rcv_msg.d);
Serial.println();
}
esp_now_peer_info_t peerInfo2;
esp_now_peer_info_t peerInfo3;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
WiFi.mode(WIFI_MODE_STA);
Serial.println(WiFi.macAddress());
if(esp_now_init() != ESP_OK){
Serial.println("Error init ESP-NOW");
return;
}
esp_now_register_send_cb(OnDataSent); //注册发送回调函数
esp_now_register_recv_cb(OnDataRecv); //注册接收回调函数
//注册通信频道
// esp_now_peer_info_t peerInfo;
memcpy(peerInfo2.peer_addr, broadcastAddress2, 6);
char macStr[18];
snprintf(macStr,sizeof(macStr),"%02x:%02x:%02x:%02x:%02x:%02x", peerInfo2.peer_addr[0],
peerInfo2.peer_addr[1],peerInfo2.peer_addr[2],peerInfo2.peer_addr[3],
peerInfo2.peer_addr[4], peerInfo2.peer_addr[5]);
Serial.print("mac addr: ");Serial.println(macStr);
peerInfo2.channel = 1; //通道
peerInfo2.encrypt = false; //是否加密
if(esp_now_add_peer(&peerInfo2) != ESP_OK) {
Serial.println("Failed to add peer 2");
return;
}
// 3
memcpy(peerInfo3.peer_addr, broadcastAddress3, 6);
peerInfo3.channel = 1; //
peerInfo3.encrypt = false;
if(esp_now_add_peer(&peerInfo3) != ESP_OK) {
Serial.println("Failed to add peer 3");
return;
}
}
void loop() {
// put your main code here, to run repeatedly:
strcpy(msg.a, "this is a char");
msg.b = random(1, 20);
msg.c = 10.0f;
msg.d = false;
esp_err_t result = esp_now_send(0, (uint8_t *)&msg, sizeof(msg));
if(result == ESP_OK) {
Serial.println("Sent with success");
} else {
Serial.println("Error sending the data");
}
delay(200);
}
模块2
esp_now_2.ino
// 接收端程序
#include <WiFi.h>
#include "esp_now.h"
//2作为发送,1和3作为接收
uint8_t broadcastAddress1[] = {0xA0,0xB7,0x65,0x4F,0x27,0x30};
uint8_t broadcastAddress2[] = {0xA0,0xB7,0x65,0x60,0xE7,0xB8};
uint8_t broadcastAddress3[] = {0xA0,0xB7,0x65,0x48,0x78,0x9C};//A0:B7:65:48:78:9C
typedef struct {
char a[32];
int b;
float c;
bool d;
}Message_t;
Message_t send_msg, rcv_msg;
void OnDataSend(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 OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len){
memcpy(&rcv_msg, incomingData, sizeof(rcv_msg));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Char: ");
Serial.print(rcv_msg.a);
Serial.print("Int: ");
Serial.println(rcv_msg.b);
Serial.print("float: ");
Serial.println(rcv_msg.c);
Serial.print("Bool: ");
Serial.println(rcv_msg.d);
Serial.println();
}
esp_now_peer_info_t peerInfo1;
esp_now_peer_info_t peerInfo3;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.println(WiFi.macAddress());
if(esp_now_init() != ESP_OK) {
Serial.println("Error init ESP-NOW");
return;
}
esp_now_register_recv_cb(OnDataRecv); //注册接收信息回调函数
esp_now_register_send_cb(OnDataSend);
//设置通信频道
//esp_now_peer_info_t peerInfo;
memcpy(peerInfo1.peer_addr, broadcastAddress1, 6);
peerInfo1.channel = 1; //通道
peerInfo1.encrypt = false;
if(esp_now_add_peer(&peerInfo1) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
memcpy(peerInfo3.peer_addr, broadcastAddress3, 6);
peerInfo3.channel = 1; //通道
peerInfo3.encrypt = false;
if(esp_now_add_peer(&peerInfo3) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
}
void loop() {
// put your main code here, to run repeatedly:
strcpy(send_msg.a, "this is a slaver");
send_msg.b = random(1,50);
send_msg.c = 20.0f;
send_msg.d = true;
esp_err_t result = esp_now_send(0, (uint8_t *)&send_msg, sizeof(send_msg));
if(result == ESP_OK){
Serial.println("Send with success");
} else {
Serial.println("Error sending the data");
}
delay(200);
}
模块3
esp_now_3.ino
// 接收端程序
#include <WiFi.h>
#include "esp_now.h"
//3作为发送,1和2作为接收
uint8_t broadcastAddress1[] = {0xA0,0xB7,0x65,0x4F,0x27,0x30};
uint8_t broadcastAddress2[] = {0xA0,0xB7,0x65,0x60,0xE7,0xB8};
uint8_t broadcastAddress3[] = {0xA0,0xB7,0x65,0x48,0x78,0x9C};//A0:B7:65:48:78:9C
typedef struct {
char a[32];
int b;
float c;
bool d;
}Message_t;
Message_t send_msg, rcv_msg;
void OnDataSend(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 OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len){
memcpy(&rcv_msg, incomingData, sizeof(rcv_msg));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Char: ");
Serial.print(rcv_msg.a);
Serial.print("Int: ");
Serial.println(rcv_msg.b);
Serial.print("float: ");
Serial.println(rcv_msg.c);
Serial.print("Bool: ");
Serial.println(rcv_msg.d);
Serial.println();
}
esp_now_peer_info_t peerInfo1;
esp_now_peer_info_t peerInfo2;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.println(WiFi.macAddress());
if(esp_now_init() != ESP_OK) {
Serial.println("Error init ESP-NOW");
return;
}
esp_now_register_recv_cb(OnDataRecv); //注册接收信息回调函数
esp_now_register_send_cb(OnDataSend);
//设置通信频道
//esp_now_peer_info_t peerInfo;
memcpy(peerInfo1.peer_addr, broadcastAddress1, 6);
peerInfo1.channel = 1; //通道
peerInfo1.encrypt = false;
if(esp_now_add_peer(&peerInfo1) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
memcpy(peerInfo2.peer_addr, broadcastAddress2, 6);
peerInfo2.channel = 1; //通道
peerInfo2.encrypt = false;
if(esp_now_add_peer(&peerInfo2) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
}
void loop() {
// put your main code here, to run repeatedly:
strcpy(send_msg.a, "this is a slaver");
send_msg.b = random(1,50);
send_msg.c = 30.0f;
send_msg.d = true;
esp_err_t result = esp_now_send(0, (uint8_t *)&send_msg, sizeof(send_msg));
if(result == ESP_OK){
Serial.println("Send with success");
} else {
Serial.println("Error sending the data");
}
delay(200);
}
广播通讯
参考链接:https://dronebotworkshop.com/esp-now/中的Broadcast Mode
章节
在这个模式下测试至少需要两个ESP32模块,相对于上面的多对多通讯的实现而言,这种方式可以不用提前配对且可以多个模块共用一套代码。当然广播通讯模式可也能会有一些问题:例如有些产品通过网络广播的形式形成了一个网络,可能会受到其它网络通过广播模式发数据的影响,具体的情况还需还要根据实际的应用来考虑。
这里通过广播模式每隔0.5S发送一次hello world
,可以通过MAC地址确定对应的信息是由哪个模块发送来的。
esp_now_broadcast.ino
#include "WiFi.h"
#include "esp_now.h"
void formatMacAddress(const uint8_t *macAddr, char *buffer, int maxLength){
snprintf(buffer, maxLength, "%02X:%02X:%02X:%02X:%02X:%02X", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
}
void receiveCallback(const uint8_t *macAddr, const uint8_t *data, int dataLen) {
// 将接收到的数据复制到缓存中,最大允许250个字节 + 1个null终止字节
char buffer[ESP_NOW_MAX_DATA_LEN+1];
int msgLen = min(ESP_NOW_MAX_DATA_LEN, dataLen);
strncpy(buffer, (const char *)data, msgLen);
buffer[msgLen] = 0; //确保最后一个字节为null
char macStr[18];
formatMacAddress(macAddr, macStr, 18);
Serial.printf("Received message from: %s - %s", macStr, buffer);
}
void sendCallback(const uint8_t *macAddr, esp_now_send_status_t status) {
char macStr[18];
formatMacAddress(macAddr, macStr, 18);
Serial.print("Last Packet Send to: ");
Serial.println(macStr);
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
esp_now_peer_info_t peerInfo = {};
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
void broadcast(const String &message) {
// uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// esp_now_peer_info_t peerInfo = {};
memcpy(&peerInfo.peer_addr, broadcastAddress, 6);
if(!esp_now_is_peer_exist(broadcastAddress)) {
esp_now_add_peer(&peerInfo);
}
esp_err_t result = esp_now_send(peerInfo.peer_addr, (const uint8_t *)message.c_str(), message.length());
if(result == ESP_OK) {
Serial.println("Broadcast message success");
} else if(result == ESP_ERR_ESPNOW_NOT_INIT) {
Serial.println("ESP-NOW not Init");
} else if(result == ESP_ERR_ESPNOW_ARG) {
Serial.println("Invalid Argument");
} else if(result == ESP_ERR_ESPNOW_INTERNAL) {
Serial.println("Internal Error");
} else if(result == ESP_ERR_ESPNOW_NO_MEM) {
Serial.println("ESP_ERR_ESPNOW_NO_MEM");
} else if(result == ESP_ERR_ESPNOW_NOT_FOUND) {
Serial.println("Peer not found");
} else {
Serial.println("Unknown error");
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
delay(1000);
WiFi.mode(WIFI_STA);
Serial.println("ESP-NOW Broadcast Demo");
Serial.print("MAC Address: ");
Serial.println(WiFi.macAddress());
WiFi.disconnect();
if(esp_now_init() == ESP_OK) {
Serial.println("ESP-NOW init success");
esp_now_register_recv_cb(receiveCallback);
esp_now_register_send_cb(sendCallback);
} else {
Serial.println("ESP-NOW init failed");
delay(3000);
ESP.restart();
}
}
void loop() {
// put your main code here, to run repeatedly:
broadcast("hello world!");
delay(500);
}
基于ESP-IDF框架
github上找到的基于网状网络的ESP-NOW协议组件:https://github.com/aZholtikov/zh_network/tree/main
- 无需配对即可进行数据传输
- 支持广播模式和单播模式
- …
详细的说明可到链接地址查看,后续有时间可以研究下。
后续内容待添加。。。