ESP32学习---ESP-NOW

news2024/11/24 22:24:43

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

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

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

后续内容待添加。。。

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

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

相关文章

05 | Swoole 源码分析之 WebSocket 模块

首发原文链接&#xff1a;Swoole 源码分析之 WebSocket 模块 大家好&#xff0c;我是码农先森。 引言 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它允许客户端和服务器之间进行实时数据传输。 与传统的 HTTP 请求-响应模型不同&#xff0c;WebSocket 可以保持…

免费开源的 AI 绘图工具 ImgPilot

免费开源的 AI 绘图工具 ImgPilot 分类 开源分享 项目名: ImgPilot -- 通过提示词及涂鸦生成图片 Github 开源地址&#xff1a; GitHub - leptonai/imgpilot: Turn the draft into amazing artwork with the power of Real-Time Latent Consistency Model 在线地址&#xff…

Gparted工具 初始化磁盘

Gparted工具 初始化磁盘 1、安装 没有此工具请先安装&#xff1a; yum install epel-release yum install gparted yum install yum-utils git gnome-common gcc-c yum-builddep gparted 2、打开Gparted工具&#xff0c;初始化磁盘 使用具有root权限的普通用户打开gparted&…

day_2FreeRTOS使用PWM+ADC光敏电阻完成光控灯实验

主要代码&#xff1a; int adc_val0;//保存ADC采集到的数值 float volt0;//保存电压值HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);//打开定时器的PWM通道3 TIM3->CCR30;//改变CCR的值&#xff0c;范围0——999&#xff0c;不能超过ARRwhile (1){ HAL_ADC_Start(&had…

Redis中的复制功能(一)

复制 概述 在Redis中&#xff0c;用户可以通过执行SLAVEOF命令或者设置slaveof选项&#xff0c;让一个服务器去复制(replicate)另一个服务器&#xff0c;我们称呼被复制的服务器为主服务器(master)&#xff0c;而对主服务器进行复制的服务器则被称为从服务器(slave),如图所示…

【御控物联】JavaScript JSON结构转换(8):数组To数组——多层属性重组

文章目录 一、JSON结构转换是什么&#xff1f;二、案例之《JSON数组 To JSON数组》三、代码实现四、在线转换工具五、技术资料 一、JSON结构转换是什么&#xff1f; JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换&#xff0c;生成新的JS…

win10企业评估版转正式版

一、winr 输入 C:\Windows\System32\spp\tokens\skus 二、下载 Windows 10 Enterprise LTSC 2021 的 SKU 蓝奏云地址 https://wwl.lanzoue.com/irkKV1th7s0d 下载好后解压 解压密码&#xff1a;www.cnkker.com 解压好后全部复制到 C:\Windows\System32\spp\tokens\skus 目录…

安装和使用Miniconda来管理Python环境

安装和使用Miniconda来管理Python环境 一、Miniconda简介二、Miniconda的安装 1. 下载2. 安装三、Miniconda的配置四、Miniconda的使用 1. Conda相关2. 环境管理3. 包管理 一、Miniconda简介 Miniconda是一个免费的最小化Python环境管理工具(精简版Anaconda)&#xff0c;只包…

多图详细教你注册Google(Gmail)新账号,常见问题和注意事项

对于做外贸&#xff0c;或者需要和外国客户、朋友沟通的小伙伴来说&#xff0c;一个Google账号&#xff08;也就是Gmail账号&#xff0c;下述统一用Google账号来表述&#xff09;是非常必要的&#xff0c;一方面是通过Gmail邮箱收发邮件、沟通往来&#xff0c;另一个方面是很多…

redis集群配置(精华版):分片集群模式

分片集群模式 概念动手实操1、环境准备2、配置文件配置3、启动所有redis4、创建集群5、测试集群读/写 概念 ​ Redis 分片集群是一种用于横向扩展 Redis 数据库的方法&#xff0c;它将数据分散存储在多个 Redis 节点中&#xff0c;从而提高了系统的吞吐量和容量。在 Redis 分片…

报错:AttributeError: module ‘numpy‘ has no attribute ‘unit8‘解决

错误问题&#xff1a; 解决方法&#xff1a; 哥们姐们仔细一点吧这个unit8是打错了&#xff0c;无非就是uint8写成了unit8 应该是【uint8】&#xff0c;以后敲代码仔细点哦

Google Chrome将某个页签静音,不是网站

Google Chrome将某个页签静音&#xff0c;不是网站 打开chrome://flags/在里面搜索&#xff0c;audio&#xff0c;找到Tab audio muting UI contorl的选项&#xff0c;右侧设置为Enable。重新启动浏览器。 发现有声音的浏览器页签有一个喇叭图标&#xff0c;点击一下就行了。

It takes two (搜索)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例&#xff1a; 输入 3 4 AAAO AAAA AAAA 输出 NO 思路&#xff1a; 根据题目意思&#xff0c;如果存在的 A 联通不可以成为 矩形&#xff0c;输出 NO&#xff0c;否则输出 YES 这道题看数据范…

java线程(一)--进程,多线程,synchronized和lock锁,JUC,JUnit

Java线程入门 单核CPU和多核CPU的理解 单核CPU&#xff0c;其实是一种假的多线程&#xff0c;因为在一个时间单元内&#xff0c;也只能执行一个线程的任务。例如&#xff1a;虽然有多车道&#xff0c;但是收费站只有一个工作人员在收费&#xff0c;只有收了费才能通过&#xf…

hive之full outer join(全连接)使用

文章目录 前言语法 :总结 前言 full outer join结合了 LEFT JOIN 和 RIGHT JOIN 的结果&#xff0c;并使用NULL值作为两侧缺失匹配结果。 语法 : SELECT table1.column_name(s),table2.column_name(s) FROM table1 FULL OUTER JOIN table2 ON table1.column_name table2.c…

【YOLO 系列】基于YOLO V8的高速公路摄像头车辆检测识别系统【python源码+Pyqt5界面+数据集+训练代码】

摘要&#xff1a; 基于YOLO V8的高精度高速公路摄像头车辆检测识别系统可用于公路上车辆的识别检测与定位&#xff0c;利用YOLO V8算法可实现图片、视频、摄像头等方式对不同车辆进行目标检测识别&#xff0c;另外支持结果可视化与检测结果的导出。本系统采用YOLO V8目标检测模…

BM25 二叉树的后序遍历(postOrder()返回值用void)

import java.util.*;/** public class TreeNode {* int val 0;* TreeNode left null;* TreeNode right null;* public TreeNode(int val) {* this.val val;* }* }*/public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&a…

软件测试-进阶篇

目录 测试的分类1 按测试对象划分1.1 界面测试1.2 可靠性测试1.3 容错性测试1.4 文档测试1.5 兼容性测试1.6 易用性测试1.7 安装卸载测试1.8 安装测试1.9 性能测试1.10 内存泄漏测试 2 按是否查看代码划分2.1 黑盒测试&#xff08;Black-box Testing&#xff09;2.2 白盒测试&a…

使用ssh免密登录服务器

最近写一些shell脚本的时候&#xff0c;需要读取远程服务器的目录下的内容&#xff0c;不能在脚本中直接使用密码&#xff0c;所以就想到了使用免密的方式进行读取。 一、虚拟机环境 下面是我安装的虚拟机网络配置 虚拟机编号 IP地址 子网掩码 账号 100 192.168.164.100…

每天学点儿Python(3) -- for循环

for循环结构格式如下 for 循环变量 in 遍历对象:语句块 举例一、 for i in "Hello"print(i) 执行结果如下 举例二、 #打印100-999之间的水仙花数 #注意&#xff1a;Python中 / 除法&#xff0c;运输后为浮点数, // 为取除法后的整数&#xff0c;而不是C/C中的注释…