ESP8266+DHT11+MQTT+OneNet:低成本远程温湿度监控实战

ESP8266+DHT11+MQTT+OneNet:低成本远程温湿度监控实战 简介本资源是一套基于ESP8266与DHT11传感器实现远程温湿度监控的嵌入式物联网实践方案面向嵌入式初学者、IoT开发者及高校课程设计者解决环境参数采集、Wi-Fi联网、MQTT协议接入云平台等典型物联网开发痛点。压缩包共46个文件含12个XML配置与工程描述文件、10个WebP格式界面/示意图、4个Java客户端代码支持Android端数据查看、3份README说明文档及C主控逻辑main.cpp、头文件mqtt_device_info.h和PlatformIO工程配置platformio.ini整体仅167KB轻量易部署。已有336人学习下载资源结构清晰分层——include提供设备连接定义src含核心采集与MQTT发布逻辑android_client扩展移动端可视化能力配套说明覆盖OneNet平台注册、主题配置与阈值告警设置要点可直接编译烧录运行是掌握ESP8266MQTTOneNet全链路开发的高实用性入门范例。 最近帮朋友搞了一套花房温湿度远程监控人不在现场又想知道棚里到底热不热、潮不潮。市面上几十块钱的温湿度计只能站在跟前看想远程看数据就得另想办法。我用esp8266搭配DHT11温湿度传感器通过MQTT协议把温度、湿度实时上传到OneNet平台整套成本三十块钱上下数据延迟基本在秒级手机打开OneNet的网页或者App就能看到。这篇文章会把整个链路拆开讲硬件选型怎么定、开发环境哪里容易卡壳、OneNet平台要怎么配置、MQTT连接背后的原理是什么、代码逐段怎么理解、实测中会遇到哪些坑。不是从零教你写代码而是把每一步“为什么这么做”讲清楚。适合有基本Arduino经验、想低成本入门物联网、或者单纯需要远程看温湿度的人参考。1. 为什么是esp8266DHT11MQTTOneNet这个组合1.1 从需求倒推器件选型思路远程温湿度监控这件事拆开看就三个环节现场采集温湿度、把数据发到互联网上、手机或电脑能随时查到。每个环节都有对应的选型逻辑不是随便凑的。采集端选了DHT11。这传感器最大的优势就是便宜、够用、接线简单。它内部走单总线协议占用一个GPIO就能读数据典型工作电压3.3V和ESP8266的逻辑电平正好匹配。精度方面温度误差正负2摄氏度湿度误差正负5%RH放在花房、仓库、机房这种场景完全够用。如果你追求更高精度那得看DHT22或者SHT30成本会翻几倍但这不是本项目的重点。主控和联网选了ESP8266。在这个价位段ESP8266是WiFi接入方案里综合成本最低、资料最全、生态最成熟的选择之一。尤其是NodeMCU和Wemos D1 mini这类开发板板载USB转串口和稳压电路不用自己焊最小系统拿杜邦线一插就能开搞。相比之下STM32虽然也行但你要额外配WiFi模块工程量和调试成本都上去了。传输协议选了MQTT。这里有个容易忽略的点MQTT和HTTP的差别不只是“协议不同”而是通信模型完全不一样。HTTP是请求-响应模式设备每次上报都要建立TCP连接、发送大堆Header服务器处理完再断开。MQTT是发布-订阅模式设备与Broker之间维持一条长连接消息头部极小而且数据一到Broker就能实时推给所有订阅者。对ESP8266这种RAM只有160KB级别、还要考虑功耗的设备来说MQTT明显更合适。云平台选了OneNet。国内几个主流物联网平台里OneNet对个人开发者比较友好免费额度对传感器上报这种低频数据够用注册之后创建产品和设备就能接入而且自带图表展示、触发器告警不用自己从头画可视化界面。1.2 这套方案到底适合哪些场景适合的场景其实挺广花房、大棚、仓库、档案室、机房需要远程看温湿度趋势和超限告警家里养宠物、热带鱼、多肉植物人出差时想知道环境有没有异常学生做毕业设计或物联网入门项目想完整走一遍传感器采集到云端展示的链路不适合的场景也要说清楚需要计量级读数精度的场景别用DHT11建议换DHT22、SHT30或更高级的传感器断网也要能本地控制的场景这套方案依赖云端网络一断就“瞎”了纽扣电池跑一年那种超低功耗场景ESP8266的功耗偏高得换ESP32或者加深度休眠策略1.3 项目的技术全景图把整个项目拆开看其实是四段链路的串联DHT11采集温湿度 - ESP8266读取并处理数据 - MQTT协议通过WiFi上报OneNet - OneNet控制台/API展示数据这四段各自都有独立的坑。硬件采集端要处理传感器接线和读取时序问题设备端程序要处理WiFi连接和MQTT长连接的稳定性问题平台接入要搞懂OneNet的设备鉴权方式和Topic格式最后展示端要理解数据流和API的用法。后面几章就按这个链路顺序展开。2. 硬件选型与接线开发板怎么选DHT11四条腿怎么接2.1 esp8266开发板怎么选市面上的ESP8266开发板形态五花八门但常见的大概就三类我直接放个对比表开发板优点缺点适合人群NodeMCUV3 LoLin引脚标注齐全、板载USB转串口、稳压电路、价格便宜体积稍大约等于一个U盘大小绝大多数初学者Wemos D1 mini体积更小、也能直接插USB、能配外壳引脚少一些排针要自己焊做小盒子、嵌入式安装ESP-01s裸模块最便宜、体积最小、省空间GPIO只有2个、需要USB转TTL模块烧录、接线麻烦已经跑通流程后做微型设备第一次做项目的朋友我真心建议直接NodeMCU或者Wemos D1 mini别图便宜选ESP-01s。ESP-01s光是把固件烧录进去就要折腾半天要拉低GPIO0、要用外部串口模块GPIO还少得可怜调试体验会劝退不少人。2.2 DHT11引脚定义与接线方式DHT11常见有两种形态一种是四针直插的裸传感器VCC、DATA、NC、GND一种是已经焊好上拉电阻的三针模块VCC、DATA、GND。接线方式如下VCC - 3.3V别接5V虽然DHT11标称3.3-5V都能工作但ESP8266的GPIO是3.3V逻辑电平统一用3.3V最稳DATA - GPIO4NodeMCU开发板上丝印标注为D2NC - 悬空GND - GND这里有个很多人踩过的坑DATA引脚不要选GPIO0NodeMCU上标注D3。因为GPIO0在ESP8266启动时是决定是否进入下载模式的关键引脚接了传感器可能会影响上电启动。同理GPIO15D8也被内部下拉电阻占用不适合做输入。最省心的选择是GPIO4或GPIO5。关于上拉电阻DHT11的单总线协议要求数据线空闲时保持高电平所以需要在DATA和VCC之间接一个4.7k到10k的上拉电阻。如果你买的是模块版电阻已经焊在板子上了直接连就好。如果你买的是裸传感器这个电阻一定要加否则读取会频繁失败。顺便说一句热搜词里有人提到“dht11原理图嘉立创画图”。如果你打算用嘉立创EDA画一块自己的小PCB去元件库搜DHT11把上述四根引脚和上拉电阻补齐就行原理图本身很简洁难点其实在封装和布局VCC和GND间建议加一个0.1uF的退耦电容靠近传感器放置。2.3 供电与长线连接的稳定性供电是整个项目里最容易被低估的一个环节。ESP8266的WiFi射频在发射瞬间电流会飙升到200mA以上如果你用劣质USB线供电线材内阻大压降严重芯片会随机重启表现就是运行几分钟就自动复位、串口输出乱码。我的建议是用5V/1A以上的充电头加一根质量好的USB线。如果要把设备放到离插座远的位置可以买一根短线延长线比一堆劣质长线稳定得多。DHT11和数据引脚的连接线不要太长超过20厘米就容易受干扰读到NaN的概率会明显增加。如果实在无法缩短距离可以用双绞线连接并在传感器那边的VCC和GND之间加一个0.1uF电容。3. 开发环境里最容易卡壳的三个环节Arduino IDE、离线包、固件烧录3.1 Arduino IDE版本与ESP8266支持包我用的是Arduino IDE 1.8.19配ESP8266 Board Package 2.7.4。这套组合我用了很久非常稳定。新版Arduino IDE 2.x也能用但如果你只想尽快跑通项目1.8.x加离线包是性价比最高的路径。开发板支持包的安装常规操作是打开“文件 - 首选项 - 附加开发板管理器网址”填入http://arduino.esp8266.com/stable/package_esp8266com_index.json然后“工具 - 开发板 - 开发板管理器”搜esp8266点击安装。但这里就是很多人卡住的地方国内网络访问这个地址经常超时开发板管理器一直转圈最终安装失败。解决办法是用离线安装包。网上能找到esp8266 board package的离线下载下载后放到Arduino IDE的staging目录Windows下一般是C:\Users\你的用户名\AppData\Local\Arduino15\staging\packages然后回开发板管理器再触发一次安装IDE就会发现本地缓存并直接安装不再走外网。如果你找不到对应版本的staging目录还有一个办法直接把离线包解压到Arduino15\packages\esp8266\hardware\esp8266\版本号目录下重启IDE也能识别。这个我实测过相比在线安装离线方案胜在稳定。3.2 开发板参数设置安装完支持包接下来是“工具”菜单里的开发板参数。不同开发板选型不同我以NodeMCU为例参数推荐值说明开发板NodeMCU 1.0 (ESP-12E Module)对应ESP8266 4MB Flash版本Flash Size4M (1M SPIFFS)程序空间足够留1M给文件系统Upload Speed115200如果失败可以降到9600或57600CPU Frequency160MHz对WiFi响应更稳定默认80也能跑波特率115200要与程序里的Serial.begin保持一致选好参数后点击上传IDE会先编译再烧录。烧录失败最常见的三个原因串口选错了进设备管理器看是COM几、CH340驱动没装、上传瞬间没让板子进入下载模式。NodeMCU板载了自动下载电路一般不需要手动按键但有些山寨版的自动下载电路不稳定遇到这种情况上传时按住板载FLASH键再点上传看到开始烧录再松手。3.3 驱动问题与esptool备用方案电脑识别不了串口时十有八九是USB转串口芯片驱动问题。NodeMCU和Wemos D1 mini用的基本都是CH340G少数老款用CP2102。去官网下载对应驱动装好如果设备管理器里还是感叹号尝试换一根USB线、换个USB口或者重启电脑。如果你遇到Arduino IDE上传一直失败但又要救砖或者清空Flash可以依赖esptool.py。这是乐鑫官方开源的烧录工具命令行方式操作基本命令如下esptool.py --port COM3 --baud 115200 write_flash 0x00000 你的固件.bin参数里--port指定串口--baud是波特率0x00000是Flash起始地址。日常用Arduino IDE上传程序并不需要手动跑esptool它主要用来处理两类情况一是设备反复重启、串口乱码可以先执行esptool.py --port COM3 erase_flash把Flash清空重新烧录二是你需要自己编译固件然后直接刷bin文件时。3.4 需要安装的第三方库Arduino IDE里还需要装三个核心库PubSubClientMQTT客户端库作者是KnollearyDHT sensor libraryAdafruit出品Adafruit Unified SensorDHT库的依赖库这三个在库管理器里直接搜名字就能装。如果下载慢同样可以在GitHub上找zip包然后“项目 - 导入库 - 添加.zip库”手动安装。注意Adafruit的DHT库还有一个比较老的版本叫DHTlib两者API完全不同本项目用Adafruit的DHT sensor library。4. 打通OneNet前必须搞懂的MQTT细节产品、设备、Topic与数据流4.1 MQTT协议的核心概念为什么物联网场景选它MQTT的全称是Message Queuing Telemetry Transport消息队列遥测传输。它的通信模型是发布-订阅中间有个Broker消息代理负责转发客户端可以发布消息到某个Topic也可以订阅某个Topic来接收消息。这个模型和传统的Socket直接连发数据有两个重要区别第一解耦。发布者不需要知道订阅者是谁订阅者也不需要时刻在线才能收到消息。自己搭一个裸TCP Socket服务端客户端断线重连、消息路由、多客户端同时接收这些事都得自己实现这复杂度远远高于业务本身的复杂度。第二轻量且支持长连接。MQTT的最小控制报文头只有2个字节而HTTP光请求头就要几百字节。对ESP8266这种设备来说每次HTTP请求重新建连的开销太大而MQTT长连接建好之后后续每条消息的额外开销非常小。另一个容易混的概念是QoS等级。PubSubClient默认用的QoS 0意思是“最多发一次不保证送达”。对温湿度上报这种周期性数据QoS 0足够丢一帧下一帧就补回来了。如果是告警数据或设备控制指令可以考虑QoS 1至少一次但代价是可能收到重复消息需要应用层做去重。4.2 OneNet平台配置产品和设备的创建流程登录OneNet控制台第一步创建产品。产品类别选“环境监测”或“智能家居”都可以关键是接入协议一定要选MQTT。OneNet老平台的“多协议接入”和新的OneNet Studio在操作路径上有差异但核心概念是共通的。我先讲通用流程创建产品后你会得到一个产品ID。然后在产品下添加设备给设备起名添加完成后会生成设备ID和APIKey。这三个字符串的作用要分清产品ID产品级唯一标识设备接入时的归属标识设备ID设备唯一标识MQTT连接时作为ClientIDAPIKey设备密钥MQTT连接时的密码同时也是调用OneNet HTTP API时的鉴权凭证把它们三个分别存好后续代码里全要用。注意不要截图发微信群或者贴到公开仓库里APIKey泄露等于别人可以拿你的设备额度上报数据。4.3 MQTT接入地址、端口与Topic格式OneNet老版多协议接入的MQTT地址是域名mqtt.heclouds.com端口是6002。这是一个非常关键的坑OneNet的MQTT端口不是默认的1883而是6002。很多人第一次连接失败就是端口写错了。设备接入OneNet时MQTT的ClientID必须填设备ID用户名填产品ID密码填APIKey。这与很多MQTT Broker默认只验证用户名密码的做法不太一样但PubSubClient的connect方法本身就支持传这三个参数直接填进去即可。上报数据点的Topic是$dp这是OneNet规定的数据点上报主题。数据要通过JSON格式发送格式如下{ datastreams: [ { id: temperature, datapoints: [ { value: 25.5 } ] }, { id: humidity, datapoints: [ { value: 60 } ] } ] }这里的id就是数据流名字。OneNet平台收到消息后会自动为设备和数据流建立数据点你可以在设备详情页的数据流列表里看到temperature和humidity。注意JSON字段的大小写不能错是datastreams不是datastream是datapoints不是datapoint。新版OneNet Studio的接入地址和Topic格式可能略有不同如果你用的是新平台以官方文档为准。但老版多协议接入的这套流程依然有大量存量用户在跑。4.4 OneNet的HTTP API调用如果不想每次打开网页看数据OneNet还提供了HTTP API用来查询设备状态、数据点、命令下发等。举个查询设备最新数据点的例子GET /devices/{device_id}/datapoints?datastream_idtemperature HTTP/1.1 Host: api.heclouds.com api-key: 你的APIKey请求头里把api-key换成你的APIKey返回的JSON里就是该数据流的数据点数组。用Postman、curl、或者Python脚本都能调。这个API适合做定时抓数、自动生成报表、或者接第三方通知系统。5. 主程序逐段拆解连接、采样、上云的状态机设计5.1 程序整体结构代码量不大但结构上我会分成四个清晰的部分WiFi连接、DHT11采样、MQTT连接与保活、数据发布。核心思想是loop里同时维护这几件事而不是在一个大delay里串行执行所有操作。下面是一份完整可用的示例代码基于ESP8266和Arduino#include ESP8266WiFi.h #include PubSubClient.h #include DHT.h const char* ssid 你的WiFi名; const char* password 你的WiFi密码; const char* mqtt_server mqtt.heclouds.com; const int mqtt_port 6002; const char* product_id 你的产品ID; const char* device_id 你的设备ID; const char* api_key 你的APIKey; #define DHTPIN 4 // GPIO4对应NodeMCU的D2 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); WiFiClient espClient; PubSubClient client(espClient); unsigned long lastPublish 0; const unsigned long publishInterval 30000; // 30秒上报一次 void setup_wifi() { delay(10); Serial.println(); Serial.print(Connecting to ); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(); Serial.println(WiFi connected); } void reconnect_mqtt() { while (!client.connected()) { Serial.print(Attempting MQTT connection...); if (client.connect(device_id, product_id, api_key)) { Serial.println(connected); } else { Serial.print(failed, rc); Serial.print(client.state()); Serial.println( try again in 5 seconds); delay(5000); } } } void publish_sensor_data() { float h dht.readHumidity(); float t dht.readTemperature(); if (isnan(h) || isnan(t)) { Serial.println(Failed to read from DHT sensor!); return; } String json {; json \datastreams\:[; json {\id\:\temperature\,\datapoints\:[{\value\:; json String(t, 1); json }]},; json {\id\:\humidity\,\datapoints\:[{\value\:; json String(h, 1); json }]}; json ]}; Serial.print(Publish message: ); Serial.println(json); if (client.publish($dp, json.c_str())) { p a hrefhttps://download.csdn.net/download/weixin_44010641/89268252 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p