ESP-MESH 网络协议 – ESP32 和 ESP8266使用(painlessMesh 库)

释放双眼,带上耳机,听听看~!

了解如何使用 ESP-MESH 网络协议,通过 ESP32 和 ESP8266 NodeMCU 开发板构建 Mesh 网络。ESP-MESH 允许多个设备(节点)在单个无线局域网下相互通信。ESP32 和 ESP8266 开发板均支持此功能。在本教程中,我们将向您展示如何使用 Arduino 内核开始使用 ESP-MESH。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

本文涵盖以下主题:

  •  ESP-MESH 简介
  • ESP-MESH Basic 示例(广播消息)
  • 使用 ESP-MESH 交换传感器读数(广播)

 Arduino集成开发环境

如果您想使用 Arduino IDE 对 ESP32 和 ESP8266 开发板进行编程,您IDE应该安装 ESP32 或 ESP8266 插件。

 ESP-MESH 简介

根据乐鑫文档:

“ESP-MESH 是一种建立在 Wi-Fi 协议之上的网络协议。ESP-MESH 允许分布在大型物理区域(室内和室外)的众多设备(称为节点)在单个 WLAN(无线局域网)下互连。

ESP-MESH 具有自组织和自修复功能,这意味着网络可以自主构建和维护。更多信息,请访问 ESP-MESH 官方文档

传统Wi-Fi网络架构

在传统的 Wi-Fi 网络架构中,单个节点(接入点 – 通常是路由器)连接到所有其他节点(站点)。每个节点都可以使用接入点相互通信。但是,这仅限于接入点 Wi-Fi 覆盖范围。每个站点都必须在范围内才能直接连接到接入点。ESP-MESH 不会发生这种情况。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

ESP-MESH 网络架构

使用 ESP-MESH,节点无需连接到中心节点。节点负责中继彼此的传输。这允许多个设备分布在较大的物理区域。节点可以自组织并动态地相互通信,以确保数据包到达其最终节点目的地。如果从网络中删除任何节点,它能够自组织以确保数据包到达目的地。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

 painlessMesh 库

painlessMesh 库允许我们以简单的方式使用 ESP8266 或/和 ESP32 开发板创建网状网络。

“painlessMesh是一个真正的自组织网络,这意味着不需要规划、中央控制器或路由器。任何包含 1 个或多个节点的系统都将自组织成功能齐全的网格。网格的最大大小(我们认为)受到堆中可分配给子连接缓冲区的内存量的限制,因此应该非常高。有关 painlessMesh 库的更多信息。

安装 painlessMesh 库

此库需要一些其他库依赖项。应该会弹出一个新窗口,要求您安装任何缺少的依赖项。选择“全部安装”。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

如果未显示此窗口,则需要安装以下库依赖项:

如果您使用的是 PlatformIO,请将以下行添加到 platformio.ini 文件以添加库并更改监视器速度。

 对于 ESP32:

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    arduinoUnity
    TaskScheduler
    AsyncTCP

 对于ESP8266:

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    TaskScheduler
    ESPAsyncTCP

ESP-MESH Basic 示例(广播消息)

要开始使用 ESP-MESH,我们首先会尝试使用该库的基本示例。此示例创建一个网状网络,其中所有板将消息广播到所有其他板。

我们用四块开发板(两块 ESP32 和两块 ESP8266)对这个示例进行了实验。您可以添加或删除看板。该代码与 ESP32 和 ESP8266 开发板兼容。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

代码 – painlessMesh 库基本示例

将以下代码复制到 Arduino IDE(库示例中的代码)。该代码与 ESP32 和 ESP8266 开发板兼容。

/*
  
  更多Arduino/ESP8266/ESP32等教程请访问: https://www.qutaojiao.com
  
  This is a simple example that uses the painlessMesh library: https://github.com/gmag11/painlessMesh/blob/master/examples/basic/basic.ino
*/

#include "painlessMesh.h"

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

Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain

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

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

// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("startHere: Received from %u msg=%sn", from, msg.c_str());
}

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

void changedConnectionCallback() {
  Serial.printf("Changed connectionsn");
}

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

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

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  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() {
  // it will run the user scheduler as well
  mesh.update();
}

 

在上传代码之前,您可以设置MESH_PREFIX(就像 MESH 网络的名称)和MESH_PASSWORD变量(您可以将其设置为您喜欢的任何内容)。

然后,我们建议您更改每个板的以下行,以便轻松识别发送消息的节点。例如,对于节点 1,更改消息,如下所示:

String msg = "Hi from node 1 ";

代码的工作原理

首先包括 painlessMesh 库。

#include "painlessMesh.h"

 网格详细信息

然后,添加网格详细信息。MESH_PREFIX是指网格的名称。您可以将其更改为您喜欢的任何内容。

#define MESH_PREFIX "whateverYouLike"

顾名思义,MESH_PASSWORD是网状密码。您可以将其更改为您喜欢的任何内容。

#define MESH_PASSWORD "somethingSneaky"

网格中的所有节点都应使用相同的MESH_PREFIX和MESH_PASSWORD。

MESH_PORT是指您希望运行网状服务器的 TCP 端口。默认值为 5555。

#define MESH_PORT 5555

 调度

建议避免在网状网络代码中使用 delay()。为了维护网格,需要在后台执行一些任务。使用 delay() 将阻止这些任务的发生,并可能导致网格失去稳定性/分崩离析。

相反,建议使用 TaskScheduler 来运行在 painlessMesh 本身中使用的任务。

以下行创建一个名为 userScheduler 的新调度程序。

Scheduler userScheduler; // to control your personal task

painlessMesh

创建一个名为 mesh 的 painlessMesh 对象来处理网格网络。

 taskSendMessage

创建一个名为 taskSendMessage 的任务,只要程序正在运行,它就负责每秒调用一次 sendMessage() 函数。

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

sendMessage

sendMessage() 函数将消息发送到消息网络(广播)中的所有节点。

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

该消息包含“Hi from node 1”文本,后跟电路板芯片 ID。

String msg = "Hi from node 1";
msg += mesh.getNodeId();

要广播消息,只需在网格对象上使用 sendBroadcast() 方法,并将要发送的消息 (msg) 作为参数传递。

mesh.sendBroadcast(msg);

每次发送新消息时,代码都会更改消息之间的间隔(一到五秒)。

taskSendMessage.setInterval(random(TASK_SECOND * 1, TASK_SECOND * 5));

receivedCallback

接下来,创建几个回调函数,当网格上发生特定事件时,将调用这些函数。

receivedCallback() 函数打印消息发件人 (from) 和消息内容 (msg.c_str())。

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

每当新节点加入网络时,newConnectionCallback() 函数就会运行。此函数仅打印新节点的芯片 ID。您可以修改该函数以执行任何其他任务。

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

每当网络上的连接发生变化时(当节点加入或离开网络时),changedConnectionCallback() 函数就会运行。

void changedConnectionCallback() {
  Serial.printf("Changed connectionsn");
}

nodeTimeAdjustedCallback() 函数在网络调整时间时运行,以便所有节点同步。它打印偏移量。

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

 setup()

在 setup() 中,初始化串行监视器。

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

选择所需的调试消息类型:

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on

mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

使用前面定义的详细信息初始化网格。

mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);

将所有回调函数分配给其对应的事件。

mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

最后,将 taskSendMessage 函数添加到 userScheduler。调度程序负责在正确的时间处理和运行任务。

userScheduler.addTask(taskSendMessage);

最后,启用 taskSendMessage,以便程序开始向网格发送消息。

taskSendMessage.enable();

要保持网格运行,请将 mesh.update() 添加到 loop() 中。

void loop() {
  // it will run the user scheduler as well
  mesh.update();
}

 示范

将提供的代码上传到所有看板。不要忘记修改消息以轻松识别发送方节点

将主板连接到计算机后,打开与每个主板的串行连接。您可以使用串行监视器,也可以使用PuTTY等软件,并为所有板打开多个窗口。

您应该看到所有看板都收到彼此的消息。例如,这些是节点 1 接收的消息。它接收来自节点 2、3 和 4 的消息。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

当网格发生变化时,您还应该看到其他消息:当板离开或加入网络时。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

使用 ESP-MESH 交换传感器读数

在下一个示例中,我们将在 4 块板之间交换传感器读数(您可以使用不同数量的板)。每个板子都会收到其他板子的读数。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

例如,我们将交换来自 BME280 传感器的传感器读数,但您可以使用任何其他传感器。

 所需零件

以下是此示例所需的部件:

  • 4 个 ESP 板(ESP32 或 ESP8266)
  •  4 个 BME280
  •  面包板
  •  连接线

 Arduino_JSON库

在此示例中,我们将以 JSON 格式交换传感器读数。

如果将 VS Code 与 PlatformIO 配合使用,请在 platformio.ini 文件中包含库,如下所示:

ESP32

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    arduinoUnity
    AsyncTCP
    TaskScheduler
    adafruit/Adafruit Unified Sensor @ ^1.1.4
    adafruit/Adafruit BME280 Library @ ^2.1.2
    arduino-libraries/Arduino_JSON @ ^0.1.0

ESP8266

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    TaskScheduler
    ESPAsyncTCP
    adafruit/Adafruit Unified Sensor @ ^1.1.4
    adafruit/Adafruit BME280 Library @ ^2.1.2
    arduino-libraries/Arduino_JSON @ ^0.1.0

 电路图

将 BME280 传感器连接到 ESP32 或ESP8266默认的 I2C 引脚,如下图所示。

ESP32

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

推荐阅读:ESP32 with BME280 Sensor using Arduino IDE(压力、温度、湿度)

ESP8266 NodeMCU

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

推荐阅读:使用 Arduino IDE ESP8266 BME280(压力、温度、湿度)

代码 – ESP-MESH 广播传感器读数

将以下代码上传到每个开发板。此代码读取当前温度、湿度和压力读数并将其广播到网状网络上的所有板。读数以 JSON 字符串的形式发送,该字符串还包含用于标识发送方板的节点号。

/*
  
  更多Arduino/ESP8266/ESP32等教程请访问: https://www.qutaojiao.com
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "painlessMesh.h"
#include <Arduino_JSON.h>

// MESH Details
#define   MESH_PREFIX     "RNTMESH" //name for your MESH
#define   MESH_PASSWORD   "MESHpassword" //password for your MESH
#define   MESH_PORT       5555 //default port

//BME object on the default I2C pins
Adafruit_BME280 bme;

//Number for this node
int nodeNumber = 2;

//String to send to other nodes with sensor readings
String readings;

Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String getReadings(); // Prototype for sending sensor readings

//Create tasks: to send messages and get readings;
Task taskSendMessage(TASK_SECOND * 5 , TASK_FOREVER, &sendMessage);

String getReadings () {
  JSONVar jsonReadings;
  jsonReadings["node"] = nodeNumber;
  jsonReadings["temp"] = bme.readTemperature();
  jsonReadings["hum"] = bme.readHumidity();
  jsonReadings["pres"] = bme.readPressure()/100.0F;
  readings = JSON.stringify(jsonReadings);
  return readings;
}

void sendMessage () {
  String msg = getReadings();
  mesh.sendBroadcast(msg);
}

//Init BME280
void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }  
}

// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("Received from %u msg=%sn", from, msg.c_str());
  JSONVar myObject = JSON.parse(msg.c_str());
  int node = myObject["node"];
  double temp = myObject["temp"];
  double hum = myObject["hum"];
  double pres = myObject["pres"];
  Serial.print("Node: ");
  Serial.println(node);
  Serial.print("Temperature: ");
  Serial.print(temp);
  Serial.println(" C");
  Serial.print("Humidity: ");
  Serial.print(hum);
  Serial.println(" %");
  Serial.print("Pressure: ");
  Serial.print(pres);
  Serial.println(" hpa");
}

void newConnectionCallback(uint32_t nodeId) {
  Serial.printf("New Connection, nodeId = %un", nodeId);
}

void changedConnectionCallback() {
  Serial.printf("Changed connectionsn");
}

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

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

  //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  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() {
  // it will run the user scheduler as well
  mesh.update();
}

 

该代码与 ESP32 和 ESP8266 开发板兼容。

代码的工作原理

继续阅读本部分,了解代码的工作原理。

 

首先包括所需的库:与 BME280 传感器接口的Adafruit_Sensor和Adafruit_BME280;用于处理网状网络的 painlessMesh 库和用于轻松创建和处理 JSON 字符串的 Arduino_JSON。

#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "painlessMesh.h"
#include <Arduino_JSON.h>

 

Mesh详情

在以下行中插入网格详细信息。

#define MESH_PREFIX    "RNTMESH" //name for your MESH
#define MESH_PASSWORD  "MESHpassword" //password for your MESH
#define MESH_PORT      5555 //default port

MESH_PREFIX是指网格的名称。您可以将其更改为您喜欢的任何内容。顾名思义,MESH_PASSWORD是网状密码。您可以将其更改为您喜欢的任何内容。网格中的所有节点都应使用相同的MESH_PREFIX和MESH_PASSWORD。

MESH_PORT是指您希望运行网状服务器的 TCP 端口。默认值为 5555。

 BME280

在默认的 ESP32 或 ESP8266 引脚上创建一个名为 bme 的Adafruit_BME280对象。

Adafruit_BME280 bme;

在 nodeNumber 变量中,插入主板的节点号。每个板的编号必须不同。

int nodeNumber = 2;

readings 变量将用于保存要发送到其他板的读数。

String readings;

 userScheduler

以下行创建一个名为 userScheduler 的新调度程序。

Scheduler userScheduler; // to control your personal task

painlessMesh

创建一个名为 mesh 的 painlessMesh 对象来处理网格网络。

 创建任务

创建一个名为 taskSendMessage 的任务,只要程序正在运行,该任务就负责每 5 秒调用一次 sendMessage() 函数。

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

 getReadings()

getReadings() 函数从 BME280 传感器获取温度、湿度和压力读数,并连接所有信息,包括名为 jsonReadings 的 JSON 变量上的节点号。

JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temp"] = bme.readTemperature();
jsonReadings["hum"] = bme.readHumidity();
jsonReadings["pres"] = bme.readPressure()/100.0F;

以下行显示了具有任意值的 jsonReadings 变量的结构。

{
  "node":2,
  "temperature":24.51,
  "humidity":52.01,
  "pressure":1005.21
}

然后使用 stringify() 方法将 jsonReadings 变量转换为 JSON 字符串,并保存在 readings 变量中。

readings = JSON.stringify(jsonReadings);

然后,该函数返回此变量。

return readings;

sendMessage

sendMessage() 函数将带有读数和节点号 (getReadings()) 的 JSON 字符串发送到网络中的所有节点(广播)。

void sendMessage () {
  String msg = getReadings();
  mesh.sendBroadcast(msg);
}

initBME

initBME() 函数初始化 BME280 传感器。

void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

receivedCallback

接下来,创建几个回调函数,当网格上发生某些事件时,将调用这些函数。

receivedCallback() 函数打印消息发件人 (from) 和消息内容 (msg.c_str())。

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

该消息采用 JSON 格式,因此,我们可以按如下方式访问变量:

JSONVar myObject = JSON.parse(msg.c_str());
int node = myObject["node"];
double temp = myObject["temp"];
double hum = myObject["hum"];
double pres = myObject["pres"];

最后,在串行监视器上打印所有信息。

Serial.print("Node: ");
Serial.println(node);
Serial.print("Temperature: ");
Serial.print(temp);
Serial.println(" C");
Serial.print("Humidity: ");
Serial.print(hum);
Serial.println(" %");
Serial.print("Pressure: ");
Serial.print(pres);
Serial.println(" hpa");

每当新节点加入网络时,newConnectionCallback() 函数就会运行。此函数仅打印新节点的芯片 ID。您可以修改该函数以执行任何其他任务。

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

每当网络上的连接发生变化时(当节点加入或离开网络时),changedConnectionCallback() 函数就会运行。

void changedConnectionCallback() {
  Serial.printf("Changed connectionsn");
}

nodeTimeAdjustedCallback() 函数在网络调整时间时运行,以便所有节点同步。它打印偏移量。

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

 setup()

在 setup() 中,初始化串行监视器。

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

调用 initBME() 函数以初始化 BME280 传感器。

initBME();

选择所需的调试消息类型:

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on

mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

使用前面定义的详细信息初始化网格。

mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);

将所有回调函数分配给其对应的事件。

mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

最后,将 taskSendMessage 函数添加到 userScheduler。调度程序负责在正确的时间处理和运行任务。

userScheduler.addTask(taskSendMessage);

最后,启用 taskSendMessage,以便程序开始向网格发送消息。

taskSendMessage.enable();

要保持网格运行,请将 mesh.update() 添加到 loop() 中。

void loop() {
  // it will run the user scheduler as well
  mesh.update();
}

 示范

将代码上传到所有开发板(每个开发板具有不同的节点编号)后,您应该看到每个开发板正在接收其他开发板的消息。

以下屏幕截图显示了节点 1 收到的消息。它接收来自节点 2、3 和 4 的传感器读数。

ESP-MESH 网络协议 - ESP32 和 ESP8266使用(painlessMesh 库)

 总结

希望您喜欢这篇 ESP-MESH 网络协议的快速介绍。

给TA打赏
共{{data.count}}人
人已打赏
Nodemcu/ESP8266Nodemcu/ESP8266-进阶动态

WebSocket 服务器:控制输出 - ESP8266 NodeMCU

2023-12-9 21:37:07

Nodemcu/ESP8266Nodemcu/ESP8266-进阶动态

在Arduino IDE中安装ESP8266 NodeMCU LittleFS文件系统上传器

2023-12-11 19:45:57

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索
'); })();