ESP32学习---ESP-NOW

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;}// 3memcpy(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:9Ctypedef 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:9Ctypedef 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; //确保最后一个字节为nullchar 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

  1. 无需配对即可进行数据传输
  2. 支持广播模式和单播模式

  3. 详细的说明可到链接地址查看,后续有时间可以研究下。

后续内容待添加。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/785541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7.1 Mysql shell 定时备份

直接上脚本----linu 定时任务执行 #!/bin/bash# 配置信息 DB_USER"your_username" # 数据库用户名 DB_PASSWORD"your_password" # 数据库密码 DB_NAME"your_database_name" # 要备份的数据库名 BACKUP_DIR"/path/to/backup/directory"…

如何在Ubuntu系统部署Z-blog博客结合cpolar实现无公网IP访问本地网站

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

CrossOver玩游戏会损害电脑吗 CrossOver玩游戏会卡吗 Mac玩游戏 crossover24免费激活

CrossOver是一款可以在macOS上运行Windows应用程序的软件&#xff0c;它利用了Wine技术&#xff0c;无需安装虚拟机或双系统&#xff0c;可以直接在苹果系统下运行Windows游戏。那么&#xff0c;使用CrossOver玩游戏会损害电脑吗&#xff1f;CrossOver玩游戏会卡吗&#xff1f;…

LLaMA-Factory参数的解答

打开LLaMA-Factory的web页面会有一堆参数 &#xff0c;但不知道怎么选&#xff0c;选哪个&#xff0c;这个文章详细解读一下&#xff0c;每个参数到底是什么含义这是个人写的参数解读&#xff0c;我并非该领域的人如果那个大佬看到有参数不对请反馈一下&#xff0c;或者有补充的…

【Entity Framework】EF中的增删改查

【Entity Framework】EF中的增删改查 文章目录 【Entity Framework】EF中的增删改查一、概述二、DbContext数据上下文三、EntityState五个状态值四、EF添加数据4.1 EF Add方式4.2 EF 通过改变对象的状态为 Added4.3 调用方sql4.4 调用存储过程 五、EF修改数据5.1 不查询数据库&…

项目管理系统在制造业的应用,提高生产效率的秘诀与解决方案

缩短产品交货周期&#xff0c;提高产品交付率是当下很多制造业面临的难题&#xff0c;项目管理系统业务流程自动化&#xff0c;能够显著改善项目效率。接下来我们说一说项目管理系统在制造业的应用&#xff0c;项目管理系统制造业解决方案。 制造业典型的项目背景 随着企业体量…

学习【Redis原理篇】这一篇就够了

目录 1. 数据结构1-1. 动态字符串&#xff08;SDS&#xff09;1-2. intset1-3. Dict 2. 网络模型3. 通信协议4. 内存策略 1. 数据结构 1-1. 动态字符串&#xff08;SDS&#xff09; 我们都知道Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字…

【Java项目】基于SpringBoot的【就业信息管理系统】

在当今这个科技迅猛发展的时代&#xff0c;计算机技术在生活中扮演着至关重要的角色&#xff0c;特别是在信息管理领域。在这样的背景下&#xff0c;学习计算机知识不只是简单地掌握一项技能&#xff0c;更关键的是将所学知识应用于实际&#xff0c;以创新的思维不断简化人们的…

on-my-zsh 命令自动补全插件 zsh-autosuggestions 安装和配置

首先 Oh My Zsh 是什么? Oh My Zsh 是一款社区驱动的命令行工具&#xff0c;正如它的主页上说的&#xff0c;Oh My Zsh 是一种生活方式。它基于 zsh 命令行&#xff0c;提供了主题配置&#xff0c;插件机制&#xff0c;已经内置的便捷操作。给我们一种全新的方式使用命令行。…

热门IT【视频教程】-华为/思科/红帽/oracle

华为认证 网络工程师-入门基础课&#xff1a;华为HCIA认证课程介绍-CSDN博客 网络工程师进阶课&#xff1a;华为HCIP认证课程介绍-CSDN博客 职场进阶&#xff0c;踏上高峰——HCIE-Datacom认证-CSDN博客 华为HCIA试听课程 &#xff1a; 超级实用&#xff0c;华为VRP系统文件…

8.6 循环神经网络的简洁实现

每个步长共用参数 加载数据 虽然 8.5节 对了解循环神经网络的实现方式具有指导意义&#xff0c;但并不方便。 本节将展示如何使用深度学习框架的高级API提供的函数更有效地实现相同的语言模型。 我们仍然从读取时光机器数据集开始。 import torch from torch import nn from…

#include<初见C语言之指针(5)>

目录 一、sizeof和strlen的对比 1. sizeof 2.strlen 二、数组和指针题解析 1. ⼀维数组 1.1数组名理解 2.字符数组 3. ⼆维数组 三、指针运算题解析 总结 一、sizeof和strlen的对比 1. sizeof 我们前面介绍过sizeof是单目操作符 sizeof括号中有表达式&#xff0c;不…

解决PATH变量污染的问题

文章目录 解决PATH变量污染的问题概述笔记清空PATH变量之后的系统设置在命令行查看清空后的PATH变量以 gitea-1.17.1-gogit-windows-4.0-amd64.exe 为例以系统命令 where为例run_vs2019.bat备注 - 批处理的后缀最好是batEND 解决PATH变量污染的问题 概述 随着不断安装新软件,…

一文彻底搞懂如何创建线程

文章目录 1. java创建线程(Thread)方式2. 继承 Thread 类3. 实现 Runnable 接口4. 实现 Callable 接口5. 使用线程池6. 使用匿名类 1. java创建线程(Thread)方式 方式一&#xff1a;继承于Thread类&#xff0c;这是最常见的创建线程的方式之一&#xff0c;通过继承 Thread 类并…

BeanDefinition

这里写目录标题 BeanDefinitionBeanDefinitionReaderAbstractBeanDefinitionReaderXmlBeanDefinitionReader BeanDefinition 上述Spring的基本运行中&#xff0c;你所有的定义描述信息都在XML文件里面&#xff0c;如何读取呢&#xff1f; 当然通过 new ClassPathXmlApplicatio…

常见微服务的组件?

注册中心&#xff1a;就是一个服务注册的地方&#xff0c;我们可以把拆分的服务注册到注册中心&#xff0c;这样注册中心就能管理这些服务&#xff0c;服务之间的调用就会很方便&#xff0c;通过服务名就能相互调用。 负载均衡&#xff1a;被调用放的负载均衡&#xff0c;比如…

【智能算法】黄金正弦算法(GSA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2017年&#xff0c;Tanyildizi等人受到正弦函数单位圆内扫描启发&#xff0c;提出了黄金正弦算法&#xff08;Golden Sine Algorithm, GSA&#xff09;。 2.算法原理 2.1算法思想 GSA来源于正弦函…

FreeBSD下路由问题留档

碰到了一个非常神奇的事情&#xff0c;一台笔记本有以太网和wifi&#xff0c;都可以连到同一台路由器&#xff0c;ip地址配置在同一网段&#xff0c;但是如果插上网线&#xff0c;再拔掉网线的话&#xff0c;那么wifi即使连上&#xff0c;也无法上网。 看路由信息&#xff0c;发…

【Spring源码分析】透过源码看透Spring事务

阅读此需阅读下面这些博客先【Spring源码分析】Bean的元数据和一些Spring的工具【Spring源码分析】BeanFactory系列接口解读【Spring源码分析】执行流程之非懒加载单例Bean的实例化逻辑【Spring源码分析】从源码角度去熟悉依赖注入&#xff08;一&#xff09;【Spring源码分析】…

书生浦语笔记一

2023年6月&#xff0c;InternLM的第一代大模型正式发布。仅一个月后&#xff0c;该模型以及其全套工具链被开源。随后&#xff0c;在8月份&#xff0c;多模态语料库chat7B和lagent也被开源。而在接下来的9月份&#xff0c;InternLM20B的开源发布进一步加强了全线工具链的更新。…