docker入门结合物联(使用)
课件地址:https://docker.easydoc.net。
bilibili地址:Docker 镜像发布和部署_哔哩哔哩_bilibili
课件地址:https://docker.easydoc.net
第一节:Docker 是什么
Docker 是一个应用打包、分发、部署的工具
你也可以把它理解为一个轻量的虚拟机,它只虚拟你软件需要的运行环境,多余的一点都不要,
而普通虚拟机则是一个完整而庞大的系统,包含各种不管你要不要的软件。
跟普通虚拟机的对比

Docker的三步骤
打包、分发、部署
打包:就是把你软件运行所需的依赖、第三方库、软件打包到一起,变成一个安装包
分发:你可以把你打包好的“安装包”上传到一个镜像仓库,其他人可以非常方便的获取和安装
部署:拿着“安装包”就可以一个命令运行起来你的应用,自动模拟出一摸一样的运行环境,不管是在 Windows/Mac/Linux。
重要概念:镜像、容器
镜像:可以理解为软件安装包,可以方便的进行传播和安装。
容器:软件安装后的状态,每个软件运行环境都是独立的、隔离的,称之为容器。
安装
桌面版:https://www.docker.com/products/docker-desktop
服务器版:Install | Docker Docs
Ubuntu下使用docker
我使用的是VM虚拟机,ubuntu版本为20.04。
需要配置一下网络为桥接模式,才能将ip地址映射出来,windos上才可以访问后端映射出来的端口。配置如下:


新创建的ubuntu系统进入root用户需要先设置密码
//第一步
sudo passwd root
//第二步
su
在新主机上首次安装 Docker 之前,您需要 需要设置 Docker 存储库。之后,您可以安装和更新 存储库中的 Docker。
1.设置 Docker 的存储库。apt
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
(补充)
如果在sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc失败
原因curl 命令在尝试连接到 download.docker.com 的 443 端口(HTTPS)时被对方重置连接,这通常与网络限制、防火墙或目标服务器临时故障有关。
输入以下命令
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "nameserver 8.8.4.4" >> /etc/resolv.conf
2.安装 Docker 软件包。
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
3.通过运行映像来验证安装是否成功:hello-world
sudo docker run hello-world
运行成功结果

(在配置中遇到的问题)
nano编辑器快速使用
| 快捷键 | 功能描述 |
|---|---|
Ctrl + O |
保存文件(“WriteOut”) |
Ctrl + X |
退出编辑器 |
Ctrl + R |
读取文件内容到当前编辑 |
Ctrl + G |
显示帮助文档 |
Ctrl + T |
检查拼写(需安装拼写工具) |
Alt + Backspace |
删除光标前的单词 |
Ctrl + ^ |
跳转到指定行号 |
未配置daemon.json文件
打开文件/etc/docker/daemon.json文件。
1.sudo nano /etc/docker/daemon.json```
2. 将其镜像地址进行替换
```bash
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://registry.docker-cn.com",
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com",
"https://hub.uuuadc.top",
"https://docker.anyhub.us.kg",
"https://dockerhub.jobcher.com",
"https://dockerhub.icu",
"https://docker.ckyl.me",
"https://docker.awsl9527.cn",
"https://mirror.baidubce.com"
]
}
3.重启生效该文件
systemctl restart docker
systemctl daemon-reload
maven包地址 :
Apache Archive Distribution Directory
https://archive.apache.org/dist/maven/maven-3/
docket重要命令
1. 创建并运行容器
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
- 常用选项:
-d:后台运行( detached mode )-p HOST:CONTAINER:端口映射-v HOST:CONTAINER:挂载卷-e KEY=VALUE:设置环境变量--name NAME:指定容器名称--rm:退出时自动删除容器
示例:启动一个 Nginx 容器并映射端口 80:
docker run -d -p 80:80 --name my-nginx nginx:alpine
2. 查看容器
docker ps [OPTIONS]
-a:显示所有容器(包括已停止的)-q:只显示容器 ID
3.开启,停止,重新运行容器命令
docker stop CONTAINER_ID/NAME # 停止运行中的容器
docker start CONTAINER_ID/NAME # 启动已停止的容器
docker restart CONTAINER_ID/NAME # 重启容器
1. 查看本地镜像
docker images [OPTIONS]
2. 拉取镜像
docker pull IMAGE[:TAG]
示例:拉取 Redis 6.2 版本:
docker pull redis:6.2
3. 构建镜像
docker build [OPTIONS] PATH
-t NAME:TAG:为镜像命名并添加标签--no-cache:不使用缓存构建
示例:从当前目录的 Dockerfile 构建镜像:
docker build -t my-app:1.0 .
4. 删除镜像
docker rmi IMAGE_ID/NAME:TAG # 删除镜像
docker image prune -a # 删除所有未使用的镜像
3、容器内操作
1. 进入容器
docker exec -it CONTAINER_ID/NAME /bin/bash # 交互式进入容器
2. 查看容器日志
docker logs [OPTIONS] CONTAINER_ID/NAME
-f:跟踪日志输出(类似 tail -f)--tail NUM:显示最后 N 行日志
3. 查看容器资源使用情况
docker stats [CONTAINER_ID/NAME...] # 实时监控
docker inspect CONTAINER_ID/NAME # 获取详细信息
4.数据卷与网络
1. 数据卷管理
docker volume create VOLUME_NAME # 创建卷
docker volume ls # 查看卷
docker volume rm VOLUME_NAME # 删除卷
2. 网络管理
docker network create NETWORK_NAME # 创建网络
docker network ls # 查看网络
docker network connect NETWORK_NAME CONTAINER_ID # 连接容器到网络
5.其他常用命令
1. 登录 / 登出 Docker Hub
docker login # 登录 Docker Hub
docker logout # 登出
2. 推送镜像到仓库
docker push IMAGE:TAG
3. 清理 Docker 系统
docker system prune [OPTIONS] # 清理无用的容器、网络、镜像等
乐联Enjoylot项目部署
有了以上的docker知识,我们就能够来操作容器了,以一个项目进行理解容器,并且简单运行。(只有容器知识,不涉及源码),只讲配置。
文档:2. 演示和快速入门 - 飞书云文档

由于docker镜像问题,有部分镜像拉去不下来,所以导致部分容器虽然运行起来了但是运行的并不完整,导致iot-server跑不起来,查看日志也是一直有错误,解决完一个错误,又出现其他容器的错误。
正常的日志应该为下图所示,没有出现红色的ERROR。
在docker中可以进行容器之间相互通信,所以不需要在虚拟机上做复杂配置,只需要拉去别人的镜像,就能够得到一个带有软件的虚拟机。
查看容器运行命令
## 查看后台日志
```aiignore
docker logs -f iot-server
或
docker compose logs -f server
```
## 其他
1. 单独启动server
docker compose --env-file docker.env up server
3.启动服务
docker compose --env-file docker.env up -d

怎么解决问题的呢,首先需要卸载所有容器, 最好将docker一同卸载,得到一个纯净的ubuntu系统来操作。需要去轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台购买一个镜像,由于很多国内的免费镜像有大大小小的问题。
选择专业版。

之后得到自己的专属镜像地址,进入ubuntu中配置 /etc/docker/daemon.json加入自己的镜像
之后重新启动该文件
systemctl restart docker
systemctl daemon-reload
大概率问题就解决了。
跑起来之后我们打开前端项目----------------------------------------------------------------------------------------
前端的项目跑起来很简单,我们只需要查看三个文件如下图:

首先README.md就是一个项目的文档,拿到一个项目首先要来查看这个文件,此项目这个文件有很详细的讲解。
按照文件进行下载安装配置pnpm
## 🐶 新手必读
- nodejs > 16.18.0 && pnpm > 8.6.0 (强制使用pnpm)
- 安装启动:
```
# 安装 pnpm,提升依赖的安装速度
npm config set registry https://registry.npmmirror.com
npm install -g pnpm
# 安装依赖
pnpm install
# 启动服务
npm run dev
```
修改env.dev文件中的请求路径

配置完成后运行该命令。



docker容器对于端口映射问题
由于docker是一个隔离的环境,所以对于需要通过ip地址来操作后端应用时,需要设置好ip地址的问题,和端口映射问题。例如:在windos上访问后端mqtt接口时候,由于docker容器没有直接暴露在外,windous不能通过访问ubuntu的ip地址来访问容器内的端口。
解决方法:
首先进入容器,所需要的服务是否以及启动起来。
//1.进入容器
docker exec -it iot-server bash
//2.检查容器内是否有MQTT服务进程(以常见的mosquitto为例)
ps -ef | grep mosquitto # 若使用其他MQTT服务(如emqx),替换为对应名称
# 若有进程输出,说明服务已启动;无输出则需启动服务(如:mosquitto -c 配置文件路径)
//3.确认 MQTT 服务是否在配置的 18831 端口监听:
# 在容器内执行(查看端口监听状态)
netstat -tulpn | grep 18831
# 或(适用于无netstat的系统)
ss -tulpn | grep 18831
# 若输出类似 "tcp 0 0 127.0.0.1:18831 0.0.0.0:* LISTEN 进程ID/服务名"
# 说明服务仅监听容器内的127.0.0.1,外部无法访问(关键问题!)
# 更新包索引(可选)
apt update
# 安装net-tools(包含netstat)
apt install -y net-tools
若
若显示确实什么安装包就查找对应的安装命令,直接运行安装命令后继续。
接下来在宿主机(ubuntu)上运行以下命令,查看容器端口是否正常映射。
# 查看容器端口映射 docker inspect iot-server | grep "PortBindings" -A 10
# 若未映射18831端口,需重新启动容器并添加映射: docker run -p 18831:18831 ...(其他参数) iot-server

也可以直接在docker-comppose.yml文件中找到相应的映射端口进行添加。

完成端口映射后,回到mqtt客户端连接 。

就可以连接成功。
几个连接时候的重要参数:
1。订阅 topic:/sys/R755G5Wb3jst4tD7/C18338/c/#
topic 组成格式:/sys/{productKey}/{dn}/c/#
md5 生成工具:https://www.toolhelper.cn/DigestAlgorithm/MD5

2。属性上报:填写并发送: payload 可以填一个属性也可以同时填多个属性。
//1.上报的主题
topic: /sys/R755G5Wb3jst4tD7/C18338/s/event/property/post
//2.上报的内容,params中的参数为物模型参数
payload: {
"id": "2a72ddb8-7a84-4a57-b745-97698716fb9e",
"method": "thing.event.property.post",
"params": {
"model": 1,"remain":90
},
"version":"1.0.0"
}
各字段含义:
-
id- 作用:消息的唯一标识符(类似 “订单号”),用于标识一条消息,方便后续追踪、重试或响应匹配(例如服务器返回结果时,会携带相同的
id以对应请求)。 - 格式:通常是 UUID(如示例中的字符串)、数字或自定义唯一字符串,确保在一定范围内不重复即可。
- 作用:消息的唯一标识符(类似 “订单号”),用于标识一条消息,方便后续追踪、重试或响应匹配(例如服务器返回结果时,会携带相同的
-
method- 作用:定义消息的 “操作类型” 或 “业务含义”,告诉接收方(如服务器)这条消息的用途。
- 示例中
"thing.event.property.post"表示 “设备属性事件上报”,常见于物联网场景(设备向平台上报自身属性数据,如温度、状态等)。
-
params- 作用:消息的核心业务数据,即需要传递的具体内容。
- 示例中
{"test1": 33}表示上报的属性为test1,值为33(可以是温度、传感器读数等实际业务数据)。
-
version- 作用:指定协议版本,确保发送方和接收方使用相同的格式规则解析消息(避免因版本不兼容导致解析错误)。
- 示例中
"1.0.0"是自定义的版本号,具体含义由业务方约定。


数据下发:

以下(可以试一试),不做不影响。

其他知识(选看):
在ubuntu系统中有较难的DNS网络配置问题:
在 Ubuntu 系统中,DNS(域名系统)是网络通信的核心组件之一,负责将人类可读的域名(如 www.baidu.com)转换为计算机可识别的 IP 地址(如 180.101.50.242)。以下详细说明 DNS 配置的核心问题:
- DNS 是域名与 IP 的 “翻译官”,配置错误会直接导致网络访问失败。
- Ubuntu 主要通过
/etc/resolv.conf、/etc/netplan/*.yaml或systemd-resolved配置 DNS,具体取决于网络管理工具。 - 当出现域名解析问题、网络速度慢或特定服务依赖时,需手动配置 DNS 服务器。
TDengine实时数据库:
TDengine 是一款专为物联网(IoT)、工业互联网、金融等领域设计的开源高性能时序数据库(Time-Series Database),具备高性能、高压缩比、易扩展等特点。
TDengine 作为时序数据库(Time-Series Database),与传统关系型数据库(如 MySQL)和缓存数据库(如 Redis)在设计目标、数据模型和适用场景上有本质区别。以下是三者的核心差异及联合使用的原因:
一、核心区别对比
| 特性 | TDengine(时序数据库) | MySQL(关系型数据库) | Redis(键值缓存) |
|---|---|---|---|
| 数据模型 | 时间序列导向,超级表 + 子表结构 | 表 - 行 - 列结构,支持复杂关系 | 键值对、集合、列表等数据结构 |
| 写入性能 | 极快(单节点 10 万 + 点 / 秒) | 中等(依赖磁盘 IO,数万 QPS) | 极快(内存操作,百万 QPS) |
| 数据压缩 | 高(80%+ 空间节省) | 低(依赖存储引擎,通常 30%-50%) | 无(除非使用 RDB 持久化) |
| 查询模式 | 时间范围查询为主,聚合分析 | 支持复杂查询(JOIN、子查询) | 简单 KV 查询、范围操作 |
| 数据时效性 | 历史数据为主,近期数据实时写入 | 全量数据持久化 | 热点数据缓存,可能丢失 |
| 数据量 | PB 级时序数据 | 通常 TB 级(受限于存储和性能) | GB 级(受限于内存) |
| 典型场景 | IoT 数据、监控指标、金融交易记录 | 业务系统(订单、用户)、OLTP | 缓存、计数器、 |
3. TDengine:高效处理时序数据(常用于物联网设备的快速写入)
- 适用场景:
- 高频写入:IoT 设备数据采集(每秒数万条记录)。
- 时间序列分析:实时监控指标(CPU 使用率、网络流量)。
- 长期存储:历史数据压缩存储(10 年 + 数据)。
- 快速聚合:按时间窗口统计(如 hourly/daily 报表)。
- 局限性:
- 不支持复杂关系查询(如多表 JOIN)。
- 事务支持弱(仅单行事务)。
- 数据模型相对固定(需预先定义超级表)。
将ESP32通过MQTT协议连接到该物联网平台进行管理
需要用新版的ArduinoIDE,如果用低版本的Arduino1.8可能是库版本的问题,导致连接不上,或者订阅主题,发布消息有错误。用新版的IDE问题解决。
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
// WiFi配置
const char* wifiSSID = "Hxxxx";
const char* wifiPassword = "clxxxx";
// MQTT配置
const char* mqttBroker = "192.168.110xx";
const int mqttPort = 18831;
const char* mqttClientId = "HYzcZazD4nxpfCxxxx1";
const char* mqttUsername = "xx3";
const char* mqttPassword = "a6d2311b1be0afdc85dxxa9158d03f";
const char* mqttTopic = "/sys/HYzcZazD4nxpfCXX/cx3/s/event/property/post";
const char* statusTopic = "/sys/HYzcZazD4nxpfCXX/ccx3/status"; // 状态主题
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
// 连接WiFi
void connectWiFi() {
Serial.print("连接WiFi: ");
Serial.println(wifiSSID);
WiFi.begin(wifiSSID, wifiPassword);
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED && (millis() - startAttemptTime) < 10000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi已连接");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nWiFi连接失败");
}
}
// 连接MQTT
bool connectMQTT() {
if (mqttClient.connected()) {
return true;
}
Serial.println("尝试连接MQTT服务器...");
// 设置遗嘱消息,当客户端意外断开时发布
bool result = mqttClient.connect(
mqttClientId,
mqttUsername,
mqttPassword
);
if (result) {
Serial.println("MQTT连接成功");
// 发布在线状态
mqttClient.publish(statusTopic, "online", true);
return true;
} else {
Serial.print("MQTT连接失败,错误代码: ");
Serial.println(mqttClient.state());
return false;
}
}
// 发布消息
bool publishMessage() {
if (!mqttClient.connected()) {
Serial.println("MQTT未连接,无法发布消息");
return false;
}
// 生成随机数
int randomValue = random(100);
// 构建JSON消息
StaticJsonDocument<200> jsonDoc;
jsonDoc["id"] = "2a72ddb8-7a84-4a57-b745-97698716fb9e";
jsonDoc["method"] = "thing.event.property.post";
jsonDoc["version"] = "1.0.0";
jsonDoc["params"]["test1"] = randomValue;
// 序列化JSON
char jsonBuffer[200];
size_t jsonLength = serializeJson(jsonDoc, jsonBuffer);
// 打印待发布的JSON
Serial.print("待发布JSON: ");
Serial.println(jsonBuffer);
// 发布消息并检查返回值
bool publishResult = mqttClient.publish(
mqttTopic,
reinterpret_cast<const uint8_t*>(jsonBuffer),
jsonLength,
false // 不保留消息
);
if (publishResult) {
Serial.println("消息发布成功");
return true;
} else {
Serial.println("消息发布失败");
return false;
}
}
// 处理WiFi断开事件
void handleWiFiDisconnect() {
Serial.println("WiFi连接断开");
// 可以添加重连逻辑
}
// 处理MQTT断开事件
void handleMQTTDisconnect() {
Serial.println("MQTT连接断开");
// 可以添加重连逻辑
}
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0)); // 初始化随机数种子
// 连接WiFi
connectWiFi();
mqttClient.setServer(mqttBroker, mqttPort); //设置客户端连接的服务器,连接Onenet服务器, 使用18831端口
delay(2000);
Serial.println("setServer Init!");
// 设置MQTT服务器和回调函数
mqttClient.setServer(mqttBroker, mqttPort);
}
void loop() {
// 检查WiFi连接状态
if (WiFi.status() != WL_CONNECTED) {
handleWiFiDisconnect();
connectWiFi();
delay(2000); // 避免频繁重试
return;
}
// 检查MQTT连接状态
if (!mqttClient.connected()) {
handleMQTTDisconnect();
if (!connectMQTT()) {
delay(5000); // 连接失败,延迟重试
return;
}
}
// 处理MQTT网络流量
mqttClient.loop();
// 发布消息
publishMessage();
// 延迟
delay(1000);
}
ESP32端串口输出信息

网页端数据实时刷新。

第一版:
代码上行与下行都实现,能够控制ESP32灯
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
// WiFi配置
const char* wifiSSID = "xxx6";
const char* wifiPassword = "xxxk";
// MQTT配置
const char* mqttBroker = "1xxx";
const int mqttPort = 18831;
const char* mqttClientId = "HYzxx_m1";
const char* mqttUsername = "cx3";
const char* mqttPassword = "a6d2311b1be0afxxa9158d03f";
const char* pubTopic = "/sys/HYzcZazD4nxxxcc123/s/event/property/post";
const char* subTopic = "/sys/HYzcZazD4nxxccc123/c/service/property/set";
const char* statusTopic = "/sys/HYzcZaxxfCXX/ccc123/status";
// 定时发布参数(非阻塞式)
const unsigned long PUBLISH_INTERVAL = 1000; // 发布间隔1秒
unsigned long lastPublishTime = 0; // 上次发布时间
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
// 连接WiFi
void connectWiFi() {
if (WiFi.status() == WL_CONNECTED) return; // 已连接则直接返回
Serial.print("连接WiFi: ");
Serial.println(wifiSSID);
WiFi.begin(wifiSSID, wifiPassword);
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED && (millis() - startAttemptTime) < 10000) {
delay(100); // 缩短重试间隔,加快连接
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi已连接");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nWiFi连接失败");
}
}
// MQTT消息回调函数(精简处理,优先响应控制指令)
void mqttCallback(char* topic, byte* payload, unsigned int length) {
// 仅处理订阅的控制主题,过滤其他无关消息
String topicStr = String(topic);
if (topicStr != subTopic) {
return; // 非控制主题,直接忽略,减少处理时间
}
// 快速解析test2,优先执行灯控(精简打印,减少IO阻塞)
char jsonBuffer[length + 1];
memcpy(jsonBuffer, payload, length);
jsonBuffer[length] = '\0';
StaticJsonDocument<200> doc; // 缩小缓冲区,加快解析
DeserializationError error = deserializeJson(doc, jsonBuffer);
if (error) return; // 解析失败直接返回
// 只关注test2字段,快速响应
if (doc.containsKey("params") && doc["params"].containsKey("test2")) {
String test2Str = doc["params"]["test2"].as<String>();
int test2Value = test2Str.toInt();
// 立即执行灯控,不等待打印
if (test2Value == 1) {
digitalWrite(LED_BUILTIN, HIGH);
} else if (test2Value == 2) {
digitalWrite(LED_BUILTIN, LOW);
}
// 后台打印状态(不阻塞控制流程)
Serial.print("\n灯控指令执行: test2=");
Serial.println(test2Value);
}
}
// 连接MQTT并订阅主题
bool connectMQTT() {
if (mqttClient.connected()) return true;
Serial.println("尝试连接MQTT服务器...");
bool result = mqttClient.connect(mqttClientId, mqttUsername, mqttPassword);
if (result) {
Serial.println("MQTT连接成功");
mqttClient.subscribe(subTopic); // 订阅控制主题
mqttClient.publish(statusTopic, "online", true);
} else {
Serial.print("MQTT连接失败,错误代码: ");
Serial.println(mqttClient.state());
}
return result;
}
// 非阻塞式发布消息(仅在间隔时间到后发布)
void publishMessageNonBlock() {
unsigned long currentTime = millis();
if (currentTime - lastPublishTime < PUBLISH_INTERVAL) {
return; // 未到发布时间,跳过
}
lastPublishTime = currentTime; // 更新时间戳
if (!mqttClient.connected()) return;
// 简化发布数据,减少JSON构建时间
int randomValue = random(100);
StaticJsonDocument<150> jsonDoc; // 缩小缓冲区
jsonDoc["id"] = "2a72ddb8-7a84-4a57-b745-97698716fb9e";
jsonDoc["method"] = "thing.event.property.post";
jsonDoc["params"]["test1"] = randomValue;
char jsonBuffer[150];
serializeJson(jsonDoc, jsonBuffer);
mqttClient.publish(pubTopic, jsonBuffer);
// 精简打印,减少IO耗时
Serial.print("发布test1数据: ");
Serial.println(randomValue);
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW); // 初始关闭
connectWiFi();
mqttClient.setServer(mqttBroker, mqttPort);
mqttClient.setCallback(mqttCallback); // 回调函数优先处理
mqttClient.setSocketTimeout(5000); // 缩短超时,减少阻塞
}
void loop() {
// 快速处理MQTT消息(最高优先级)
if (mqttClient.connected()) {
mqttClient.loop(); // 非阻塞式处理消息,立即响应回调
}
// 维持连接(次要优先级)
connectWiFi();
connectMQTT();
// 定时发布(最低优先级,不阻塞控制)
publishMessageNonBlock();
// 无delay,让loop()快速循环,提升响应速度
}
//分任务版本
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
// WiFi配置
const char* wifiSSID = "xxxx";
const char* wifiPassword = "xxxx";
// MQTT配置
const char* mqttBroker = "1xxxx";
const int mqttPort = 18831;
const char* mqttClientId = "HYxxx";
const char* mqttUsername = "ccc123";
const char* mqttPassword = "a6d2311xxd03f";
const char* pubTopic = "/sys/HYzcZaxxvent/property/post";
const char* subTopic = "/sys/HYzcZazDxxcc123/c/service/property/set";
const char* statusTopic = "/sys/HYxxxpfCXX/ccc123/status";
// 任务定时参数(毫秒)
const unsigned long WIFI_CHECK_INTERVAL = 2000; // WiFi检查间隔
const unsigned long MQTT_CHECK_INTERVAL = 1000; // MQTT连接检查间隔
const unsigned long PUBLISH_INTERVAL = 1000; // 数据发布间隔
const unsigned long MSG_PROCESS_INTERVAL = 10; // 消息处理间隔(高频)
// 全局变量
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
unsigned long lastWifiCheck = 0; // 上次WiFi检查时间
unsigned long lastMqttCheck = 0; // 上次MQTT检查时间
unsigned long lastPublish = 0; // 上次发布时间
unsigned long lastMsgProcess = 0; // 上次消息处理时间
// ==================== 任务1:WiFi连接维护 ====================
void taskWiFiMaintain() {
unsigned long currentTime = millis();
if (currentTime - lastWifiCheck < WIFI_CHECK_INTERVAL) {
return; // 未到检查时间
}
lastWifiCheck = currentTime;
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi断开,尝试重连...");
WiFi.reconnect(); // 快速重连
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi重连成功");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
}
}
}
// ==================== 任务2:MQTT连接维护 ====================
void taskMQTTMaintain() {
unsigned long currentTime = millis();
if (currentTime - lastMqttCheck < MQTT_CHECK_INTERVAL) {
return; // 未到检查时间
}
lastMqttCheck = currentTime;
if (!mqttClient.connected() && WiFi.status() == WL_CONNECTED) {
Serial.println("MQTT断开,尝试重连...");
bool connected = mqttClient.connect(
mqttClientId,
mqttUsername,
mqttPassword
);
if (connected) {
Serial.println("MQTT重连成功");
mqttClient.subscribe(subTopic); // 重新订阅主题
mqttClient.publish(statusTopic, "online", true);
} else {
Serial.print("MQTT重连失败,错误代码: ");
Serial.println(mqttClient.state());
}
}
}
// ==================== 任务3:MQTT消息处理(高优先级) ====================
void taskProcessMessages() {
unsigned long currentTime = millis();
if (currentTime - lastMsgProcess < MSG_PROCESS_INTERVAL) {
return; // 高频检查,确保消息不延迟
}
lastMsgProcess = currentTime;
if (mqttClient.connected()) {
mqttClient.loop(); // 处理收到的消息(触发回调函数)
}
}
// ==================== 任务4:数据发布 ====================
void taskPublishData() {
unsigned long currentTime = millis();
if (currentTime - lastPublish < PUBLISH_INTERVAL) {
return; // 未到发布时间
}
lastPublish = currentTime;
if (!mqttClient.connected()) {
return; // MQTT未连接,跳过发布
}
// 生成并发布数据
int randomValue = random(100);
StaticJsonDocument<150> jsonDoc;
jsonDoc["id"] = "2a72ddb8-7a84-4a57-b745-97698716fb9e";
jsonDoc["method"] = "thing.event.property.post";
jsonDoc["params"]["test1"] = randomValue;
char jsonBuffer[150];
serializeJson(jsonDoc, jsonBuffer);
mqttClient.publish(pubTopic, jsonBuffer);
Serial.print("发布test1: "); // 可选:精简打印
Serial.println(randomValue);
}
// ==================== MQTT消息回调(灯控核心逻辑) ====================
void mqttCallback(char* topic, byte* payload, unsigned int length) {
// 仅处理订阅的控制主题
if (strcmp(topic, subTopic) != 0) {
return;
}
// 快速解析test2并执行灯控
char jsonBuffer[length + 1];
memcpy(jsonBuffer, payload, length);
jsonBuffer[length] = '\0';
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, jsonBuffer);
if (error) return;
if (doc["params"].containsKey("test2")) {
int test2Value = doc["params"]["test2"].as<String>().toInt();
// 立即执行灯控
if (test2Value == 1) {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("灯光已打开");
} else if (test2Value == 2) {
digitalWrite(LED_BUILTIN, LOW);
Serial.println("灯光已关闭");
}
}
}
// ==================== 初始化与主循环 ====================
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW); // 初始关闭
// 初始化连接
WiFi.begin(wifiSSID, wifiPassword);
mqttClient.setServer(mqttBroker, mqttPort);
mqttClient.setCallback(mqttCallback); // 绑定回调函数
}
void loop() {
// 按优先级执行任务(消息处理 > 连接维护 > 数据发布)
taskProcessMessages(); // 最高优先级:实时处理消息
taskWiFiMaintain(); // 中优先级:维护WiFi连接
taskMQTTMaintain(); // 中优先级:维护MQTT连接
taskPublishData(); // 低优先级:定时发布数据
}
更多推荐
所有评论(0)