十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

微信小程序+MQTT+ESP8266:DHT11温湿度监测与LED控制实战

微信小程序+MQTT+ESP8266:DHT11温湿度监测与LED控制实战 简介ESP32微信小程序智能家居控制系统是一套面向物联网初学者的实用源码包围绕MQTT消息协议串起温湿度采集、烟雾报警和LED远程控制等完整链路。压缩包共3个文件分别为Arduino主程序(.ino)、MQTT客户端源文件(.cpp)及其头文件(.h).ino负责ESP32初始化、Wi-Fi联网、MQTT配置与传感器数据读取.cpp/.h则提供连接云服务器和收发消息所需的底层接口。整包仅7KB体量非常精简却完整覆盖硬件接入、云端通道与设备控制三块核心代码适合课程设计、智能家居改造入门或毕业设计对照。已有1772人在CSDN学习下载可直接参照其中逻辑理解微信小程序与ESP32通过MQTT双向通信的流程进而掌握DHT11单总线温湿度读取、烟雾传感阈值报警以及LED远程开关的实现方法。哪怕是刚接触Arduino的读者也能借此了解从设备端到云平台再到手机端的完整数据链路。1. 微信、MQTT、DHT11 与 LED 的搭积木游戏这个 zip 里到底装了什么微信作为国民级应用跟一块温湿度传感器和 LED 灯有什么关系我第一次看到这个 zip 包名时以为只是把几个流行词拼在一起。但仔细拆开链路微信小程序做人机界面MQTT 当消息总线DHT11 负责感知环境LED 作为执行反馈——这恰好构成了物联网里从“感知”到“控制”的最小闭环。这个项目特别适合放在宿舍或实验室里当环境监测报警器也可以用来系统学习“设备 - Broker - 微信端”三层架构。我习惯把这类压缩包先解压分清固件目录和小程序目录把无关的打包文件删掉再跑条件编译。目标是让嵌入式工程师能复用这套通信协议让前端开发也能看懂设备端的取舍新手按步骤接线烧录熟手直接改主题和回调逻辑就能接入自己的业务。2. 先立住理论MQTT 消息模型与 DHT11 温湿度数据格式很多 zip 项目跑不起来不是因为代码缺什么而是因为对 MQTT 主题的“有状态”理解不够。DHT11 的数据是“读取型”LED 的状态是“保持型”两者在消息设计上完全不一样。只有把这一层想清楚后面写固件和微信端才不会被回调函数绕晕。2.1 MQTT 协议详解发布订阅模型与 QoS 选择MQTT 是基于发布订阅模型的轻量消息协议它和 HTTP 的本质区别在于HTTP 是请求-响应客户端必须知道服务端地址MQTT 则通过 Broker 中转发布者只管发订阅者只管收两者时间上可以错开。这对传感器节点极其友好因为设备可以“说一句就下线”Broker 还能把最后一条消息留住微信端上线后再补拿。我一般会在本地或者云主机上先部署一个 Mosquitto 或 EMQX 作为 Broker。部署完成后用命令行客户端先行验证链路这里最常用的就是mosquitto_submosquitto_sub -h 192.168.1.10 -p 1883 -u device1 -P device1_secret -t home/room1/# -v参数解释-h指定 Broker 主机地址-p是端口-u和-P是 MQTT 用户名和密码如果 Broker 开了 ACL-t是要订阅的主题#通配符代表匹配该层级下所有子主题-v会在输出里同时打印主题和载荷。我刚写固件时一定先开一个这样的终端能直观看到设备有没有成功上报载荷是不是合法 JSON。MQTT 的 QoS 这里多说一点选错会发生丢数据或者重复控制指令。下表是几个典型场景的对比QoS语义下发 LED 指令上报 DHT11 数据0最多一次不确认一般不选常用丢了下次再报1至少一次有重传推荐防止丢灯控可以用但可能重复2仅一次握手重很少用不推荐注意很多 Arduino 库的publish第三个参数是保留标记不是 QoS。像 PubSubClient 的publish(topic, payload, retained)只能发 QoS 0。如果你需要 QoS 1可以换用支持 QoS 的客户端或者用 Mosquitto 的mosquitto_pub手动测试 ACL 链路。2.2 温湿度传感器 DHT11 的单总线时序与数据解析DHT11 是典型的单总线传感器一条数据线既做输入又做输出。主机先拉低总线 18ms 以上然后释放DHT11 会回一个低电平响应信号之后再输出 40 位数据。每一位的“0”和“1”靠高电平持续时长区分大约 26-28us 是高电平视为 070us 左右视为 1。反正逻辑电平很快我常见的误区是拿逻辑分析仪去抓波形其实用示波器看一遍就记住了。40 位数据排列是湿度高 8 位、湿度低 8 位、温度高 8 位、温度低 8 位、校验和 8 位。校验和 前四字节相加取低 8 位。读取代码一般用现成库但核心逻辑要能看懂uint8_t dht_data[5] {0, 0, 0, 0, 0}; if (dht11_read(dht_data)) { float humidity (dht_data[0] 8 | dht_data[1]) / 10.0f; float temperature ((dht_data[2] 0x7F) 8 | dht_data[3]) / 10.0f; if ((uint8_t)(dht_data[0] dht_data[1] dht_data[2] dht_data[3]) ! dht_data[4]) { // 校验失败丢弃本次读取 } }我在实际工程中一般不直接操作底层的精确延时而是用 DHT sensor library 封装好的readTemperature()和readHumidity()。尽管如此还是要记住 DHT11 的采样周期至少 1 秒连续快速读会导致返回NAN或固定值黑客在论坛上说的“读取卡死”多半就是这个原因。2.3 消息体设计JSON 还是纯文本设备端能发一串26.5 45.0但微信小程序那边解析会很难受。我通常把上报和控制都设计成 JSON理由很简单可扩展、可读、不用位运算。主题划分会直接影响 ACL 和调试所以我把传感器和控制指令分开home/room1/sensor—— 设备定时上报的温湿度home/room1/led—— 微信端下发的 LED 控制传感器载荷示例{ device: esp8266-01, type: sensor, temp: 26.4, hum: 45.2, ts: 1712330000 }device字段允许同一主题下多设备共存以后增加第二个传感器也不用改主题。ts是 Unix 时间戳微信端拿它算数据新鲜度。LED 控制载荷保持简单便于在 callback 里直接判断{state: on, brightness: 128}brightness预留了后续 PWM 调光哪怕旧客户端不认识这个字段也不影响开关功能。这就是 JSON 最实在的好处加字段不下滑兼容。3. 固件端实现ESP8266 采集 DHT11 并控制 LED理论部分足够清了现在可以动手烧录固件。我选择 ESP8266 作为主控因为 Arduino 生态对 DHT11 和 MQTT 的支持非常成熟入门成本低。如果你用的是 STM32思路也是一样的封装好 WiFi 模块和 MQTT 客户端再对接 GPIO 逻辑。3.1 硬件接线与元器件清单准备一块 ESP8266 开发板、DHT11、直插型 LED、1kΩ 限流电阻、面包板和若干杜邦线。接线原则是避开 ESP8266 下载模式和串口引脚冲突我一般把 DHT11 放在 GPIO4LED 放在 GPIO5。DHT11 接线表DHT11 引脚接 ESP8266 引脚说明VCC3.3VDHT11 工作电压 3.3V-5V直接 3.3V 即可GNDGND跟 ESP8266 共地DATAGPIO4 / D2这个引脚必须外接 10kΩ 上拉电阻悬空脚不接不用管LED 接线表LED 引脚接 ESP8266 引脚说明正极GPIO5 / D1串联 220-470Ω 电阻负极GND低电平点亮也可以高电平但需要换接线很多人上来就把 LED 直接接 3.3V然后发现 GPIO 驱动能力不足灯只亮一下就灭。限流电阻用欧姆定律算R (3.3 - LED 正向压降) / 工作电流普通红色 LED 压降约 1.8V取 5mA 工作电流算出来约 300Ω实际取 1kΩ 也能亮只是亮度低一些。3.2 Arduino 固件连接 WiFi 与 MQTT打开 Arduino IDE安装DHT sensor library和PubSubClient然后新建工程粘贴最小代码。下面这份代码我尽可能压到最少行数但保留身份验证和回调处理#include ESP8266WiFi.h #include PubSubClient.h #include DHT.h #define DHTPIN 4 #define DHTTYPE DHT11 #define LEDPIN 5 const char* ssid YOUR_WIFI; const char* wifi_password YOUR_WIFI_PASS; const char* mqtt_host 192.168.1.10; const int mqtt_port 1883; const char* mqtt_user device1; const char* mqtt_pass device1_secret; const char* sensor_topic home/room1/sensor; const char* led_topic home/room1/led; WiFiClient espClient; PubSubClient client(espClient); DHT dht(DHTPIN, DHTTYPE); void connectMQTT() { while (!client.connected()) { if (client.connect(esp8266_room1, mqtt_user, mqtt_pass)) { client.subscribe(led_topic); } else { delay(2000); } } } void callback(char* topic, byte* payload, unsigned int length) { String message ; for (int i 0; i length; i) { message (char)payload[i]; } if (String(topic) led_topic) { if (message.indexOf(\on\) 0) { digitalWrite(LEDPIN, HIGH); } else { digitalWrite(LEDPIN, LOW); } } } void setup() { pinMode(LEDPIN, OUTPUT); digitalWrite(LEDPIN, LOW); Serial.begin(115200); dht.begin(); WiFi.mode(WIFI_STA); WiFi.begin(ssid, wifi_password); while (WiFi.status() ! WL_CONNECTED) { delay(500); } client.setServer(mqtt_host, mqtt_port); client.setCallback(callback); } void loop() { if (!client.connected()) { connectMQTT(); } client.loop(); static unsigned long lastMsg 0; if (millis() - lastMsg 5000) { float h dht.readHumidity(); float t dht.readTemperature(); if (!isnan(h) !isnan(t)) { String payload String({\device\:\esp8266-01\,\type\:\sensor\,\temp\:) String(t) String(,\hum\:) String(h) String(,\ts\:) String(millis() / 1000) String(}); client.publish(sensor_topic, payload.c_str(), true); } else { Serial.println(Failed to read from DHT11); } lastMsg millis(); } }代码说明client.connect(esp8266_room1, mqtt_user, mqtt_pass)的三个参数分别是客户端 ID、用户名和密码。如果 Broker 没有开认证后两个参数可以留空但生产环境一定要开否则谁都能控制你的 LED。client.subscribe(led_topic)在连接成功后调用表示只关注 LED 控制指令。publish(sensor_topic, payload.c_str(), true)的第三个参数是 retained 标记不是 QoS。这个true让 Broker 缓存该主题的最新一条消息微信端订阅时立刻就能收到最后的状态。温湿度上报周期我放在5000msDHT11 完全扛得住如果你把它改成 500ms大概率读到NAN。3.3 LED 控制回调与本地自动保护callback里用message.indexOf(\on\)判断是否包含“on”这是最快速的匹配方式不用引入 JSON 解析库。如果微信端发给你的 JSON 字符串中间有空格比如{state: on}这里的indexOf(\on\)也能命中因为on前后有引号作为边界。但只有回调是不够的。我通常会加一层“本地优先”逻辑比如温度超过 30°C 时 LED 自动点亮作为报警此刻忽略远程关机指令。做法是在loop()里判断温度而不是在callback里static bool overheated false; void loop() { // ... if (!isnan(t)) { if (t 30.0) { digitalWrite(LEDPIN, HIGH); overheated true; } else if (overheated t 28.0) { overheated false; } } }注意在callback里加这样一段保护逻辑会造成状态覆盖因为回调触发时t不一定是当前最新温度。所以我习惯把本地保护放在主循环里只在最后让“远程控制”覆盖“本地状态”这样可预测性更强。4. 微信小程序通过 MQTT over WebSocket 远程控制微信小程序不能直接发起 TCP 连接所以要使用 MQTT over WebSocket 才能让小程序与 Broker 通信。一般做法是让 Broker 监听 WS/WSS 端口小程序用 MQTT.js 连接。如果你不想折腾前端证书还可以走“后端订阅 模板消息”的链路但那种方案只能推送通知不能反向控制。4.1 小程序中使用 mqtt.js 连接 Broker先在本地执行npm install mqtt然后把dist/mqtt.min.js拷贝到小程序的utils目录。开发阶段在微信开发者工具里勾选“不校验合法域名”正式发布前再去公众平台配置 Socket 域名。页面核心连接代码如下const mqtt require(../../utils/mqtt.min.js) Page({ data: { temp: --, hum: --, ledOn: false, connected: false }, onLoad() { const client mqtt.connect(wss://user:pass192.168.1.10:8084/mqtt, { clientId: wx_ Math.random().toString(16).substr(2, 8), cleanSession: true, reconnectPeriod: 4000 }) client.on(connect, () { this.setData({ connected: true }) client.subscribe(home/room1/sensor) client.subscribe(home/room1/led) }) client.on(message, (topic, payload) { const msg JSON.parse(payload.toString()) if (topic home/room1/sensor) { this.setData({ temp: msg.temp msg.temp.toFixed(1), hum: msg.hum msg.hum.toFixed(1) }) } else if (topic home/room1/led) { this.setData({ ledOn: msg.state on }) } }) client.on(close, () { this.setData({ connected: false }) }) this.client client }, toggleLed() { const state this.data.ledOn ? off : on this.client.publish(home/room1/led, JSON.stringify({ state }), { qos: 1 }) } })这里wss://user:pass192.168.1.10:8084/mqtt的地址写法是把用户名和密码直接放在 URL 里MQTT.js 解析时会自动拆出来。路径/mqtt是很多 Broker 默认的 MQTT over WebSocket 路径如果你的 Broker 用了自定义路径这里必须改一致。clientId加随机后缀是为了避免多个用户使用同一个固定 ID 互踢。reconnectPeriod建议设在 3000-5000ms。太短会导致弱网环境下疯狂重连反而加重 Broker 负担。cleanSession: true意味着每次上线都是全新的会话不保存遗嘱消息如果你希望离线消息也能收到则需要设为 false 并认真设计session expiry。4.2 WXML 页面绑定与事件处理页面布局不复杂放一个卡片显示温湿度放一个 switch 控制 LEDview classcard text温度{{temp}} °C/text text湿度{{hum}} %/text switch checked{{ledOn}} disabled{{!connected}} bindchangetoggleLed / text wx:if{{!connected}}连接已断开/text /viewbindchangetoggleLed在小程序 switch 组件里回传一个事件对象但我们不关心它的detail.value而是直接读data.ledOn取反。这样保证“先改界面再发指令”避免快速点击多次发送一样的状态。如果你是资深前端想用内置的wx.connectSocket自己封装全套 MQTT那我建议别费那个劲直接用 MQTT.js 省心得多。4.3 没有小程序时微信公众号模板消息桥接如果你暂时不想注册小程序也不要写客户端还可以用公众号模板消息做单向告警。先写一个守护脚本订阅home/room1/sensor温度超过阈值就调用公众号的接口把消息推给粉丝。这个方案不需要 WebSocket代码量更小但只能近实时推送用户无法直接在微信里控制 LED。实现方法很简单Node-RED 里拖一个mqtt in节点再接Function节点判断温度最后接HTTP Request节点调用模板消息接口。后端脚本也可以使用 Python 的paho-mqtt加requests逻辑完全一致。这个思路适合做“告警通知”而不是“远程控制”两个场景别搞混。5. 部署排错与进阶把 zip 里的项目跑成稳定节点的 6 个细节部署顺序建议是先配 Broker再烧录固件最后改小程序。以下是容易出现问题的地方以及排查方法现象排查点参考命令/代码温湿度数据不更新DHT11 接线不稳或采样间隔太短mosquitto_sub -h broker -t home/room1/sensor -vMQTT 每隔几秒断开客户端 ID 冲突或 ACL 拒绝tail -f /var/log/mosquitto/mosquitto.log小程序无法连接证书无效或不支持 wss开发者工具 Network 面板看状态码LED 灯不执行主题不匹配或载荷里没有onmosquitto_pub -t home/room1/led -m {state:on}灯亮一下就灭GPIO 驱动电流不足测量 LED 两端电压确认不低于 1.8V上报数据偶尔错乱DHT11 读取距离上次小于 1 秒把delay改为 1500ms 以上最后一颗进阶棋子我指的是 PWM 调光。普通 LED 的驱动电路只需要一个限流电阻但如果你用 ESP8266 的analogWrite(LEDPIN, brightness)输出 PWMLED 的亮度就会变成平滑可调。微信端小程序发送{state:on,brightness:512}固件在 callback 里解析 brightness 然后执行analogWrite(LEDPIN, brightness)。PWM 频率默认在 1kHz 左右不会产生人耳可闻噪声大多数普通 LED 也扛得住。更复杂的大功率 LED 需要外置恒流驱动芯片但原理仍是 PWM 占空比控制。验证链路打通很快先mosquitto_sub看到温湿度再mosquitto_pub控制 LED最后小程序点击 switch 观察 Broker 日志。稳定性调优时记得给 ESP8266 加看门狗ESP.wdtFeed()放在 WiFi 重连的 while 循环里避免断网时重启卡死。本文还有配套的精品资源点击获取
返回列表