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

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 Directoryhttps://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项目部署

官网地址:enjoy-iot: 基于若依基础框架开发的物联网平台,包含了产品、物模型、消息转换、组件(mqtt组件、EMQX组件、http组件、tcp组件、modbus组件等)、设备管理、设备分组、规则引擎、第三方平台接入、数据流转(http/mqtt/kafka)、告警中心等模块,支持es/td等多种时序数据库。

有了以上的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"
}

各字段含义:

  1. id

    • 作用:消息的唯一标识符(类似 “订单号”),用于标识一条消息,方便后续追踪、重试或响应匹配(例如服务器返回结果时,会携带相同的id以对应请求)。
    • 格式:通常是 UUID(如示例中的字符串)、数字或自定义唯一字符串,确保在一定范围内不重复即可。
  2. method

    • 作用:定义消息的 “操作类型” 或 “业务含义”,告诉接收方(如服务器)这条消息的用途。
    • 示例中"thing.event.property.post"表示 “设备属性事件上报”,常见于物联网场景(设备向平台上报自身属性数据,如温度、状态等)。
  3. params

    • 作用:消息的核心业务数据,即需要传递的具体内容。
    • 示例中{"test1": 33}表示上报的属性为test1,值为33(可以是温度、传感器读数等实际业务数据)。
  4. 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();        // 低优先级:定时发布数据
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐