开源硬件
Arduino
客制化键盘
Arduino_寄存器
二进制运算
寄存器+二进制运算
LCD-逐字显示
密码依次录入
等待输入
WiFi Duck(无线击键注入攻击平台)
WiFi Duc-New
WiFi Duc-Old
蓝牙无线烧录
ESP8266
ESP-NOW
ESP8266看门狗
ESP8266-休眠模式
ESP01/01S使用说明
WIFI_SD
ESP8266-Web服务器
ESP8266-WIFI自动认证
ESP32
ESP32 ADC2
ESP32_PWM
ESP32_CAM
ESP32 小坦克
ESP32_限电保护
Arduino IDE 添加 ESP32
ESP32-iPhone BLE攻击
STM32
STM32F103-虚拟键盘
STC
STC8G1K08(A)
树莓派-触摸屏
Arduino IDE
Arduino_自制库
Arduino库收集
常见排序算法
冒泡排序
选择排序
插入排序
希尔排序
归并排序
快速排序
计数排序
预处理
millis(运行时长)
Arduino IDE 2.X-修改数据位置
Mixly
Mixly安装教程
Mixly 模块介绍
Mixly-添加ESP32CAM支持
Mixly-库定制工具
模块
4G模块连接物联网
GPS模块
语音模块(JQ8900)
安信可VB语音识别
28BYJ-48(5V步进)
FreeRTOS
FreeRTOS-多任务基础
FreeRTOS-任务共享全局变量
FreeRTOS-多核多任务
FreeRTOS-MUTEX
FreeRTOS-常规程序改多任务
FreeRTOS-定时器
LaserGRBL(激光雕刻)
LaserGRBL-GRBL
GRBL-CNC Shield v4
MicroPython
Scratch
Wokwi(在线仿真)
html转无符号数组
待做开源项目
本文档使用 MrDoc 发布
-
+
首页
ESP-NOW
## 什么是ESP-NOW ESP-NOW 是由乐鑫开发的另一款无线通信协议,可以使多个设备在没有或不使用 Wi-Fi 的情况下进行通信。这种协议类似常见于无线鼠标中的低功耗 2.4GHz 无线连接——设备在进行通信之前要进行配对。配对之后,设备之间的连接是持续的、点对点的,并且不需要握手协议。它是一种短数据传输、无连接的快速通信技术,可以让低功耗控制器直接控制所有智能设备而无需连接路由器,适用于智能灯、遥控控制、传感器数据回传等场景。 使用了 ESP-NOW 通信之后,如果某一个设备突然断电之后,只要它一旦重启,就是自动连接到对应的节点中重新进行通信。 ESP-NOW 支持如下特性: * 单播包加密或单播包不加密通信; * 加密配对设备和非加密配对设备混合使用; * 可携带最长为 250 字节的有效 payload 数据; * 支持设置发送回调函数以通知应用层帧发送失败或成功。 * 同样,ESP-NOW 也存在一些限制: 暂时不支持广播包; * 加密配对设备有限制,Station 模式下最多支持10 个加密配对设备;SoftAP * 或 SoftAP + Station 混合模式下最多支持 6 个加密配对设备。非加密配对* * 设备支持若干,与加密设备总数和不超过 20 个; * 有效 payload 限制为 250 字节。 ## ESP-NOW 通信方式 ### 一对一单向通信 一对一单向通信是最简单的通信方式,也就是一个设备负责发送数据,另一个设备负责接收数据,如下图所示:  ### 一对多单向通信 一对多单向通信是指一个设备负责发送数据,多个设备负责接收数据。其中数据发送端就类似与遥控器,数据接收端可以负责分别控制不同的设备,如下图所示:  ### 多对一单向通信 多对一单向通信是指一个设备专门负责接收数据,其余设备则向它发送数据。这种场景主要应用于多个设备采集不同的传感器数据,然后向中心或者总控制器汇总数据,如下图所示:  ### 双向通信 相对于单向通信,双向通信是指通信的双方既可以发送数据、又可以接收数据。一对一双向通信如下图所示:  在双向通信中,也可以加入更多的设备,进行两两之间的数据交互,如下图所示:  当然以上的这些通信,不仅仅限于 ESP32 开发板之间的通信,`所有支持 ESP-NOW 的设备之间都可以进行通信`,比如 ESP32 与 ESP32 之间、ESP8266 与 ESP8266 之间、甚至 ESP32 与 ESP8266 之间,都可以进行 ESP-NOW 无线通信。 传输距离(开阔的场地环境下): * `ESP8266`板载天线可获得长达 `140` 米的稳定通信 * `ESP32`板载天线可获得长达 `220` 米的稳定通信 ## 实际使用 以下都基于**Arduino IDE**来使用,如果IDE没有其相应主控,自行安装 `https://dl.espressif.com/dl/package_esp32_index.json` `http://arduino.esp8266.com/stable/package_esp8266com_index.json` ### 获取设备 MAC 地址 ESP-NOW 是点对点的通讯方式,在发送数据时需要指定接收设备,这好比你给对方发送 QQ 消息必须知道对方的 QQ 号一样。 在这里我们一般通过设备的 MAC 地址作为区分不同接收设备的凭证。 ``` #ifdef ESP8266 //判断是不是ESP8266 #include <ESP8266WiFi.h> //8266的WIFI库为`ESP8266WiFi.h` #elif defined ESP32 //判断是不是ESP32 #include <WiFi.h> //32的WIFI库为`WiFi.h` #endif void setup(){ Serial.begin(115200); Serial.println(); Serial.print("MAC Address: "); Serial.println(WiFi.macAddress()); //WiFi.macAddress(),8266/32的WiFi库都有这 } void loop(){ } ``` 注意ESP8266/ESP32的WiFi库不一样  这个就是 MAC 地址,每个使用ESP-NOW的设备都需要记录,以便使用 所有通讯设备都有MAC,且独一无二 ### NOW 数据发送 在发送方,程序应包含: * 初始化 ESP-NOW; * 发送数据时注册回调函数—— **发送数据**发送消息时将执行函数。这可以告诉我们消息是否成功传递; * 添加对等设备(接收方)。为此,您需要知道接收方的 MAC 地址; * 向对等设备发送消息。 #### ESP8266 ``` //加载NOW所需的ESP8266库 #include <ESP8266WiFi.h> #include <espnow.h> //接收设备的 MAC 地址,设置 FF:FF:FF:FF:FF:FF 为广播 //可以同时定义多个设备 uint8_t mac[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; //创建一个包含我们要发送的数据类型的结构。我们称这种结构 结构消息,可以更改里面包含的变量类型和数量 //发送数据的结构,必须与接收结构匹配 typedef struct struct_message { char a[32]; int b; float c; bool d; } struct_message; //创建变量类型 结构消息 名称为 myData ,这将存储发送的数据 struct_message myData; //字符串形式的发送数据,注意,目前字符串形式只能串口输出,无法调用 uint8_t myData2[] = {"esp8266"}; //发送信息时的回调函数,每次发送信息会自动调用该函数 void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { Serial.print("最后数据包发送状态: "); if (sendStatus == 0){ Serial.println("交付成功"); //打印接收方mac地址 //for(int i = 0; i < 5; ++i){ // Serial.printf("%x:", *(mac_addr + i)); //} //Serial.printf("%x", *(mac_addr + 5)); } else{ Serial.println("交付失败"); } } void setup() { Serial.begin(115200); //设定波特率 WiFi.mode(WIFI_STA); //ESP-NOW时基于WIFI的,这个命令主要是开启WIFI,也可设置为 WIFI_AP 模式 //初始化ESP-NOW,不需要返回,可以直接执行 esp_now_init(); if (esp_now_init() != 0) { Serial.println("初始化 ESP-NOW 时出错"); return; } //初始化NOW服务并设置状态 esp_now_set_self_role(ESP_NOW_ROLE_COMBO); //ESP_NOW_ROLE_CONTROLLER,//控制方 //ESP_NOW_ROLE_SLAVE,//被控制方 //ESP_NOW_ROLE_COMBO,//控制方&被控制方双角色,双向通信时就用它 //执行完这个函数,每次发送数据就会自动调用回调函数了 esp_now_register_send_cb(OnDataSent); //与新设备配对 esp_now_add_peer(mac, ESP_NOW_ROLE_COMBO, 1, NULL, 0); } void loop() { //设置要发送的数据,通过 [数组名].[数据名] 来写入发送数据的内容 strcpy(myData.a, "THIS IS A CHAR"); //必须要使用 strcpy(myData.a, "内容"); 来把内容复制进char*中 myData.b = random(1,20); //生成一个随机数 myData.c = 1.2; //小数 myData.d = false; //布尔值 //通过ESP-NOW发送消息 esp_now_send(mac, (uint8_t *) &myData, sizeof(myData)); //esp_now_send(mac2, myData2, sizeof(myData2)); //可以给多个设备发送数据,也可以发送不一样的数据,但是要注意,同一设备只能接受一个数据结构的 delay(1000); } ``` #### ESP32 ESP32和ESP8266发送端基本程序大体一致,主要是`esp_now_add_peer()`不同 ``` //加载NOW所需的ESP32库 #include <esp_now.h> #include <WiFi.h> //接收设备的 MAC 地址,设置 FF:FF:FF:FF:FF:FF 为广播 //可以同时定义多个设备 uint8_t mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //创建一个包含我们要发送的数据类型的结构。我们称这种结构 结构消息,可以更改里面包含的变量类型和数量 //发送数据的结构,必须与接收结构匹配 typedef struct struct_message { char a[32]; int b; float c; bool d; } struct_message; //创建变量类型 结构消息 名称为 myData ,这将存储发送的数据 struct_message myData; //发送信息时的回调函数,每次发送信息会自动调用该函数 void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print("最后数据包发送状态:\t"); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "交付成功" : "交付失败"); //这是判断的缩写,判断是否发送成功,成功执行第一项,不成功执行第二项 } void setup() { Serial.begin(115200); //设定波特率 WiFi.mode(WIFI_STA); //ESP-NOW时基于WIFI的,这个命令主要是开启WIFI,也可设置为 WIFI_AP 模式 //初始化ESP-NOW,不需要返回,可以直接执行 esp_now_init(); if (esp_now_init() != ESP_OK) { Serial.println("初始化 ESP-NOW 时出错"); return; } //执行完这个函数,每次发送数据就会自动调用回调函数了 esp_now_register_send_cb(OnDataSent); //注册设备 esp_now_peer_info_t peerInfo; memcpy(peerInfo.peer_addr, mac, 6); peerInfo.channel = 0; peerInfo.encrypt = false; //与新设备配对 if (esp_now_add_peer(&peerInfo) != ESP_OK){ Serial.println("无法添加对等服务器"); return; } } void loop() { //设置要发送的数据,通过 [数组名].[数据名] 来写入发送数据的内容 strcpy(myData.a, "THIS IS A CHAR"); //必须要使用 strcpy(myData.a, "内容"); 来把内容复制进char*中 myData.b = random(1,20); //生成一个随机数 myData.c = 1.2; //小数 myData.d = false; //布尔值 //通过ESP-NOW发送消息 esp_now_send(mac, (uint8_t *) &myData, sizeof(myData)); //可以使用带发送反馈的方式来进行发送消息,可以查看是否发送成功 // esp_err_t result = esp_now_send(mac, (uint8_t *) &myData, sizeof(myData)); // if (result == ESP_OK) { // Serial.println("数据发送成功"); // } // else { // Serial.println("发送数据时出错"); // } delay(1000); } ``` ### NOW 数据接收 在接收方,程序应包括: * 初始化 ESP-NOW; * 注册接收回调函数**数据接收**。这是一个将在收到消息时执行的函数。 * 在该回调函数中,将消息保存到一个变量中,以使用该信息执行任何任务。 #### ESP8266 ``` //加载NOW所需的ESP8266库 #include <ESP8266WiFi.h> #include <espnow.h> //创建包含多数据的结构,要与发送端一致 typedef struct struct_message { char a[32]; int b; float c; bool d; } struct_message; //创建变量类型 结构消息 名称为 myData ,这将存储发送的数据 struct_message myData; //接收信息时的回调函数,每次接收到信息会自动调用该函数 void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) { memcpy(&myData, incomingData, sizeof(myData)); Serial.print("数据长度: "); Serial.println(len); Serial.print("数据a: "); Serial.println(myData.a); Serial.print("数据b: "); Serial.println(myData.b); Serial.print("数据c: "); Serial.println(myData.c); Serial.print("数据d: "); Serial.println(myData.d); Serial.println(); //打印发送方mac地址 //for(int i = 0; i < 5; ++i){ // Serial.printf("%x:", *(mac + i)); //} //Serial.printf("%x", *(mac + 5)); //数组类型数据 //for(int i = 0; i < len; ++i){ // Serial.printf("%c", *(incomingData + i)); //} } void setup() { Serial.begin(115200); //设定波特率 WiFi.mode(WIFI_STA); //ESP-NOW时基于WIFI的,这个命令主要是开启WIFI,也可设置为 WIFI_AP 模式 //初始化ESP-NOW,不需要返回,可以直接执行 esp_now_init(); if (esp_now_init() != 0) { Serial.println("初始化 ESP-NOW 时出错"); return; } //初始化NOW服务并设置状态 esp_now_set_self_role(ESP_NOW_ROLE_SLAVE); //ESP_NOW_ROLE_CONTROLLER,//控制方 //ESP_NOW_ROLE_SLAVE,//被控制方 //ESP_NOW_ROLE_COMBO,//控制方&被控制方双角色,双向通信时就用它 //执行完这个函数,每次接收到数据就会自动调用回调函数了 esp_now_register_recv_cb(OnDataRecv); } void loop() { } ``` #### ESP32 ESP32和ESP8266 接收端大致,接收体的回调函数需要调整下,调用库更改下,程序可以通用,注意发送消息结构和发送方一致 ``` //加载NOW所需的ESP32库 #include <esp_now.h> #include <WiFi.h> //接收数据的结构,必须与发送结构匹配 typedef struct struct_message { char a[32]; int b; float c; bool d; } struct_message; //创建变量类型 结构消息 名称为 myData ,这将存储发送的数据 struct_message myData; //接收信息时的回调函数,每次接收信息会自动调用该函数 void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { memcpy(&myData, incomingData, sizeof(myData)); Serial.print("数据长度: "); Serial.println(len); Serial.print("数据a: "); Serial.println(myData.a); Serial.print("数据b: "); Serial.println(myData.b); Serial.print("数据c: "); Serial.println(myData.c); Serial.print("数据d: "); Serial.println(myData.d); Serial.println(); } void setup() { Serial.begin(115200); //设定波特率 WiFi.mode(WIFI_STA); //ESP-NOW时基于WIFI的,这个命令主要是开启WIFI,也可设置为 WIFI_AP 模式 //初始化ESP-NOW,不需要返回,可以直接执行 esp_now_init(); if (esp_now_init() != ESP_OK) { Serial.println("初始化 ESP-NOW 时出错"); return; } //执行完这个函数,每次接收到数据就会自动调用回调函数了 esp_now_register_recv_cb(OnDataRecv); } void loop() { } ``` ### NOW 双向收发 ESP8266 和 ESP32 代码通用,已做硬件适配 这里两个设备使相同的数据格式,故一个代码就行,如果是指定 MAC 手动更改下即可 ``` #ifdef ESP8266 //判断是不是ESP8266 #include <ESP8266WiFi.h> //8266的WIFI库为`ESP8266WiFi.h` #include <espnow.h> //8266的ESP_NOW库 #elif defined ESP32 //判断是不是ESP32 #include <WiFi.h> //32的WIFI库为`WiFi.h` #include <esp_now.h> //32的ESP_NOW库 #endif //接收设备的 MAC 地址,设置 FF:FF:FF:FF:FF:FF 为广播 //可以同时定义多个设备 uint8_t mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //创建一个包含我们要发送的数据类型的结构。我们称这种结构 结构消息,可以更改里面包含的变量类型和数量 //发送数据的结构,必须与接收结构匹配 typedef struct struct_message { char a[32]; int b; float c; bool d; } struct_message; //创建变量类型 结构消息 名称为 myData ,这将存储发送的数据 struct_message myData; //发送信息时的回调函数,每次发送信息会自动调用该函数 #ifdef ESP8266 //如果是ESP8266执行以下程序 void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { #elif defined ESP32 //如果是ESP32执行以下程序 void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t sendStatus) { #endif Serial.print("最后数据包发送状态:\t"); Serial.println(sendStatus == 0 ? "交付成功" : "交付失败"); //这是判断的缩写,判断是否发送成功,成功执行第一项,不成功执行第二项 } //接收信息时的回调函数,每次接收到信息会自动调用该函数 #ifdef ESP8266 //如果是ESP8266执行以下程序 void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) { #elif defined ESP32 //如果是ESP32执行以下程序 void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { #endif memcpy(&myData, incomingData, sizeof(myData)); Serial.print("数据长度: "); Serial.println(len); Serial.print("数据a: "); Serial.println(myData.a); Serial.print("数据b: "); Serial.println(myData.b); Serial.print("数据c: "); Serial.println(myData.c); Serial.print("数据d: "); Serial.println(myData.d); Serial.println(); } void setup() { Serial.begin(115200); //设定波特率 WiFi.mode(WIFI_STA); //ESP-NOW时基于WIFI的,这个命令主要是开启WIFI,也可设置为 WIFI_AP 模式 //初始化ESP-NOW,不需要返回,可以直接执行 esp_now_init(); if (esp_now_init() != 0) { Serial.println("初始化 ESP-NOW 时出错"); return; } //执行完这个函数,每次发送数据就会自动调用回调函数了 esp_now_register_send_cb(OnDataSent); //执行完这个函数,每次接收到数据就会自动调用回调函数了 esp_now_register_recv_cb(OnDataRecv); //注册设备,ESP8266和ESP32不一样,ESP8266要设置角色,ESP32不需要 #ifdef ESP8266 //如果是ESP8266执行以下程序 esp_now_set_self_role(ESP_NOW_ROLE_COMBO); esp_now_add_peer(mac, ESP_NOW_ROLE_COMBO, 1, NULL, 0); #elif defined ESP32 //如果是ESP32执行以下程序 esp_now_peer_info_t peerInfo; memcpy(peerInfo.peer_addr, mac, 6); peerInfo.channel = 0; peerInfo.encrypt = false; //与新设备配对 if (esp_now_add_peer(&peerInfo) != ESP_OK){ Serial.println("无法添加对等服务器"); return; } #endif } void loop() { //设置要发送的数据,通过 [数组名].[数据名] 来写入发送数据的内容 strcpy(myData.a, "THIS IS A CHAR"); //必须要使用 strcpy(myData.a, "内容"); 来把内容复制进char*中 myData.b = random(1,20); //生成一个随机数 myData.c = 1.2; //小数 myData.d = false; //布尔值 //通过ESP-NOW发送消息 esp_now_send(mac, (uint8_t *) &myData, sizeof(myData)); delay(1000); } ``` ### NOW 加密通讯 通用程序,已适配ESP8266和ESP32,MAC自行更改 ``` #ifdef ESP8266 //判断是不是ESP8266 #include <ESP8266WiFi.h> //8266的WIFI库为`ESP8266WiFi.h` #include <espnow.h> //8266的ESP_NOW库 #elif defined ESP32 //判断是不是ESP32 #include <WiFi.h> //32的WIFI库为`WiFi.h` #include <esp_now.h> //32的ESP_NOW库 #endif //接收设备的 MAC 地址,设置 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF 为广播 //可以同时定义多个设备 uint8_t mac[] = {0x7C, 0x9E, 0xBD, 0xF5, 0x23, 0x6C}; //ESP32 //uint8_t mac[] = {0x3C, 0x71, 0xBF, 0xF4, 0x64, 0x0C}; //ESP32 //uint8_t mac[] = {0xC4, 0x5B, 0xBE, 0x63, 0x5D, 0x3B}; //ESP8266 //设置主密钥(PMK)和设备密钥(LMK),设备密钥最多6个,注意只能正好16个字符!!! static const char* PMK_KEY_STR = "EXACTLY_SIXTEEN!"; //同项目的设备主密钥要一致 static const char* LMK_KEY_STR = "NOT_MANY_SIXTEEN"; //连接设备间的设备密钥要一致 //创建一个包含我们要发送的数据类型的结构。我们称这种结构 结构消息,可以更改里面包含的变量类型和数量 //发送数据的结构,必须与接收结构匹配 typedef struct struct_message { // char a[32]; //可以使用字符串行驶接收 String a; int b; float c; bool d; } struct_message; //创建变量类型 结构消息 名称为 myData ,这将存储发送的数据 struct_message myData; //发送信息时的回调函数,每次发送信息会自动调用该函数 #ifdef ESP8266 //如果是ESP8266执行以下程序 void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { #elif defined ESP32 //如果是ESP32执行以下程序 void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t sendStatus) { #endif Serial.print("最后数据包发送状态:\t"); Serial.println(sendStatus == 0 ? "交付成功" : "交付失败"); //这是判断的缩写,判断是否发送成功,成功执行第一项,不成功执行第二项 } //接收信息时的回调函数,每次接收到信息会自动调用该函数 #ifdef ESP8266 //如果是ESP8266执行以下程序 void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) { #elif defined ESP32 //如果是ESP32执行以下程序 void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { #endif memcpy(&myData, incomingData, sizeof(myData)); Serial.print("数据长度: "); Serial.println(len); Serial.print("数据a: "); Serial.println(myData.a); Serial.print("数据b: "); Serial.println(myData.b); Serial.print("数据c: "); Serial.println(myData.c); Serial.print("数据d: "); Serial.println(myData.d); Serial.println(); } void setup() { Serial.begin(115200); //设定波特率 WiFi.mode(WIFI_STA); //ESP-NOW时基于WIFI的,这个命令主要是开启WIFI,也可设置为 WIFI_AP 模式 //初始化ESP-NOW,不需要返回,可以直接执行 esp_now_init(); if (esp_now_init() != 0) { Serial.println("初始化 ESP-NOW 时出错"); return; } //执行完这个函数,每次发送数据就会自动调用回调函数了 esp_now_register_send_cb(OnDataSent); //执行完这个函数,每次接收到数据就会自动调用回调函数了 esp_now_register_recv_cb(OnDataRecv); //设置设备密钥 //注册设备,ESP8266和ESP32不一样,ESP8266要设置角色,ESP32不需要 #ifdef ESP8266 //如果是ESP8266执行以下程序 esp_now_set_self_role(ESP_NOW_ROLE_COMBO); //设置主密钥(PMK) esp_now_set_kok((uint8_t *)PMK_KEY_STR, 16); //带设备密钥(LMK)注册设备 esp_now_add_peer(mac, ESP_NOW_ROLE_COMBO, 1, (uint8_t *)LMK_KEY_STR, 16); #elif defined ESP32 //如果是ESP32执行以下程序 //设置 PMK 密钥 esp_now_set_pmk((uint8_t *)PMK_KEY_STR); //注册设备 esp_now_peer_info_t peerInfo; memcpy(peerInfo.peer_addr, mac, 6); peerInfo.channel = 0; //设置 LMK 密钥(如果不加密,for省略) for (uint8_t i = 0; i < 16; i++) { peerInfo.lmk[i] = LMK_KEY_STR[i]; } peerInfo.encrypt = true; //是否加密,true加密/false不加密 //与新设备配对 if (esp_now_add_peer(&peerInfo) != ESP_OK){ Serial.println("无法添加对等服务器"); return; } #endif } void loop() { //设置要发送的数据,通过 [数组名].[数据名] 来写入发送数据的内容 // strcpy(myData.a, "THIS IS A CHAR"); //必须要使用 strcpy(myData.a, "内容"); 来把内容复制进char*中 myData.a = 'Hoooooo!'; myData.b = random(1,20); //生成一个随机数 myData.c = 1.2; //小数 myData.d = false; //布尔值 //通过ESP-NOW发送消息 esp_now_send(mac, (uint8_t *) &myData, sizeof(myData)); delay(1000); } ``` #### ESP8266 Key 规则概述(主Key要一致,不支持广播包加密): * Key1 🔁 Key1 //相同Key可以互通 * NULL 🔁 NULL //都为NULL Key也可以互通 * Key1 ❎ Key2 //Key不同不通 * NULL ➡️ Key1 //NULL Key可以给带Key的发送,但是不能接收 ```cpp //key 只能设置为uint8_t [16] uint8_t PMK_KEY_STR[16]= {0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44,0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x42}; uint8_t LMK_KEY_STR[16]= {0x00, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44,0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x00}; //也可以使用这种形式,同ESP32一样 static const char* PMK_KEY_STR = "PLEASE_CHANGE_ME"; static const char* LMK_KEY_STR = "DONT_BE_LAZY_OK?"; //使用 (uint8_t *)PMK_KEY_STR 这种形式进行调用 //设置通讯加密的主密钥(key of key),所有设备的通讯共享同一主密钥 //不设置会使用默认主密钥 //需在 esp_now_add_peer 和 esp_now_set_peer_key 之前调用 esp_now_set_kok(PMK_KEY_STR, 16); //([密钥],[长度(仅支持16字节)]) //增加NOW匹配设备,将MAC存入NOW的维护列表 //([MAC],[角色],[通讯信道(1~13)],[密钥],[密钥长度(限16)]) esp_now_add_peer(mac,ESP_NOW_ROLE_COMBO, 1, LMK_KEY_STR, 16); //重新设置匹配设备的NOW密钥,会覆盖原有 //([MAC],[密钥],[密钥长度(限16)]) esp_now_set_peer_key(mac, LMK_KEY_STR, 16); ``` #### ESP32 收发设备都一样配置,主密钥(PMK)各个连接设备都有一样,设备密钥(LMK)为两个设备之间的密钥,最多6个 ``` //设置 PMK 和 LMK 密钥, LMK 最多可有6个 static const char* PMK_KEY_STR = "PLEASE_CHANGE_ME"; //主密钥 static const char* LMK_KEY_STR = "DONT_BE_LAZY_OK?"; //设备密钥 //设置 PMK 密钥,放在 esp_now_init() 后面 esp_now_set_pmk((uint8_t *)PMK_KEY_STR); //创建新设备的连接参数 //放着 esp_now_add_peer() 设备连接前面 esp_now_peer_info_t peerInfo; memcpy(peerInfo.peer_addr, mac, 6); peerInfo.channel = 0; //以下for循环为写入加密密钥,如果不加密是不需要的 for (uint8_t i = 0; i < 16; i++) { peerInfo.lmk[i] = LMK_KEY_STR[i]; } peerInfo.encrypt = true; //是否加密,不加密设置为 false ``` ### NOW 其他函数 [ESP-IDF_ESP-NOW 编程指南](https://demo-dijiudu.readthedocs.io/en/latest/api-reference/wifi/esp_now.html) #### ESP8266 ``` enum esp_now_role { ESP_NOW_ROLE_IDLE,//未设置角色,不允许发送数据 ESP_NOW_ROLE_CONTROLLER,//控制方 ESP_NOW_ROLE_SLAVE,//被控制方 ESP_NOW_ROLE_COMBO,//控制方&被控制方双角色,双向通信时就用它 }; //回调函数 typedef void (*esp_now_recv_cb_t)(u8 *mac_addr, u8 *data, u8 len);//接收端 typedef void (*esp_now_send_cb_t)(u8 *mac_addr, u8 status);//发送端 int esp_now_init(void);//初始化esp_now int esp_now_deinit(void);//取消esp_now的初始化 int esp_now_register_send_cb(esp_now_send_cb_t cb);//使用该函数之后,接收到数据会自动调用接收回调函数,回调函数的写法可以参考我上面的代码 int esp_now_unregister_send_cb(void);//与上面的函数作用相反 int esp_now_register_recv_cb(esp_now_recv_cb_t cb);//使用该函数之后,发送数据后会自动调用发送回调函数,回调函数的写法可以参考我上面的代码 int esp_now_unregister_recv_cb(void);//与上面的函数作用相反 int esp_now_send(u8 *da, u8 *data, int len);//发送数据,MAC地址中传入 全FF 或是 NULL 会广播 int esp_now_add_peer(u8 *mac_addr, u8 role, u8 channel, u8 *key, u8 key_len);//与新设备配对 int esp_now_del_peer(u8 *mac_addr);//将已配对的设备删除 int esp_now_set_self_role(u8 role);//设定设备自己的角色 int esp_now_get_self_role(void);//获取设备自己的角色 int esp_now_set_peer_role(u8 *mac_addr, u8 role);//设定某个已配对设备的角色 int esp_now_get_peer_role(u8 *mac_addr);//获取某个已配对设备的角色 int esp_now_set_peer_channel(u8 *mac_addr, u8 channel);//设定某个已配对设备的WiFi通道(1~13) int esp_now_get_peer_channel(u8 *mac_addr);//获取某个已配对设备的WiFi通道(1~13) int esp_now_set_peer_key(u8 *mac_addr, u8 *key, u8 key_len);//设定某个已配对设备的密钥 int esp_now_get_peer_key(u8 *mac_addr, u8 *key, u8 *key_len);//获取某个已配对设备的密钥 int esp_now_is_peer_exist(u8 *mac_addr);//检查已经配对的设备是否在线 int esp_now_set_kok(u8 *key, u8 len);//对通信的key进行加密,不设置时使用默认的PMK ``` #### ESP32 ESP32 不需要设定角色就可以实现控制与被控制 ``` 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 接收数据时,会调用一个函数。 ```
造物者W
2024年5月13日 17:38
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码