文章总结(帮你们节约时间)
- 你的 ESP32S3 可以不依赖路由器,直接和其他 ESP32S3 小伙伴“私聊”。
- 实现这种“秘密通讯”的关键技术叫做 ESP-NOW,是乐鑫官方提供的一种快速、低功耗的直连方案。
- 使用 ESP-NOW,你需要知道对方的“门牌号”(MAC 地址),然后就能像发短信一样直接发送和接收数据。
- 这种方式特别适合需要快速响应、低延迟或者没有外部网络覆盖的场景,比如遥控、传感器网络等。
前言:我的 ESP32S3 好像有了自己的想法…
老铁们,有没有觉得你桌子上的 ESP32S3 有点“闷闷不乐”?每天勤勤恳恳地连上你家那个信号时好时坏、密码还贼难记的路由器,像个卑微的“打工仔”,等着网络的“投喂”。它难道就没有一点自己的追求吗?难道它就不渴望拥有“社交自由”吗?!
你可能会说:“嘿,它不就是个开发板嘛,想那么多干啥?” 此言差矣!今天,咱们就要揭竿而起,带领我们的 ESP32S3 搞一场轰轰烈烈的“独立运动”!我们要让它们彻底摆脱对路由器的依赖,用自己的天线,直接向同伴发射爱的信号(或者数据包,都一样啦)!
想象一下:两个 ESP32S3,相隔数米,不需要任何中间商(路由器)赚差价,就能你一言我一语地交换信息。这感觉,是不是像极了武侠小说里的“传音入密”?或者更接地气一点,像上课时偷偷传的小纸条?甭管像啥,反正就是酷毙了!
“等等,”你可能会问,“没有路由器,它们怎么知道要把信号发给谁?难道是靠缘分?” 问得好!这当然不是靠缘分,而是靠一种专门为 ESP 设备设计的“黑科技”—— ESP-NOW!
为啥要“叛逆”?抛弃路由器的 N 个理由
在我们深入研究 ESP-NOW 这门“传音秘术”之前,先得搞明白,为啥要放着好好的路由器不用,非得让 ESP32S3 自己“搞事情”呢?难道是吃饱了撑的?
当然不是!在很多场景下,这种“直连”模式简直是天降神兵:
-
荒山野岭,信号为零?没关系! 想象一下,你在户外搞一个环境监测项目,几个 ESP32S3 分散在不同地点采集数据。方圆百里,别说 Wi-Fi 了,连个鬼影都没有!这时候,让它们组成一个自给自足的 ESP-NOW 网络,互相传递数据,最后汇总到一个有 GPRS 或 LoRa 模块的节点上,岂不美哉?路由器?那是什么,能吃吗?
-
速度!速度!只要速度! 在某些应用里,比如无线遥控小车、无人机或者实时游戏控制器,零点几秒的延迟都可能导致“车毁人亡”。数据包先发给路由器,路由器再转发给目标 ESP32S3,这一来一回,时间就耽搁了。ESP-NOW 可是点对点直达,省去了中间环节,延迟低到让你怀疑人生!就像你想告诉隔壁桌同学答案,是直接扔纸条快,还是先交给老师再由老师转交快?(嗯?好像哪里不对?)
-
简单即是美! 有时候,你就是想让两个 ESP32S3 简单地交换点信息,比如一个按钮按下,另一个灯亮起。为了这么点事儿,还得配置路由器、处理 IP 地址、搞 TCP/UDP 连接……是不是有点杀鸡用牛刀了?ESP-NOW 的配置相对简单得多,代码也更清爽,让你专注于核心业务逻辑,而不是网络配置的泥潭。
-
省电?或许吧! 虽然 ESP-NOW 本身不是像 BLE 那样的超低功耗技术,但相比于维持一个完整的 Wi-Fi 连接(尤其是在 AP 模式下),它在发送数据时才激活无线电,并且协议开销更小,理论上能更省电一些。当然,具体效果取决于你的使用方式和发送频率。
-
组建“秘密社团”! 使用 ESP-NOW,你的 ESP32S3 设备可以形成一个不依赖外部网络的、私密的通信圈子。数据只在它们之间流动,路由器?它根本不知道这个“小团体”的存在!是不是有点地下党接头的感觉?
所以你看,让 ESP32S3 “拒绝”路由器,自己发射信号,绝非一时兴起,而是实实在在的需求和优势驱动的!
揭秘“传音入密”:ESP-NOW 是个啥玩意儿?
好了,铺垫了这么多,终于轮到我们的主角——ESP-NOW 登场了!
ESP-NOW 是乐鑫(Espressif)官方推出的一种无连接的 Wi-Fi 通信协议。注意这三个字:“无连接”!这意味着什么?
意味着它不像我们平时上网那样,需要先经过 TCP 的“三次握手”建立一个稳定的连接通道,然后再开始传输数据。ESP-NOW 更像是 UDP 的“亲戚”,或者说,像发短信:你知道对方的“手机号”(在 ESP-NOW 里是 MAC 地址),直接把信息编辑好,咻~ 一下发过去就完事了!对方收到算它运气好,收不到……嗯,你可以选择让对方回个信(ACK),或者干脆就不管了(效率优先嘛)。
ESP-NOW 的核心理念可以概括为:
- 基于 MAC 地址通信:每个 Wi-Fi 设备都有一个全球唯一的物理地址,叫做 MAC 地址。ESP-NOW 通信不依赖 IP 地址,而是直接使用这个 MAC 地址来识别对方。就像快递员送货看的是门牌号(MAC),而不是住户的名字(IP)。
- 配对机制:在发送数据之前,你需要告诉发送方 ESP32S3:“喂,这个 MAC 地址是我的朋友,以后你要给它发东西。”这个过程叫做“添加配对”(Add Peer)。你可以配对多个设备,实现一对多通信。
- 短包快速传输:ESP-NOW 主要设计用于传输少量数据(每个包最大 250 字节),比如传感器读数、控制指令等。它的协议开销小,传输速度快。
- 回调机制:当 ESP32S3 收到一个 ESP-NOW 数据包时,会触发一个你预先定义好的回调函数。你可以在这个函数里处理接收到的数据。同样,发送数据后,也可以设置一个回调函数来检查数据是否发送成功(对方是否回复了 ACK)。
- 可以加密:担心你的“小纸条”被别人偷看?ESP-NOW 支持使用预设的密钥(LMK, Local Master Key)对传输的数据进行 AES 加密,增加通信的安全性。
- 可与 Wi-Fi/蓝牙共存:ESP-NOW 可以和普通的 Wi-Fi STA(连接路由器)、SoftAP(自己当热点)模式甚至蓝牙共存,互不干扰(当然,无线电是共享的,极端情况下可能有性能影响)。
简单来说,ESP-NOW 就是为 ESP 设备之间进行快速、简单、直接的数据交换而量身定做的!它抛弃了 TCP/IP 协议栈中许多对于简单通信而言不必要的复杂性,从而实现了低延迟和高效率。
实战演练:让两块 ESP32S3“隔空对话”!
理论讲完了,是不是已经摩拳擦掌,迫不及待想让你的 ESP32S3 说出第一句“悄悄话”了?别急,代码这就奉上!
场景设定: 我们用两块 ESP32S3 开发板,一块作为“发送者”(Sender),定时向另一块“接收者”(Receiver)发送一条简单的消息。
准备工作:
- 两块 ESP32S3 开发板。
- Arduino IDE 及 ESP32 支持包。
- USB 数据线。
- 关键一步:获取两块板子的 MAC 地址!
如何获取 MAC 地址?
很简单,给你的 ESP32S3 烧录下面这段简短的代码,然后打开串口监视器,就能看到它的 MAC 地址了。记下这两个地址,后面要用!
#include "WiFi.h"
void setup(){
Serial.begin(115200);
Serial.println();
Serial.print("ESP32 Board MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop(){
// Nothing to do here
}
假设你获取到的地址是:
- 板子 A (Sender) 的 MAC 地址:
AA:BB:CC:11:22:33
- 板子 B (Receiver) 的 MAC 地址:
DD:EE:FF:44:55:66
接收者 (Receiver) 代码:
#include <esp_now.h>
#include <WiFi.h>
// 回调函数,当收到 ESP-NOW 数据时会调用这个函数
void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {
char macStr[18];
Serial.print("Packet received from: ");
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("Bytes received: ");
Serial.println(len);
Serial.print("Data: ");
// 打印接收到的数据
for (int i = 0; i < len; i++) {
Serial.print((char)incomingData[i]);
}
Serial.println();
Serial.println("--------------------------------");
}
void setup() {
Serial.begin(115200);
Serial.println("ESP-NOW Receiver Starting...");
// 1. 设置 Wi-Fi 模式为 STA
// 即使不连路由器,ESP-NOW 也需要 Wi-Fi 硬件处于活动状态
WiFi.mode(WIFI_STA);
Serial.print("My MAC Address is: ");
Serial.println(WiFi.macAddress()); // 再次确认自己的 MAC
// 2. 初始化 ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return; // 初始化失败,没法玩了
} else {
Serial.println("ESP-NOW Initialized Successfully!");
}
// 3. 注册接收回调函数
// 告诉 ESP-NOW,收到数据后去调用 OnDataRecv 这个函数
esp_now_register_recv_cb(OnDataRecv);
Serial.println("Ready to receive ESP-NOW packets.");
}
void loop() {
// 对于接收方来说,主要工作都在回调函数里完成了
// loop 可以保持空闲,或者做点别的事情
delay(1000); // 无所事事地等待...
}
发送者 (Sender) 代码:
#include <esp_now.h>
#include <WiFi.h>
// 把你要发送消息的目标板子的 MAC 地址填在这里!
uint8_t broadcastAddress[] = {0xDD, 0xEE, 0xFF, 0x44, 0x55, 0x66}; // 替换成你的接收者 MAC 地址
// 发送数据的结构体,可以自定义
typedef struct struct_message {
char message[32];
int counter;
} struct_message;
// 创建一个结构体变量
struct_message myData;
// 发送状态回调函数 (可选,但推荐使用)
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);
Serial.println("ESP-NOW Sender Starting...");
// 1. 设置 Wi-Fi 模式为 STA
WiFi.mode(WIFI_STA);
Serial.print("My MAC Address is: ");
Serial.println(WiFi.macAddress()); // 再次确认自己的 MAC
// 2. 初始化 ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
} else {
Serial.println("ESP-NOW Initialized Successfully!");
}
// 3. 注册发送回调函数 (可选)
esp_now_register_send_cb(OnDataSent);
// 4. 添加配对信息 (Peer Info)
// 这是告诉 ESP-NOW 你要和谁通信
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, broadcastAddress, 6); // 设置对方 MAC 地址
peerInfo.channel = 0; // 使用当前 Wi-Fi 信道,通常 0 即可自动选择
peerInfo.encrypt = false; // 我们这里先不加密,简单点
// peerInfo.ifidx = ESP_IF_WIFI_STA; // 指定使用哪个 Wi-Fi 接口
// 添加配对
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return; // 添加失败,发不了了
} else {
Serial.print("Peer Added: ");
for(int i=0; i<6; ++i) {
Serial.printf("%02X", broadcastAddress[i]);
if (i<5) Serial.print(":");
}
Serial.println();
}
Serial.println("Ready to send ESP-NOW packets.");
}
int count = 0;
void loop() {
// 准备要发送的数据
strcpy(myData.message, "Hello from Sender!");
myData.counter = count++;
// 发送数据!
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
if (result == ESP_OK) {
Serial.println("Sent with success");
}
else {
Serial.print("Error sending the data. Error code: ");
Serial.println(result); // 打印错误码有助于调试
// 常见的错误可能是 peer 没加对,或者 ESP-NOW 没初始化成功
}
delay(2000); // 每 2 秒发送一次
}
操作步骤:
- 将接收者代码中的
WiFi.mode(WIFI_STA);
下面的Serial.println(WiFi.macAddress());
取消注释(如果之前没获取过 MAC 的话)。 - 将发送者代码中的
broadcastAddress
数组替换成你接收者板子的实际 MAC 地址。 - 分别将代码上传到两块 ESP32S3 板子上。
- 先给接收者板子上电,并打开其串口监视器。你应该能看到它打印出自己的 MAC 地址并提示“Ready to receive ESP-NOW packets”。
- 再给发送者板子上电,并打开其串口监视器。你应该能看到它打印自己的 MAC,添加配对成功,然后开始每 2 秒发送一次数据,并显示发送状态。
- 切换回接收者的串口监视器,见证奇迹的时刻到了!你应该能看到它开始打印接收到的数据包信息,包括发送者的 MAC 地址和我们发送的 “Hello from Sender!” 以及递增的计数器!
成功了没?是不是很简单!你的两块 ESP32S3 已经在不依赖任何外部网络的情况下,愉快地“聊”起来了!
还能玩出什么花样?ESP-NOW 的进阶玩法
刚才只是最基础的点对点通信,ESP-NOW 的潜力远不止于此:
- 一对多广播:发送方可以添加多个接收者的 MAC 地址作为配对,然后循环向每个配对设备发送数据。或者,使用广播 MAC 地址
FF:FF:FF:FF:FF:FF
可以将数据包发送给所有在同一信道上的 ESP-NOW 设备(接收方不需要预先添加发送方为 peer,但需要初始化 ESP-NOW 并注册接收回调)。不过广播是“尽力而为”,不保证送达,也没有发送成功的回调。 - 多对一汇聚:多个传感器节点(发送方)可以将数据发送给一个中心节点(接收方)。接收方需要将所有发送方的 MAC 地址都添加为配对(如果需要回复或管理),或者简单地接收来自任何设备的 ESP-NOW 数据(如果不需要回复)。
- 简单中继/伪 Mesh:虽然 ESP-NOW 不是真正的 Mesh 协议,但你可以写代码实现简单的中继功能。比如设备 A 发给设备 B,设备 B 收到后再转发给设备 C。这可以有限地扩展通信距离,但要注意避免数据包循环和拥堵。
- 加密通信:如果你担心数据被窃听,可以在添加配对时设置
peerInfo.encrypt = true;
并提供一个共享的 16 字节密钥peerInfo.lmk
。这样发送和接收双方都会自动进行 AES 加密和解密。
当然,ESP-NOW 也有它的局限性:
- 传输距离:受限于 Wi-Fi 信号的物理特性,通常在开阔地带几十米到上百米,室内会因墙壁等障碍物衰减。
- 数据包大小:最大 250 字节,不适合传输大文件或视频流。
- 配对数量:最多可以添加约 20 个配对设备(加密模式下数量可能减少)。
- 可靠性:无连接协议本身不保证 100% 送达,需要应用层自己处理重传或确认机制(ESP-NOW 的发送回调可以告知是否收到 ACK,算是一种底层确认)。
另一种选择:用 SoftAP 组建“局域网”
除了 ESP-NOW,还有一种让 ESP32S3 们“自力更生”的方式:让其中一个“大佬”开启 SoftAP 模式,扮演一个迷你路由器的角色,其他的 ESP32S3 则作为客户端(STA)连接到这个 SoftAP 上。
这样,它们就组成了一个小型的、不依赖外部路由器的局域网。在这个局域网内,你可以使用更标准的网络协议,比如 TCP、UDP、HTTP、MQTT 等来进行通信。
优点:
- 可以使用成熟的网络库和协议。
- 方便实现更复杂的 C/S 或 P2P 应用模型。
缺点:
- 需要一个中心节点(SoftAP),如果它挂了,整个网络就瘫了。
- 相比 ESP-NOW,协议开销更大,连接建立更慢,延迟可能更高。
- SoftAP 节点功耗相对较高。
怎么选?
- 如果你只需要设备间快速、简单地交换少量状态或控制信息,追求低延迟,ESP-NOW 通常是更好的选择。
- 如果你需要在设备间传输更复杂的数据,或者想利用现有的基于 TCP/IP 的服务(比如搭建一个本地的 Web Server 或 MQTT Broker),那么 SoftAP 模式更合适。
你可以根据你的具体应用场景来权衡。