ESP-MESH 无线组网,多设备通信更方便 | ESP32轻松学-Arduino中文社区 - Powered by Discuz! Archiver

铁熊 发表于 2021-2-6 21:07

ESP-MESH 无线组网,多设备通信更方便 | ESP32轻松学

本帖最后由 铁熊 于 2021-2-7 20:26 编辑

!(https://img-blog.csdnimg.cn/img_convert/926ec794541c82227b984afe3e668f73.png)

**ESP32 轻松学**系列文章目录:

- (https://mp.weixin.qq.com/s/Pzqit-YQNWZTvkZlakv4jQ)
- (https://mp.weixin.qq.com/s/hrK4_UNCquCtGRzvaLZPhQ)
- [蓝牙翻页笔(PPT 控制器)](https://mp.weixin.qq.com/s/W1uPAZIAVKVVjMSWPSEC4g)
- [新冠肺炎疫情数据实时显示器](https://mp.weixin.qq.com/s/kGYfIdb5-Er1I5P5tMYwpw)
- (https://mp.weixin.qq.com/s/RMPYPJJm5cRrnkXm7bzmxA)
- (https://mp.weixin.qq.com/s/iNKaNmAN3nTWsaPIg0lEXA)
- (https://mp.weixin.qq.com/s/w1-XIcgaddqlcSpnqZPD8A)
- [彩屏显示入门(一):驱动库设置与彩屏效果展示](https://mp.weixin.qq.com/s/R_KXVcjKTRa8rfP4S7rHMA)
- [彩屏显示入门(二):颜色设置与文本显示](https://mp.weixin.qq.com/s/kEjLZw_RoBHGKz1mGBSLyg)
- (https://mp.weixin.qq.com/s/uRhJTMLY1xb96B6ockNf3A)

![封面](https://img-blog.csdnimg.cn/img_convert/50da9ca6dceb04d0ccdb3c4b4d628c9a.png)

想象一下,在一些智能家居场合或者工业控制场合,如果需要在很多设备之间满足两两可以互联互通的通信要求,你有什么比较好的通信方案么?当然我们可以用线缆将它们连接在一起,但是这样做不仅距离限制比较多,而且可能需要重新施工布线,成本比较大!

今天的课程,我们将向您介绍 ESP-MESH 组网技术,不仅可以大大降低距离的限制,而且可以方便地实现多设备之间互联互通的要求,在实现难度上也会大大降低。

# ESP-MESH 简介

[^1]: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-guides/mesh.html

ESP-MESH 是一套建立在 Wi-Fi 协议之上的网络协议。ESP-MESH 允许分布在大范围区域内(室内和室外)的大量设备(下文称节点)在同一个 WLAN(无线局域网)中相互连接。ESP-MESH 具有自组网和自修复的特性,也就是说 mesh 网络可以自主地构建和维护。

传统基础设施 Wi-Fi 网络是一个“单点对多点”的网络。这种网络架构的中心节点为接入点 (AP),其他节点 (Station) 均与 AP 直接相连。其中,AP 负责各个 Station 之间的仲裁和转发,一些 AP 还会通过路由器与外部 IP 网络交换数据。在传统 Wi-Fi 网络架构中,1)由于所有 Station 均需与 AP 直接相连,不能距离 AP 太远,因此覆盖区域相对有限;2)受到 AP 容量的限制,因此网络中允许的 station 数量相对有限,很容易超载。

![传统WiFi网络架构](https://img-blog.csdnimg.cn/img_convert/a7d8b5894678c8c17be6de71f57ee941.png)

ESP-MESH 与传统 Wi-Fi 网络的不同之处在于:网络中的节点不需要连接到中心节点,而是可以与相邻节点连接。各节点均负责相连节点的数据中继。由于无需受限于距离中心节点的位置,所有节点仍可互连,因此 ESP-MESH 网络的覆盖区域更广。类似地,由于不再受限于中心节点的容量限制,ESP-MESH 允许更多节点接入,也不易于超载。

!(https://img-blog.csdnimg.cn/img_convert/dc6957734b702312b59b4cda1e1675ba.png)

# 准备工作

我们使用 Arduino 软件来编写本项目的程序,使用 ESP32 与 ESP8266 开发板。至于如何在 Arduino 中配置 ESP32 与 ESP8266 的开发环境,请查看 (https://mp.weixin.qq.com/s/Pzqit-YQNWZTvkZlakv4jQ)章节。

要完成本课程的项目,需准备如下材料:

- ESP32 主控板 × 2;
- ESP8266 主控板 × 1;
- LED 灯 × 1;
- 人体红外传感器 × 1;
- DHT11 温湿度传感器 × 1;

![材料清单](https://img-blog.csdnimg.cn/img_convert/2f215fe812aa3cb0a096e5584c73eaee.png)

# 安装 painlessMesh 库

要在 Arduino IDE 中使用 ESP-MESH,我们需要先安装一个库来支持 ESP-MESH 组网程序的编写,这个库的名称就是:`painlessMesh`。根据这个库的官方介绍,这个库可以简化 ESP-MESH 的程序编写,让你更加专注于功能的实现,而不必关心 ESP-MESH 网络架设与管理的细节。

> painlessMesh is a library that takes care of the particulars of creating a simple mesh network using esp8266 and esp32 hardware.The goal is to allow the programmer to work with a mesh network without having to worry about how the network is structured or managed.

安装这个库也很简单,只需要打开 Arduino 软件的库管理器,搜索 `painlessMesh`,即可选择对应的版本进行安装。这里推荐安装最新版。

![安装painlessMesh库](https://img-blog.csdnimg.cn/img_convert/d186113a12535b9af5e851da7befaf2b.png)

在安装 painlessMesh 库的时候,如果提示你需要安装其他依赖库,选择同意安装全部库就行。

安装完库,就可以开始 ESP-MESH 组网编程了。

# ESP-MESH 基础讲解

先打开 painlessMesh 库的基础示例:`basic.ino` 程序,来了解一下这个库的基本使用方法。basic 示例程序路径如下:**Arduino IDE → 文件 → Painless Mesh → basic**,如下图所示。

!(https://img-blog.csdnimg.cn/img_convert/eb870df0c312d425136861b1361d0fe8.png)

basic.ino 程序如下:

```cpp
#include "painlessMesh.h"

#define   MESH_PREFIX   "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

painlessMeshmesh;

Scheduler userScheduler;

void sendMessage();

Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

void sendMessage() {
String msg = "Hello from node ";
msg += mesh.getNodeId();
mesh.sendBroadcast( msg );
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}

void receivedCallback( uint32_t from, String &msg ) {
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
Serial.begin(115200);

mesh.setDebugMsgTypes( ERROR | STARTUP );

mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

userScheduler.addTask( taskSendMessage );
taskSendMessage.enable();
}

void loop() {
mesh.update();
}
```

程序只有短短 50 几行,可见要实现 ESP-MESH 组网非常简单。接下来我们对这个程序进行讲解。

## MESH 鉴权信息

首先在程序的开头,我们引入了 painlessMesh 这个库,以便后续程序可以使用这个库的相关功能:

```cpp
#include "painlessMesh.h"
```

接着对 ESP-MESH 组网的一些鉴权信息进行设定,以便具有相同鉴权信息的设备,可以互相组网。其中,`MESH_PREFIX` 可以理解为 ESP-MESH 网络的账号,`MESH_PASSWORD` 则是 ESP-MESH 网络的密码,`MESH_PORT` 为ESP-MESH 网络的端口号。这 3 个信息可以根据你的需要随便修改,只要互相 MESH 组网的设备之间这 3 个信息相同即可。设置完这些信息之后,就可以实例化一个 `mesh` 对象,用来处理后续的各种信息收发。

```cpp
#define   MESH_PREFIX   "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

painlessMeshmesh;
```

## MESH 设备发送信息

然后实例化一个 Scheduler 任务管理器 `userScheduler`,帮助ESP- MESH 网络设备调度他们的任务,比如说定时更新传感器相关的信息,然后将数据发送给其他设备等。Scheduler 任务管理也是 painlessMesh 库推荐使用的方式,**因为在 ESP-MESH 网络中要尽量避免延时 delay() 相关的代码**。实例化 userScheduler 之后,再来创建一个任务 `taskSendMessage`,并且调用 `sendMessage()` 函数,这个函数就是负责向其他设备发送信息的。

```cpp
Scheduler userScheduler;

void sendMessage();

Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );
```

sendMessage() 函数的具体内容如下,在这个函数中,实现了向其他节点设备发送了一句打招呼的语句。如果我们要向其他设备发送传感器信息或者数据,只要去修改这个函数中的内容即可。

```cpp
void sendMessage() {
String msg = "Hello from node ";
msg += mesh.getNodeId();
mesh.sendBroadcast( msg );
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}
```

## MESH 网络回调函数

接下来是几个 painlessMesh 库必备的回调函数实现。

- `receivedCallback()` 函数:负责将从其他设备接收到的信息在串口监视器中打印出来;
- `newConnectionCallback()` 函数:负责通知有没有新设备接入到 ESP-MESH 网络中;
- `changedConnectionCallback()` 函数:负责通知 ESP-MESH 网络连接出现变化,比如有设备离线或有新设备加入等;
- `nodeTimeAdjustedCallback()` 函数:负责打印时间同步信息,以确保 ESP-MESH 网络中所有设备的时间是同步的。

```cpp
void receivedCallback( uint32_t from, String &msg ) {
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
```

## setup() 初始化设置

在 setup() 初始化程序中,先初始化串口,方便后面打印信息。

```cpp
Serial.begin(115200);
```

然后是对 ESP-MESH 网络进行相关初始化设置:

- 设置打印的调试信息类型为 ERROR 与 STARTUP 等级;
- 然后根据 MESH_PREFIX、MESH_PASSWORD、userScheduler、MESH_PORT 等信息初始化 MESH 网络;
- 借着分别设置 `onReceive`(接收到消息时)、`onNewConnection`(有新的设备连接时)、`onChangedConnections`(连接的设备发生变化时)、`onNodeTimeAdjusted`(节点设备时间调整并同步时)的回调函数,用来处理 ESP-MESH 网络事件。

```cpp
mesh.setDebugMsgTypes( ERROR | STARTUP );

mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
```

最后在 setup() 中设置定时发送消息任务,以及对任务使能。

```cpp
userScheduler.addTask( taskSendMessage );
taskSendMessage.enable();
```

## loop() 重复运行

loop() 程序非常简单,只要不断去更新 ESP-MESH 网络即可:

```cpp
mesh.update();
```

## 程序测试

分别将程序上传到 2 块以上的 ESP32 开发板中(或者 ESP8266 开发板也可以),然后分别打开不同的串口监视器,可以看到从其他 ESP-MESH 网络设备上发来的信息,以及一些网络状态变更的信息。

将所有 ESP32(或 ESP8266)设备上电,打开其中一块板子的串口监视器,可以看到从其他不同节点 ID 对应的设备发送过来的消息。

![从不同的设备节点发过来的消息](https://img-blog.csdnimg.cn/img_convert/fa395fd608ae855784e3677b1ec21891.png)

偶尔也会打印出时间同步的消息:

![时间同步消息](https://img-blog.csdnimg.cn/img_convert/579bfffd61bb6ccb7a763f73456ec3be.png)

当拔下其中某一块开发板的电源之后,可以看到连接发生变化的消息被打印出来。

![连接变化信息](https://img-blog.csdnimg.cn/img_convert/fece3bfdae3e42fe47d691d3ee5adf27.png)

当有新的设备上电加入 ESP-MESH 网络时,也可以在串口监视器中看到新设备加入的信息。

![新的节点设备加入](https://img-blog.csdnimg.cn/img_convert/85aaa693a28b6de0af3e6118ed139e7f.png)

这些信息充分说明了 ESP-MESH 具有自组网和自修复的特性,可以自主地构建和维护。

# ESP-MESH 智能家居组网实例

上面初步学习了 ESP-MESH 网络的编程,下面我们修改 basic 程序,接入各种传感器,来实现一个简单的智能家居设备组网实例。

假设房间中有 3 套设备:**人体感应器(设备 1 或节点 1)**、**智能灯(设备 2 或 节点 2)**、**温湿度传感器(设备 3 或节点 3)**,这 3 套设备安装在房间的不同位置,考虑到设备可以随时移动,所以采用 ESP-MESH 组网的方式,对这 3 套设备进行互联互通,设备之间可以互相首发信息,可以根据读取到的信息,进行相应的控制,比如开关灯等。这 3 套设备的主控芯片为 ESP32 或者 ESP8266。

![房间平面示意图](https://img-blog.csdnimg.cn/img_convert/93feed352752819d8d61d8b898b69102.png)

## 电路图

这 3 套设备的电路图分别如下:

**人体感应器(设备 1 或节点 1)**:ESP8266 主控板 GPIO16 引脚连接人体红外感应传感器,用于检测房间内是否有人:

![设备1电路图](https://img-blog.csdnimg.cn/img_convert/6fb4c463c9984902b95cc68a6f8703e8.png)

**智能灯(设备 2 或 节点 2)**:ESP32 主控板 GPIO12 引脚连接 LED 灯,用于控制房间内的智能灯:

![设备2电路图](https://img-blog.csdnimg.cn/img_convert/19fc9a7bffa93e8c99a4dd3236b0f352.png)

**温湿度传感器(设备 3 或节点 3)**:ESP32 主控板 GPIO33 引脚连接 DHT11 温湿度传感器,用于读取房间内的温湿度信息。

![设备3电路图](https://img-blog.csdnimg.cn/img_convert/f160f3822a983c45585e9e62cfa8a324.png)

## 设备 1 程序修改(人体感应)

在前面讲解的 basic 程序的基础之上,我们只需要修改 `sendMessage()` 函数即可:通过人体红外感应传感器检测房间内是否有人,如果有人的话,发送“`Light on`”信息,没人的话,发送“`Light off`”信息给其他设备。

```cpp
#define PIR_PIN 16

void sendMessage() {
pinMode(PIR_PIN, INPUT);
String msg = "Message from node PIR: ";
if ( digitalRead(PIR_PIN) ) {
    msg += "Light on.";
} else {
    msg += "Light off.";
}
mesh.sendBroadcast( msg );
}
```

其余代码不再赘述,完整代码请参考附件 **esp_mesh_pir** 程序。

## 设备 2 程序修改(LED 灯控制)

在前面讲解的 basic 程序的基础之上,我们只需要修改 `receivedCallback()` 函数即可:如果从其他设备中接收到的信息包含“`Light on`”命令,就打开 LED 灯,否则熄灭 LED 灯。

```cpp
#define LED_PIN 12

void receivedCallback( uint32_t from, String &msg ) {
pinMode(LED_PIN, OUTPUT);
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
if (msg.indexOf("Light on") > 0) {
    digitalWrite(LED_PIN, HIGH);
} else {
    digitalWrite(LED_PIN, LOW);
}
}
```

其余代码不再赘述,完整代码请参考附件 **esp_mesh_led** 程序。

## 设备 3 程序修改(温湿度读取)

读取 DHT11 温湿度传感器要做一些额外的设置,首先在程序开头引入 DHT 库文件:

```cpp
#include <DHT.h>
```

然后对 DHT 温湿度传感器的引脚、类型进行设置:

```
#define DHTPIN   33
#define DHTTYPE    DHT11

DHT dht(DHTPIN, DHTTYPE);
```

在 setup() 中开启传感器:

```
dht.begin();
```

最后在前面讲解的 basic 程序的基础之上,需要修改 `sendMessage()` 函数:定时地将温度和湿度的数据发送给其他设备。

```cpp
void sendMessage() {
String msg = "Message from node DHT.";
msg = msg + " Temperature: " + String(dht.readTemperature());
msg = msg + " Humidity: " + String(dht.readHumidity());
mesh.sendBroadcast( msg );
}
```

其余代码不再赘述,完整代码请参考附件 **esp_mesh_dht** 程序。

## 效果展示

将上述代码分别上传到指定的设备节点中。打开其中一个设备 1(节点 1:人体感应)的串口监视器,可以看到另外两个设备发送过来的信息,其中就包含了温度和湿度的值。

![读取温湿度](https://img-blog.csdnimg.cn/img_convert/9b7e4885a13643489743e3ebe039a960.png)

再打开设备 3(节点 3:温湿度读取)的串口监视器,同样可以看到另外两个设备发送过来的信息,其中就包括是否开关灯的命令。

![检测是否有人](https://img-blog.csdnimg.cn/img_convert/2f64328a2eba0db567469bfdfb6aa15a.png)

最后再查看一下设备 2(节点 2:LED 灯控制)的反馈情况:当设备 1 检测到附近有人活动时,设备 2 上的 LED 灯就会被点亮,否则熄灭。

!(https://img-blog.csdnimg.cn/img_convert/1cb39ef4c9be411068b2ed07fb645182.png)

所有设备按照我们预期的情况运行,智能家居组网实验成功!

# 总结

ESP-MESH 是一种非常好用的组网方式,摆脱了 WiFi 网络距离的限制,同时组网又非常方便,可以应用在很多智能家居或者工程项目中,方便地进行数据交互与传送。由于本项目只是一个简单的演示,所以并没有对发送的信息或者数据进行包装,后续可以改进数据发送的格式,比如采用 JSON 格式,就可以发送与解码更加丰富的信息了。

以上就是本教程的全部内容,我是铁熊,下期再见!

# 代码下载

关注公众号“**铁熊玩创客**”,后台回复“**ESP-MESH**”,就可以获取本次课程的全部源码下载链接。

![微信二维码引导2](https://img-blog.csdnimg.cn/img_convert/049a9343ab7e671c1043eaff4defe317.png)

ws9528 发表于 2022-3-19 22:08

Highnose 发表于 2022-2-7 20:27
楼主这个贴非常好,很清晰

范例里这个象是广播的方式传的数据,现在,我想直接往图中的节点指名传数据不知 ...

你加一个识别码不就行了,A向Y 发送信息   这是给Y返送的信息         其它接收到判断是不是给自己发的,不是忽略,Y发现这是给自己发送的,直接响应不就完事了

wison 发表于 2021-3-11 14:07

大神,可否出esp32的ble mesh,如果用wifi的话太耗电.

思路大概是一个esp32做网关开启wifi和ble,其他节点只开ble传输设备数据

7024141 发表于 2021-11-13 22:10

请问大佬在开发过程中有遇到这个错误吗?
F:\ArduinoProject\libraries\Painless_Mesh\src/arduino/wifi.hpp: In member function 'void painlessmesh::wifi::Mesh::eventHandleInit()':
F:\ArduinoProject\libraries\Painless_Mesh\src/arduino/wifi.hpp:251:22: error: 'SYSTEM_EVENT_SCAN_DONE' is not a member of 'arduino_event_id_t'
         WiFiEvent_t::SYSTEM_EVENT_SCAN_DONE);
                      ^~~~~~~~~~~~~~~~~~~~~~
F:\ArduinoProject\libraries\Painless_Mesh\src/arduino/wifi.hpp:260:22: error: 'SYSTEM_EVENT_STA_START' is not a member of 'arduino_event_id_t'
         WiFiEvent_t::SYSTEM_EVENT_STA_START);
                      ^~~~~~~~~~~~~~~~~~~~~~
F:\ArduinoProject\libraries\Painless_Mesh\src/arduino/wifi.hpp:271:22: error: 'SYSTEM_EVENT_STA_DISCONNECTED' is not a member of 'arduino_event_id_t'
         WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
F:\ArduinoProject\libraries\Painless_Mesh\src/arduino/wifi.hpp:281:22: error: 'SYSTEM_EVENT_STA_GOT_IP' is not a member of 'arduino_event_id_t'
         WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
                      ^~~~~~~~~~~~~~~~~~~~~~~
我查到网上说是这个问题
A datatype as arduino_event_id_t is not defined in the stable release of the arduino-esp32 core v1.0.4. Also, It was defined for the first time to support esp-idf 4.2.
The ESP32-S2 SoC, unlike the ESP32, is supported since esp-idf v4.2.
不是特别懂

hundajdx 发表于 2022-1-13 15:52

非常好的帖子,赞!

ws9528 发表于 2022-2-7 19:40

明天有时间试一下

Highnose 发表于 2022-2-7 20:27

楼主这个贴非常好,很清晰

范例里这个象是广播的方式传的数据,现在,我想直接往图中的节点指名传数据不知可否?( 当然中间可能被中转了几次)

ws9528 发表于 2022-2-7 21:29

提示AsyncTCP.h: No such file or directory的解决办法

这是缺少这个库文件,在下面地址下载即可

https://github.com/me-no-dev/AsyncTCP

mengxgh 发表于 2022-3-1 17:57

7024141 发表于 2021-11-13 22:10
请问大佬在开发过程中有遇到这个错误吗?
F:\ArduinoProject\libraries\Painless_Mesh\src/arduino/wifi.hp ...

没有安装对应的库文件吗?

ws9528 发表于 2022-3-19 22:11

示例中 还是会缺少#include <ESPAsyncTCP.h>
库,暂时还没找到。
页: [1]
查看完整版本: ESP-MESH 无线组网,多设备通信更方便 | ESP32轻松学