ESP-NOW 协议用于允许 ESP-32 控制器以单工模式进行通信。ESP-NOW是乐鑫(Espressif)开发的一种基于Wi-Fi的无线通信协议,具有以下核心特性和应用场景:
一、主要特点
无连接通信:设备无需建立传统Wi-Fi连接即可传输数据,设备配对后可直接进行点对点通信,无需握手协议,连接持续且稳定。
低延迟:端到端传输仅需3 - 10ms,协议精简,将五层OSI上层协议精简为一层,降低了延时,实现了快速响应。
高能效:相比传统Wi-Fi功耗降低80%以上,专为低功耗应用设计,适用于电池供电的设备。
安全加密:支持AES-128数据加密,基于CCMP(Counter Mode Cipher Block Chaining Message Authentication Code Protocol)加密方式保护通信安全,启用加密通信时,所有设备需共享一个主密钥(PMK),并为每个对等连接派生出一个本地主密钥(LMK),确保通信私密性。
灵活组网:支持单播、广播和组播模式,支持“一对多”和“多对多”的设备连接和控制,适用于大规模数据传输,如网络配置、固件升级和调试等场景。
兼容性好:能够与Wi-Fi和Bluetooth LE共存,支持乐鑫ESP8266、ESP32、ESP32-S和ESP32-C等多系列SoC,支持本地控制+远程控制,与路由器、热点共存。
远距离通信:可在视距500m稳定通信,部分设备射频功率最大能到20dBm(100mW),比几乎所有不带PA的2.4G模块都要强。
资源占用少:CPU、flash资源占用少,减轻了CPU和Flash负担,适合作为设备配置、调试和固件升级的辅助模块。
快速配对:支持快速且用户友好的设备配对,当某一设备突然断电之后,通电重启,会自动连接到对应节点中,实现快速通信。
二、应用场景
智能家居:如灯光/窗帘控制,可实现设备的快速响应和低功耗控制。
工业物联网:用于传感器数据采集,支持大规模设备连接和数据传输。
农业监测:构建温湿度传感器网络,实现远距离、低功耗的数据采集。
运动设备:如可穿戴设备数据同步,满足设备对低功耗和快速响应的需求。
安防系统:实现多节点报警联动,提高安防系统的响应速度和可靠性。
遥控领域:如遥控玩具小车、小飞机、机器人等,可达到毫秒级的控制延时,实现比蓝牙传输更远的控制距离。
低功耗设备:基于ESP32-C2的纽扣电池无线开关方案,无需网关就可以和ESP-NOW结合使用,反应速度灵敏,支持超长待机,且产品形态不会被电池体积限制。
三、工作原理
通信架构:基于IEEE 802.11的动作帧(Action Frame)机制,充分利用了WiFi的物理层,但绕过了繁重的网络层和传输层,直接在数据链路层上实现设备间的简单高效通信。在物理层,完全依赖WiFi的无线电技术来传输数据,使用2.4GHz频段(与标准WiFi相同),采用OFDM或DSSS调制方式,具体取决于设置的数据速率;在数据链路层,使用WiFi标准中的供应商特定动作帧(Vendor Specific Action Frame)进行通信。
数据帧结构:ESP-NOW的数据帧结构有其特定设计,ESP-NOW库已处理相关复杂细节,用户只需知道它允许ESP设备在不建立完整WiFi连接的情况下,直接基于MAC地址进行通信。
工作流程:
初始化Wi-Fi和ESP-NOW。
注册对端设备(Peer)。
设置发送/接收回调函数。
执行数据传输。
处理确认和重传。
四、代码实现示例
以下是一个简单的ESP-NOW通信示例代码框架:
发送端代码:包含头文件、定义接收端MAC地址、数据结构、回调函数声明,在初始化设置中进行串口初始化、WiFi模式设置、ESP-NOW初始化、注册发送回调函数、对端设备配置和添加等操作,在主循环中准备数据并发送。
接收端代码:包含头文件、定义与发送端完全一致的数据结构、接收回调函数,在初始化设置中进行串口初始化、WiFi模式设置、ESP-NOW初始化、注册接收回调函数等操作,主循环通常为空,数据通过回调处理。






发射机代码
//ESP-NOW: Transmitter
//Ref: Random Nerd Tutorials https://randomnerdtutorials.com
//-----------------------------------------------------------
#include <esp_now.h>
#include <WiFi.h>
//-------------------------------------------------------------------------------------
uint8_t RxMACaddress[] = {0x7C, 0x9E, 0xBD, 0xF5, 0xBE, 0x30};
//-------------------------------------------------------------------------------------
typedef struct TxStruct
{
int potVal;
}TxStruct;
TxStruct sentData;
//-------------------------------------------------------------------------------------
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) //callback function
{
Serial.print("\r\nLast Packet Send Status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
//======================================================================================
void setup()
{
Serial.begin(9600);
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, RxMACaddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
//-------------------------------------------------------------------------------------
if(esp_now_add_peer(&peerInfo) != ESP_OK)
{
Serial.println("Failed to add peer");
return;
}
}
//======================================================================================
void loop()
{
sentData.potVal = analogRead(A0);
//-------------------------------------------------------------------------------------
esp_err_t result = esp_now_send(RxMACaddress, (uint8_t *) &sentData, sizeof(sentData));
//-------------------------------------------------------------------------------------
if (result == ESP_OK) Serial.println("Sent with success");
else Serial.println("Error sending the data");
//-------------------------------------------------------------------------------------
delay(500);
}
接收机代码
//-----------------------------------------------------------
//ESP-NOW: Receiver
//Ref: Random Nerd Tutorials https://randomnerdtutorials.com
//-----------------------------------------------------------
#include <esp_now.h>
#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//-------------------------------------------------------------------------------------
Adafruit_SSD1306 display(128, 64, &Wire, -1);
//-------------------------------------------------------------------------------------
typedef struct RxStruct
{
int potVal;
}RxStruct;
RxStruct receivedData;
//-------------------------------------------------------------------------------------
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len)
{
memcpy(&receivedData, incomingData, sizeof(receivedData));
}
//======================================================================================
void setup()
{
Serial.begin(9600);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
{
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
display.clearDisplay();
//-------------------------------------------------------------------------------------
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK)
{
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_register_recv_cb(OnDataRecv);
}
//======================================================================================
void loop()
{
display.setTextColor(WHITE); display.clearDisplay();
display.setTextSize(2); display.setCursor(20,0); display.print("ESP-NOW");
display.setCursor(10,18); display.print("POT Value");
display.setTextSize(3); display.setCursor(25,42); display.print(receivedData.potVal);
display.display();
}
【Arduino 动手做】通过 ESP-NOW 实现 ESP32 到 ESP32 单工通信
项目链接:https://akuzechie.blogspot.com/2021/05/esp32-to-esp32-communication-via-esp-now.html
项目作者:Anas Kuzechie
项目视频 :https://www.youtube.com/watch?v=GzugxHEzB0g
项目代码:https://akuzechie.blogspot.com/2021/05/esp32-to-esp32-communication-via-esp-now.html
评论