当前位置: 首页 > news >正文

【ESP32】【微信小程序】MQTT物联网智能家居案例

这里写自定义目录标题

  • 案例成果
  • 1.Ardino写入部分
  • 2.微信小程序JS部分
  • 3.微信小程序xml部分
  • 4. 微信小程序CSS部分

案例成果

在这里插入图片描述

1.Ardino写入部分

#include <WiFi.h>          // ESP32 WiFi库
#include <PubSubClient.h>  // MQTT客户端库
#include <DHT.h>           // DHT传感器库// WiFi配置
const char *ssid = "601B";       // WiFi名称
const char *password = "12345678"; // WiFi密码// MQTT服务器配置
const char* mqtt_server = "bemfa.com";    // MQTT服务器地址
const int mqtt_server_port = 9501;        // MQTT服务器端口
#define ID_MQTT "0c876e1774cc3bd27de1d5d1bfc61c90" // 您的客户端ID// 创建WiFi和MQTT客户端实例
WiFiClient espClient;
PubSubClient client(espClient);// 定义引脚 - 注意:ESP32使用GPIO编号,而不是NodeMCU的D编号
#define ledpin 2       // LED指示灯连接的引脚,用于远程控制开关 (GPIO2)
#define eqpin 3        // 地震传感器连接的引脚,用于检测震动 (GPIO3)
#define beeppin 18      // 蜂鸣器连接的引脚,用于警报提示 
#define flapin 5       // 火灾传感器连接的引脚,用于检测火情 (GPIO5)
#define dhtpin 15      // DHT11温湿度传感器连接的引脚 (GPIO15)
#define DHTTYPE DHT11  // 定义使用的温湿度传感器类型为DHT11
#define airpin 34      // 空气质量传感器连接的模拟引脚,用于检测空气污染程度 (GPIO34 - ADC6)// 创建DHT传感器对象
DHT dht(dhtpin, DHTTYPE);// 定义状态变量
bool val1 = 0;  // 地震传感器状态
bool val2 = 0;  // 火灾传感器状态// MQTT主题 - 与微信小程序匹配的命名
const char* mqtt_switch_topic = "switch";     // LED开关主题(接收控制命令)
const char* mqtt_temper_topic = "temper";     // 温度主题(发布温度数据)
const char* mqtt_humid_topic = "humid";       // 湿度主题(发布湿度数据)
const char* mqtt_air_topic = "air";           // 空气质量主题(发布空气质量数据)
const char* mqtt_earthquake_topic = "earthquake"; // 地震主题(发布地震状态)
const char* mqtt_flame_topic = "flame";       // 火灾主题(发布火灾状态)
const char* mqtt_beep_topic = "beep";         // 蜂鸣器主题(可选)// WiFi连接函数
void setup_wifi() {delay(10);Serial.println();Serial.print("Connecting to ");Serial.println(ssid);WiFi.begin(ssid, password);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println("");Serial.println("WiFi connected");Serial.print("IP address: ");Serial.println(WiFi.localIP());
}// MQTT消息接收回调函数
void callback(char* topic, byte* payload, unsigned int length) {Serial.print("订阅的信息为 [");Serial.print(topic);Serial.print("] ");// 将接收到的字节数组转换为字符串String message;for (int i = 0; i < length; i++) {message += (char)payload[i];}Serial.println(message);// 控制LEDif (strcmp(topic, mqtt_switch_topic) == 0) {if (message == "on") {digitalWrite(ledpin, HIGH);Serial.println("打开 LED 灯");} else if (message == "off") {digitalWrite(ledpin, LOW);Serial.println("关闭 LED 灯");}}
}// 重连函数
void reconnect() {// 循环直到重新连接while (!client.connected()) {Serial.print("尝试连接MQTT...");// 尝试连接if (client.connect(ID_MQTT)) {Serial.println("connected");// 连接成功后,订阅LED控制主题client.subscribe(mqtt_switch_topic);Serial.print("已订阅主题: ");Serial.println(mqtt_switch_topic);} else {Serial.print("failed, rc=");Serial.print(client.state());Serial.println(" try again in 5 seconds");delay(5000);}}
}// 初始化
void setup() {// 初始化串口Serial.begin(115200);Serial.println("初始化系统...");// 初始化DHT传感器dht.begin();// 设置引脚模式pinMode(ledpin, OUTPUT);digitalWrite(ledpin, LOW);// 设置传感器引脚pinMode(eqpin, INPUT);pinMode(flapin, INPUT);pinMode(beeppin, OUTPUT);pinMode(airpin, INPUT);  // 模拟输入// 连接WiFisetup_wifi();// 设置MQTT服务器和回调client.setServer(mqtt_server, mqtt_server_port);client.setCallback(callback);// 连接MQTT服务器reconnect();Serial.println("系统初始化完成!");
}void loop() {// 检查MQTT连接状态if (!client.connected()) {reconnect(); // 重新连接}// 处理MQTT消息client.loop();// 读取温湿度float h = dht.readHumidity();float t = dht.readTemperature();// 检查温湿度读取是否成功if (isnan(h) || isnan(t)) {Serial.println("Failed to read from DHT sensor!");} else {// 输出温湿度Serial.print("湿度: ");Serial.print(h);Serial.print("%, 温度: ");Serial.print(t);Serial.println("°C");// 转换为字符串用于发布String tempMsg = String(t);String humidMsg = String(h);// 发布温湿度数据到MQTTclient.publish(mqtt_temper_topic, tempMsg.c_str());client.publish(mqtt_humid_topic, humidMsg.c_str());}// 读取空气质量int airval = analogRead(airpin);Serial.print("空气质量: ");Serial.println(airval);String airMsg = String(airval);client.publish(mqtt_air_topic, airMsg.c_str());// 读取地震传感器状态val1 = digitalRead(eqpin);if (val1 == 0) {// 发布地震警报client.publish(mqtt_earthquake_topic, "off");Serial.println("无震感");client.publish(mqtt_beep_topic, "off"); // 前端无需显示蜂鸣器的状态}else {// 发布地震警报client.publish(mqtt_earthquake_topic, "on");Serial.println("有震感");client.publish(mqtt_beep_topic, "on"); // 前端无需显示蜂鸣器的状态}// 读取火灾传感器状态val2 = digitalRead(flapin);Serial.print("火灾传感器状态: ");Serial.println(val2);if (val2 == 1) {// 发布火灾警报client.publish(mqtt_flame_topic, "off");Serial.println("无火灾");digitalWrite(beeppin, HIGH);}else {// 发布火灾警报client.publish(mqtt_flame_topic, "on");Serial.println("有火灾");digitalWrite(beeppin, LOW);}// 延时100毫秒delay(100);
}

2.微信小程序JS部分

// pages/class/class.js
const app = getApp()
import mqtt from '../../utils/mqtt.min'Page({data: {// 主题定义ledtopic: "switch",          // LED开关主题eqtopic: "earthquake",       // 地震传感器主题fhtopic: "flame",            // 火灾传感器主题wdtopic: "temper",           // 温度传感器主题sdtopic: "humid",            // 湿度传感器主题kqtopic: "air",              // 空气质量传感器主题beeptopic: "beep",           // 新增:蜂鸣器主题uid: "0c876e1774cc3bd27de1d5d1bfc61c90",  // 设备唯一ID// 传感器状态qustatus: "无震感",          // 地震状态flastatus: "无火灾",         // 火灾状态wdval: "未知",               // 温度值sdval: "未知",               // 湿度值airval: "未知",              // 空气质量值beepstatus: "关闭",          // 新增:蜂鸣器状态isThemeOn: false,            // 开关状态,默认关闭device_status: "离线",       // 设备连接状态client: null                 // MQTT客户端},// MQTT连接方法mqttConnect() {var that = this;var options = {keepalive: 60,             // 心跳间隔clean: true,               // 清除会话protocolVersion: 4,        // MQTT协议版本clientId: that.data.uid    // 客户端ID};// 连接到MQTT服务器that.data.client = mqtt.connect('wxs://bemfa.com:9504/wss', options);// 连接成功回调that.data.client.on('connect', function() {console.log("连接服务器成功");that.setData({device_status: "在线"});// 订阅所有传感器主题// 订阅地震传感器主题that.data.client.subscribe(that.data.eqtopic, function(err) {if(err) console.log(err);});// 订阅火灾传感器主题that.data.client.subscribe(that.data.fhtopic, function(err) {if(err) console.log(err);});// 订阅温度传感器主题that.data.client.subscribe(that.data.wdtopic, function(err) {if(err) console.log(err);});// 订阅湿度传感器主题that.data.client.subscribe(that.data.sdtopic, function(err) {if(err) console.log(err);});// 订阅空气质量传感器主题that.data.client.subscribe(that.data.kqtopic, function(err) {if(err) console.log(err);});// 新增:订阅蜂鸣器主题that.data.client.subscribe(that.data.beeptopic, function(err) {if(err) console.log(err);});});// 接收消息回调that.data.client.on('message', function(topic, message) {var msg = message.toString();// 处理不同主题的消息// 地震传感器消息if(topic === that.data.eqtopic) {that.setData({qustatus: msg === 'on' ? '有地震' : '无地震'});}// 火灾传感器消息else if(topic === that.data.fhtopic) {that.setData({flastatus: msg === 'on' ? '有火灾' : '无火灾'});}// 温度传感器消息else if(topic === that.data.wdtopic) {that.setData({wdval: msg});}// 湿度传感器消息else if(topic === that.data.sdtopic) {that.setData({sdval: msg});}// 空气质量传感器消息else if(topic === that.data.kqtopic) {that.setData({airval: msg});}// 新增:蜂鸣器消息else if(topic === that.data.beeptopic) {that.setData({beepstatus: msg === 'on' ? '开启' : '关闭'});}});// 重连回调that.data.client.on('reconnect', function() {console.log("重新连接");that.setData({device_status: "连接中"});});// 连接错误回调that.data.client.on('error', function(error) {console.log("连接失败", error);that.setData({device_status: "离线"});});},// 页面加载时连接MQTT服务器onLoad() {this.mqttConnect()},// 开关点击事件处理onThemeSwitchTap: function() {if(this.data.client && this.data.client.connected) {// 切换开关状态const message = this.data.isThemeOn ? 'off' : 'on';// 发布消息到LED开关主题this.data.client.publish(this.data.ledtopic, message);// 更新UI状态this.setData({isThemeOn: !this.data.isThemeOn});} else {console.log("MQTT未连接");}}
})

3.微信小程序xml部分

<!--pages/znjj/znjj.wxml-->
<view class="imag4_src"></view>
<image class="imag4" src="/img/znjj.png"></image>
<view class="spacer"></view><view class="container1"><view class="view1"><view class="img_wd"><view class="content-row"><image class="img1" src="/img/wd.png"></image><text>温度</text></view><view class="loading-container"><!-- 修改:绑定温度数据 --><text class="loading-text">{{wdval}}</text></view></view></view><view class="view2"><view class="img_wd"><view class="content-row"><image class="img1" src="/img/sd.png"></image><text>湿度</text></view><view class="loading-container"><!-- 修改:绑定湿度数据 --><text class="loading-text">{{sdval}}</text></view></view></view><view class="view3"><view class="img_wd"><view class="content-row"><image class="img1" src="/img/PM2.5.png"></image><text>空气</text></view><view class="loading-container"><!-- 修改:绑定空气质量数据 --><text class="loading-text">{{airval}}</text></view></view></view>
</view><view class="spacer"></view><view class="container2"><view class="view4"><view class="img_wd"><view class="content-row"><image class="img1" src="/img/hy.png"></image><text>防火</text></view><view class="loading-container"><!-- 修改:绑定火灾状态 --><text class="loading-text">{{flastatus}}</text></view></view></view><view class="view5"><view class="img_wd"><view class="content-row"><image class="img1" src="/img/dz.png"></image><text>地震</text></view><view class="loading-container"><text class="loading-text">{{qustatus}}</text></view></view></view><view class="view6"><view class="img_wd"><view class="content-row"><image class="img1" src="/img/kt.png"></image><text>空调</text></view><view class="switch-container"><button class="theme-switch-button" bindtap="onThemeSwitchTap">{{isThemeOn ? 'on' : 'off'}}</button></view></view></view>
</view><view class="spacer"></view><view class="container3"><view class="view7"><view class="img_wd"><view class="content-text"><text>设备状态</text><text class="module">{{device_status}}</text></view></view></view><view class="view8"><view class="img_wd"><view class="content-text"><text>火灾告警</text><view wx:if="{{flastatus == '有火灾'}}"><image class="img5" src="/img/hz_on.png"></image></view><view wx:else><image class="img5" src="/img/hz_off.png"></image></view></view></view></view><view class="view9"><view class="img_wd"><view class="content-text"><text>地震告警</text><!-- 修改:使用qustatus而不是eqstatus --><view wx:if="{{qustatus == '有地震'}}"><image class="img5" src="/img/dz_on.png"></image></view><view wx:else><image class="img5" src="/img/dz_off.png"></image></view></view></view></view> 
</view>

4. 微信小程序CSS部分

/* 顶部标题图片容器样式 */
.imag4_src{display: block;  /* 设置为块级元素,占据整行 */width: 100%;     /* 宽度占满父容器 */}/* 顶部标题图片样式 */.imag4{width: 350px;    /* 设置固定宽度 */height: 200px;   /* 设置固定高度 */justify-content: center;  /* 水平居中对齐 */border: 1px solid #000;   /* 添加1像素黑色边框 */border-radius: 10px;      /* 设置10像素圆角 */margin: 20px;             /* 四周添加20像素外边距 */box-shadow: 5px 5px 15px rgba(0,0,0,0.3);  /* 添加阴影效果:右下偏移,模糊度,颜色 */transition: transform 0.3s ease;  /* 添加变换过渡效果:持续0.3秒,缓动函数ease */}/* 标题图片悬停效果 */.imag4:hover{transform: scale(1.05);  /* 悬停时放大图片到原尺寸的1.05倍 */}/* 垂直间隔元素 */.spacer{height: 10px;  /* 创建10像素高度的垂直间隔 */}/* 第一行和第二行容器共同样式 */.container1,.container2{display: flex;           /* 设置为Flex布局容器 */flex-direction: row;     /* 子元素水平排列(默认值) */justify-content: center; /* 子元素在主轴(水平)上居中对齐 */}/* 第一行和第二行中所有方块的共同样式 */.container1 .view1,.container1 .view2,.container1 .view3,.container2 .view4,.container2 .view5,.container2 .view6{width: 90px;             /* 设置宽度为90像素 */height: 100px;           /* 设置高度为100像素 */margin: 20px;            /* 四周添加20像素外边距 */position: relative;      /* 设置相对定位,作为内部元素的定位参考 */background-color: transparent;  /* 背景透明(但随后被单独的规则覆盖) */display: flex;           /* 设置为Flex布局容器 */align-items: lsft;       /* 注意:这里有拼写错误,应为'left',垂直方向左对齐 */justify-content: center; /* 水平居中对齐 */border: 2px solid rgb(291,214,214);  /* 注意:RGB值错误,291超出范围(0-255) */box-shadow: 0 0 10px rgba(37,37,37,0.5);  /* 添加阴影效果,模拟立体边框 */}/* 第一行各模块的背景颜色 */.container1 .view1 { background-color: #e3ece2; }  /* 温度模块:淡绿色 */.container1 .view2 { background-color: #ccd4cb; }  /* 湿度模块:灰绿色 */.container1 .view3 { background-color: #e5f3ee; }  /* 空气模块:浅蓝绿色 *//* 第二行各模块的背景颜色 */.container2 .view4 { background-color: #f7e7e9; }  /* 防火模块:淡粉色 */.container2 .view5 { background-color: #d7eef7; }  /* 地震模块:淡蓝色 */.container2 .view6 { background-color: #e3e4f1; }  /* 空调模块:淡紫色 *//* 模块内容容器样式 */.img_wd{justify-content: flex-start;  /* 内容在水平方向靠左对齐 */align-items: flex-start;      /* 内容在垂直方向靠上对齐 */width: 100px;                 /* 设置宽度为100像素 */height: 100px;                /* 设置高度为100像素 */position: relative;           /* 设置相对定位,供内部元素参考 */}/* 图标和文字行样式 */.content-row{margin-top: 1px;       /* 顶部添加1像素外边距 */display: flex;         /* 设置为Flex布局容器 */align-items: center;   /* 子元素在垂直方向居中对齐 */margin-left: 3px;      /* 左侧添加3像素外边距 */}/* 图标样式 */.img1{width: 30px;           /* 设置图标宽度为30像素 */height: 30px;          /* 设置图标高度为30像素 */border: 1px solid rgb(211,209,209);  /* 添加浅灰色边框 */border-radius: 10px;                 /* 设置10像素圆角 */margin: 3px;                         /* 四周添加3像素外边距 */box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.3);  /* 添加阴影效果 */transition: transform 0.3s ease;               /* 添加变换过渡效果 */margin-right: 5px;                           /* 右侧添加5像素外边距,与文字分开 */}/* 数据加载容器样式 */.loading-container{margin-top: 20px;     /* 顶部添加20像素外边距 */display: flex;        /* 设置为Flex布局容器 */justify-content: center;  /* 水平居中对齐 */align-items: center;      /* 垂直居中对齐 */}/* 加载文本样式 */.loading-text{justify-content: center;  /* 水平居中对齐(在flex子项中) */align-items: center;      /* 垂直居中对齐(在flex子项中) */font-size: 16px;          /* 设置字体大小为16像素 */color: #333;              /* 设置字体颜色为深灰色 */height: 20px;             /* 设置高度为20像素 */}/* 第三行容器样式 */.container3{display: flex;           /* 设置为Flex布局容器 */flex-direction: row;     /* 子元素水平排列 */justify-content: center; /* 水平居中对齐 *//* 注释提到暂时移除其他样式以进行故障排除 */}/* 第三行各模块共同样式 */.container3 .view7,.container3 .view8,.container3 .view9{width: 90px;            /* 设置宽度为90像素 */height: 100px;          /* 设置高度为100像素 */margin: 20px;           /* 四周添加20像素外边距 */position: relative;     /* 设置相对定位 */background-color: transparent;  /* 背景透明(但随后被单独的规则覆盖) */display: flex;          /* 设置为Flex布局容器 */align-items: left;      /* 垂直方向左对齐(注意:这应为vertical-align或align-items:flex-start) */justify-content: center; /* 水平居中对齐 */border: 2px solid rgb(219, 214, 214);  /* 添加浅灰色边框 */box-shadow: 0 0 10px rgba(37, 37, 37, 0.5);  /* 添加阴影效果 */border-radius: 60px;    /* 设置60像素圆角,几乎成为圆形 */}/* 第三行各模块的背景颜色 */.container3 .view7 { background-color: #f1ecec; }  /* 设备状态模块:淡红灰色 */.container3 .view8 { background-color: #f3f8f6; }  /* 火灾告警模块:淡青色 */.container3 .view9 { background-color: #ece6f5; }  /* 地震告警模块:淡紫色 *//* 开关按钮容器样式 */.switch-container {width: 60px;           /* 设置宽度为60像素 */height: 30px;          /* 设置高度为30像素 */display: flex;         /* 设置为Flex布局容器 */justify-content: center;  /* 水平居中对齐 */align-items: center;      /* 垂直居中对齐 */margin-top: 20px;         /* 顶部添加20像素外边距 */margin-left: auto;        /* 左侧自动外边距,配合右侧自动外边距实现水平居中 */margin-right: auto;       /* 右侧自动外边距,配合左侧自动外边距实现水平居中 */}/* 开关按钮样式 */.theme-switch-button {height: 30px;          /* 设置高度为30像素 */line-height: 30px;     /* 设置行高等于高度,使文字垂直居中 */background-color: rgb(146, 198, 247);  /* 设置淡蓝色背景 */color: rgb(13, 13, 14);               /* 设置几乎黑色的文字颜色 */border: none;                         /* 移除默认边框 */border-radius: 20px;                  /* 设置20像素圆角,使按钮呈胶囊形 */text-align: center;                   /* 文字水平居中 */font-size: 13px;                      /* 设置字体大小为13像素 */border: 1px solid rgb(241, 234, 234); /* 添加浅灰色边框 */box-shadow: 0 0 10px rgba(2, 8, 27, 0.5);  /* 添加阴影效果 */padding: 0;                           /* 移除内边距,确保文字居中显示 */}/* 文本内容容器样式 */.content-text {display: flex;                /* 设置为Flex布局容器 */flex-direction: column;       /* 子元素垂直排列 */align-items: center;          /* 水平居中对齐 */justify-content: flex-start;  /* 垂直方向从顶部开始对齐 */margin-top: 15px;             /* 顶部添加15像素外边距 */width: 100%;                  /* 宽度占满父容器 */box-sizing: border-box;       /* 边框和内边距包含在宽度内 */}/* 模块状态文本样式 */.module {margin-top: 10px;     /* 顶部添加10像素外边距 */color: #f74d4d;       /* 设置文字颜色为鲜红色 */font-weight: bold;    /* 设置字体为粗体 */}/* 告警图标样式 */.img5{margin-top: 5px;      /* 顶部添加5像素外边距 */width: 50px;          /* 设置宽度为50像素 */height: 40px;         /* 设置高度为40像素 */}

相关文章:

  • Nginx下搭建rtmp流媒体服务 并使用HLS或者OBS测试
  • 相机标定(输出相机内参和畸变参数)
  • 前端实现数据导出成excel
  • RIP动态路由(三层交换机+单臂路由)
  • 【Markdown】【HTML】在Markdown中实现康奈尔笔记模式(右侧留白)
  • 百度暑期实习岗位超3000个,AI相关岗位占比87%,近屿智能携AIGC课程加速人才输出
  • ASP.NET Core 分层项目中EFCore的使用
  • 完美解决Microsoft Edge浏览器无法同步/一直在同步中/更新失败等问题
  • 神经网络直接逆控制:神经网络与控制的结合入门级结合
  • 【C#】.net core 6.0调用MVC API接口时,提示Unsupported Media Type,状态码415
  • 穿透数据迷雾:PR 曲线与 ROC 曲线的深度剖析+面试常见问题及解析
  • spring security +kotlin 实现oauth2.0 认证
  • 加油站小程序实战教程12显示会员信息
  • 【Django】设置让局域网内的人访问
  • 忽略 CS8616 警告在 Visual Studio 2022 中【C# 8.0 】
  • Halcon应用:相机标定之应用
  • AI助理iOS开发:Copilot for Xcode 下载与安装全指南
  • Spark-SQL与Hive集成及数据分析实践
  • Android15沉浸式界面顶部有问题
  • jinjia2将后端传至前端的字典变量转换为JS变量
  • 话剧《门第》将开启全国巡演:聚焦牺牲、爱与付出
  • 为溶血性疾病治疗提供新靶点,专家团队在《细胞》发文
  • 能上天入海的“鲲龙”毕业了,AG600取得型号合格证
  • 调查显示特朗普在经济问题上的支持率跌至其总统生涯最低
  • 观察丨微短剧盛行“拿来主义”,版权保护迫在眉睫
  • 在没有穹顶的剧院,和春天的音乐会来一场约会