
简介本资源是一套基于ESP8266与DHT11传感器实现远程温湿度监控的嵌入式物联网实践方案面向嵌入式初学者、IoT开发者及高校课程设计者解决环境参数采集、Wi-Fi联网、MQTT协议接入云平台等典型物联网开发痛点。压缩包共46个文件含12个XML配置与工程描述文件、10个WebP格式界面/示意图、4个Java客户端参考代码、3份README说明文档及C主控源码main.cpp、设备信息头文件mqtt_device_info.h和PlatformIO工程配置platformio.ini等核心内容整体仅167KB轻量易部署。已有336人学习下载资源结构清晰覆盖硬件连接逻辑、Arduino风格固件开发、OneNet MQTT鉴权配置、JSON数据封装与云端可视化对接全流程附带完整可编译工程与关键注释便于快速移植、调试与二次开发。 去年帮朋友做一个花房环境监控的小项目选了ESP8266搭配DHT11数据走MQTT上传到OneNet平台。这套方案前前后后跑了两三个月过程中踩了不少坑也总结出一些稳定的配置经验。正好最近后台有不少人问这块我把整个实现过程从零到一梳理一遍包括硬件接线、平台配置、代码逻辑、常见坑点按我的实际操作顺序来写照着做基本能一次跑通。这套东西到底解决什么问题简单说就是让一个十几块钱的WiFi模块加上几块钱的温湿度传感器把环境数据发到云端你打开手机App或者网页就能实时看到温度和湿度变化。适合做花房监控、机房温控提醒、仓库环境记录这类场景。不需要自己搭服务器也不用买昂贵的工业采集器成本控制在三十块钱以内就能实现远程监控。1. 方案选型与整体设计思路1.1 为什么是ESP8266 DHT11 MQTT OneNet这个组合先说硬件端。ESP8266这颗芯片在物联网入门圈子里基本是垄断级的存在原因就三个字便宜、够用、资料多。NodeMCU开发板带USB转串口和稳压电路十几块钱包邮焊接都省了直接用杜邦线就能连传感器。DHT11是入门级的温湿度传感器精度一般但胜在稳定可靠温度精度正负2度湿度精度正负5%对大多数环境监控场景完全够用。MQTT协议的选择也很直观。它基于发布订阅模型不像HTTP那样每次都要建立连接、发送请求、等待响应。对于这种每隔一段时间上报一次数据的场景MQTT的长连接机制能有效减少网络开销和功耗。OneNet是中移物联网旗下的云平台对个人开发者免费MQTT接入文档写得清楚还自带数据可视化看板省去自己写前端页面的麻烦。这套组合的性价比是核心优势。同样功能拿工业级温湿度传感器加DTU模块来做成本得翻个几十倍。而用这套方案硬件成本不超过三十元云平台免费唯一的额外开支就是电费。1.2 整体架构与数据流向整个数据链路可以用一句话概括传感器采集 - 模块处理 - MQTT发布 - 云平台存储展示。DHT11通过单总线协议把温湿度数据传给ESP8266ESP8266在代码里把数据整理成OneNet规定的JSON格式然后通过MQTT协议发布到指定的Topic。OneNet平台收到数据后会自动解析并存档用户在平台的控制台里能看到实时曲线也可以通过平台API对外提供数据服务。这里要强调一个关键设计决策数据格式必须严格按照平台要求拼接。OneNet的MQTT接入对上报数据的格式有固定要求必须包含datastreams和datapoints这两个层级结构否则平台收不到数据。很多第一次接触的人在这里栽跟头代码逻辑看着没问题发布也成功但平台上死活看不到数据八成就是JSON格式不对。1.3 方案对比为什么没选其他方案我一开始其实考虑过Blynk。Blynk的App界面确实好看但免费版有数据点数量限制而且服务器在海外国内访问延迟不稳定。巴法云也试过配置简单但数据存储和展示能力比OneNet弱不少。OneNet最戳我的一点是数据流模板和可视化看板相对完善。你只需要在平台创建好数据流设备上传的数据就会自动形成曲线。另外OneNet的文档中心把MQTT接入的Topic格式、报文格式、鉴权方式写得一清二楚这对于新手来说非常友好。当然OneNet也有槽点比如部分旧版文档和新版控制台对不上API端点变了没及时更新。这个我在后面实测部分会详细说明。2. OneNet平台配置与设备接入准备2.1 创建产品与设备先去OneNet官网注册账号登录后进入控制台。左侧菜单选择多协议接入这里要特别注意选择MQTT协议因为OneNet本身支持HTTP、MQTT、TCP等多种协议每种协议的接入方式和数据格式不一样。点击添加产品填写产品名称、选择设备类型、接入协议选MQTT。创建完成后进入产品详情页记住两个关键信息产品ID和产品APIKey。产品ID是后续MQTT连接时作为username用的产品APIKey是设备鉴权用的代码里作为password。这两个参数格外重要漏一个都连不上。产品创建好后在设备列表里添加设备。设备名称自定义就好比如sensor_01。添加完成后点开设备详情能看到设备ID。这个设备ID在MQTT连接时作为clientId使用。到这里平台侧的准备工作就完成了需要记下来的三要素是产品ID、产品APIKey、设备ID。2.2 MQTT连接参数对照表在写代码之前把MQTT连接需要用到的一组参数列清楚参数值在MQTT协议中的用途服务器地址mqtt.heclouds.com接入服务器域名端口1883MQTT非加密端口clientId设备ID客户端唯一标识username产品ID连接鉴权用户名password产品APIKey连接鉴权密码这组参数在PubSubClient库的connect函数里一一对应少一个或者填错一个都会导致连接失败。特别是clientId、username、password这三个的对应关系和你在平台创建的信息严格绑定设备在平台是孤儿状态还是在线状态就看这几个字段对不对。2.3 数据流模板设置设备创建好后进入控制台的数据流管理需要提前定义数据流模板。这里定义的数据流名称要和后面代码里上传数据时使用的名称完全一致。我的做法是创建两个数据流temperature和humidity。如果你还想监控其他参数比如光照强度、土壤湿度在这里提前加好就行。数据流的单位可以顺手填上摄氏度、百分比这样后面看曲线的时候图表纵轴显示更直观。这里有坑点数据流名称要是大小写不一致平台虽然不会报错但数据会存在另起的一个新数据流里导致你盯的那个数据流一直是空的。所以设置模板时建议把名称写清楚后面代码里复制粘贴不要手动敲。3. 硬件接线与开发环境搭建3.1 DHT11模块接线详解先把模块引脚定义说清楚。市面上常见的DHT11有两种形态一种是四脚的裸传感器一种是集成底板的蓝色方块。后者自带上拉电阻和滤波电容接线更简单建议新手选这种。集成底板的三个引脚定义引脚类型接ESP8266哪个引脚VCC电源正极3V3GND电源负极GNDDOUT数据输出D4GPIO2数据引脚我用的D4。为什么选D4因为GPIO2在NodeMCU板子上默认接了一个LED方便调试的时候观察状态同时这个脚也支持普通的数字输入输出接DHT11没有问题。想换其他引脚也可以代码里把引脚号改掉就行但D1、D2、D5这些引脚在部分NodeMCU板子上可能受SPI或者I2C复用影响不是不能用只是会增加排查难度。接线的时候还要注意DHT11的数据引脚是开漏输出正常工作需要外接一个4.7K到10K的上拉电阻。带底板的模块已经把这个电阻焊好了所以你直接接杜邦线就能用。如果用的是裸传感器记得自己加上拉电阻否则读出来的数据会间歇性出错。供电方面DHT11工作电压范围是3.3V到5.5V所以接NodeMCU的3V3或者VU5V都能正常工作。但有一点要注意如果用5V供电数据引脚会输出5V电平直接怼到ESP8266的GPIO上可能会损坏芯片。最保险的方式是3.3V供电数据引脚电平匹配不用做电平转换。3.2 Arduino IDE开发环境搭建ESP8266的开发环境搭建核心就一步在Arduino IDE里添加ESP8266开发板支持。但国内网络环境下载这个包经常失败我用的是离线安装的方式。具体操作分三步。第一步去GitHub上搜esp8266-arduino离线包下载对应IDE版本的安装包。注意版本要和Arduino IDE匹配我用的是2.7.4版本对应Arduino IDE 1.8.x系列。第二步打开Arduino IDE菜单栏选择文件 - 首选项在附加开发板管理器网址里添加一行http://arduino.esp8266.com/stable/package_esp8266com_index.json。第三步菜单栏选择工具 - 开发板 - 开发板管理器搜esp8266如果能看到列表就说明离线包安装成功了选择NodeMCU 1.0 (ESP-12E Module)就完成环境配置。装好之后建议先跑一个Blink例程验证环境是否正常。点文件 - 示例 - ESP8266 - Blink选好端口直接上传。如果板载LED开始闪烁说明编译器和烧录链路都是通的。3.3 用到的库文件清单代码里需要用到几个第三方库在Arduino IDE的库管理器里直接搜名字安装库名版本用途DHT sensor library1.4.4读取DHT11数据Adafruit Unified Sensor1.1.5DHT库的依赖PubSubClient2.8MQTT客户端ArduinoJson6.x拼接JSON数据这里要提醒一下PubSubClient库默认的MQTT包大小是256字节OneNet要求上传的数据包虽然不大但为了保险起见建议在代码开头把MQTT_MAX_PACKET_SIZE改成1024。不然以后想在数据里加点字段时就会莫名其妙掉线排查半天才发现是缓冲不够。ArduinoJson的版本要注意。v6和v5的API差异很大网上很多教程写的是v5的语法你直接复制过去用会报错。我这篇文章里的代码是v6的写法也是目前Arduino IDE默认安装的新版照着用就好。4. 代码实现从WiFi连接到数据上云4.1 WiFi连接与掉线重连ESP8266上WiFi连接这块我直接用了ESP8266WiFi库自带的WiFiMulti类。它相比直接调用WiFi.begin()的好处是能配置多个备选WiFi主网络挂了自动切备用网络对家庭场景来说足够了。#include ESP8266WiFi.h #include ESP8266WiFiMulti.h ESP8266WiFiMulti wifiMulti; void setupWiFi() { wifiMulti.addAP(你的WiFi名称, 你的WiFi密码); Serial.print(正在连接WiFi); while (wifiMulti.run() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\nWiFi连接成功IP地址: ); Serial.println(WiFi.localIP()); }WiFi连接成功后有两点要留意。第一点是ESP8266的WiFi连接速度和路由器的信道设置关系很大如果路由器开了自动信道模块可能花较长时间才连上这不是代码问题。第二点是如果设备放到墙角等信号不好的地方WiFi会间歇性断开所以后面MQTT循环里要加上重连逻辑我这里采用了定时检查WiFi状态的方式。4.2 MQTT连接与心跳保活MQTT客户端我用的是PubSubClient库。这个库把MQTT的CONNECT、PUBLISH、SUBSCRIBE都封装好了只需要在回调函数里处理收到的消息就行。但这个库有个特性要特别注意它是在loop()函数里被反复调用的必须保证loop()的执行频率稳定否则心跳包发不出去服务器会判定设备掉线。#include PubSubClient.h const char* mqtt_server mqtt.heclouds.com; const int mqtt_port 1883; const char* clientId 你的设备ID; const char* username 你的产品ID; const char* password 你的产品APIKey; WiFiClient espClient; PubSubClient mqttClient(espClient); void reconnectMQTT() { while (!mqttClient.connected()) { Serial.print(正在连接MQTT服务器...); if (mqttClient.connect(clientId, username, password)) { Serial.println(MQTT连接成功); } else { Serial.print(MQTT连接失败错误码: ); Serial.println(mqttClient.state()); delay(5000); } } }关于mqttClient.state()返回的错误码网上能查到对应表但实际调试时最常用的几个-2是网络连接失败-4是用户名密码错误-5是clientId被占用。如果你同时开着两个窗口跑了同一个设备ID的客户端程序后启动的那个会被服务器踢掉所以测试时记得关掉旧的窗口。4.3 DHT11数据读取与容错处理DHT11的读取太频繁没有意义因为这款传感器本身的采样周期就是1秒读太频繁反而会得到相同的数据。而且单总线协议对时序要求比较敏感如果程序里同时有WiFi操作导致的中断延迟可能读到校验错误的数据。#include DHT.h #define DHTPIN 2 // 对应NodeMCU的D4引脚 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); void readSensor() { float h dht.readHumidity(); float t dht.readTemperature(); if (isnan(h) || isnan(t)) { Serial.println(DHT11读取失败); return; } Serial.print(温度: ); Serial.print(t); Serial.print(°C 湿度: ); Serial.print(h); Serial.println(%); }这里用了isnan()做容错这是非常有必要的。DHT库读取失败时会返回NaN不是一个数如果你不做判断直接拿去拼JSON上传平台上会显示一个异常的数据点影响后续的曲线分析。我实际测试时发现DHT11在刚上电的瞬间读取最容易失败所以程序启动后先延时2秒再开始第一次读取。另一个要注意的是DHT11的响应时间。从发送读取命令到数据返回整个通信过程大约4毫秒如果代码里在读取前后插入delay(100)这种长延时没问题但不要在读取过程中插入其他耗时操作否则会读到空数据。4.4 数据格式拼接与主题发布OneNet的MQTT数据上传有两个话题可以使用$dp是数据点上传用于普通数据上报$sys/产品ID/设备ID/dp/post/json是标准化的旧版数据点话题。我实测下来新版控制台推荐使用$dp简化话题兼容性更好。数据格式必须遵循OneNet的规范#include ArduinoJson.h void publishData(float temp, float humi) { StaticJsonDocument256 doc; JsonArray datastreams doc.createNestedArray(datastreams); JsonObject ds_temp datastreams.createNestedObject(); ds_temp[id] temperature; JsonArray values_temp ds_temp.createNestedArray(datapoints); values_temp.createNestedObject()[value] temp; JsonObject ds_humi datastreams.createNestedObject(); ds_humi[id] humidity; JsonArray values_humi ds_humi.createNestedArray(datapoints); values_humi.createNestedObject()[value] humi; char jsonBuffer[256]; serializeJson(doc, jsonBuffer); mqttClient.publish($dp, jsonBuffer); Serial.print(发布数据: ); Serial.println(jsonBuffer); }JSON解析这块是很多人的拦路虎。你需要理解OneNet要求的结构最外层是datastreams数组每个元素代表一个数据流里面有id和datapoints数组datapoints里的每个元素可以有value、time等字段。不要求time字段时不写也没问题平台自动打上接收时间戳。我实际测试时遇到过一个诡异的问题数据明明发布成功了但平台上温度显示正常湿度始终是0。排查了很久发现是JSON格式的问题湿度数据在datapoints数组里多写了一层嵌套导致平台解析失败。如果你也遇到类似情况建议在串口监视器里打印publishData函数生成的完整JSON串和OneNet文档里给的示例逐字符比对。4.5 完整代码整合把所有模块整合到loop()里加上定时上报逻辑unsigned long lastSendTime 0; const unsigned long sendInterval 10000; // 10秒上报一次 void loop() { if (wifiMulti.run() ! WL_CONNECTED) { delay(500); return; } if (!mqttClient.connected()) { reconnectMQTT(); } mqttClient.loop(); if (millis() - lastSendTime sendInterval) { float h dht.readHumidity(); float t dht.readTemperature(); if (!isnan(h) !isnan(t)) { publishData(t, h); } lastSendTime millis(); } }上报间隔我建议至少10秒一次。原因很简单DHT11本身精度有限频繁上报只会让平台存一堆几乎相同的曲线点白白消耗流量。OneNet平台免费版对数据点有一定存储限制如果1秒上报一次几小时就会攒大量数据。10秒一次一天才8640个点一个月也就二十来万免费额度完全够用。如果你后面对数据实时性要求更高可以考虑把DHT11换成DHT22或者SHT30精度和响应速度都会有明显提升。但要提醒你换传感器不是简单换个型号就行DHT库和接线都要做对应调整。5. 实测过程与数据验证5.1 设备首次上线的排查流程所有代码写完烧录进去打开串口监视器波特率设成115200。串口窗口里会依次打印WiFi连接状态、MQTT连接状态和每10秒一次的温湿度读数。如果一切正常你会看到类似这样的输出正在连接WiFi...... WiFi连接成功IP地址: 192.168.1.100 正在连接MQTT服务器... MQTT连接成功 温度: 25.30°C 湿度: 62.00% 发布数据: {datastreams:[{id:temperature,datapoints:[{value:25.3}]},{id:humidity,datapoints:[{value:62}]}]}看到发布数据这行说明数据已经从模块发出去了。此时回到OneNet控制台刷新设备列表设备状态应该从离线变成在线。再点进设备详情切到数据流标签页很快就能看到temperature和humidity两个数据流下面有数据点出现。如果串口里显示MQTT连接失败先检查三要素是否填对特别注意不要多复制了空格。再检查服务器地址OneNet的MQTT地址目前是mqtt.heclouds.com有些旧版文档写的是183.230.40.39那种IP地址那个已经不需要用了。如果MQTT连接成功但平台上没有数据问题大概率出在JSON格式上。用串口打印出来的JSON串和文档示例比对我遇到过的最常见错误是datapoints数组名拼错写成datapoint少了s平台解析时找不到这个字段数据就丢了。5.2 OneNet平台端的数据验证数据上传成功之后可以在控制台看到直观的可视化效果。进入设备管理 - 数据流可以看到每个数据流的实时值和历史记录。点开某个数据流平台自动绘制实时曲线。另外OneNet还提供了API接口你可以用浏览器直接访问以下格式的URL来获取最新数据http://api.heclouds.com/devices/你的设备ID/datastreams/temperature请求时需要在HTTP头里加一个api-key字段值为产品APIKey。返回的JSON里包含温度的最新值和上报时间。这个API接口很有用后如果你想用手机端做自定义展示可以拿这个接口对接其他平台。5.3 稳定性验证与功耗表现这套系统我连续运行了将近两个月。期间遇到过两次WiFi掉线但都触发了重连逻辑自动恢复了不需要人工干预。MQTT连接相对稳定没有出现过因为心跳超时被服务器踢下线的情况。功耗方面NodeMCU开发板因为板载USB转串口芯片和稳压电路一直都在耗电待机电流大概70毫安左右如果直接用USB供电的话一个月电费不到几块钱。但如果想做电池供电的低功耗方案这套板子就不太合适了需要用ESP8266-01S之类模块自己做最小系统加深度睡眠那是另一个话题。6. 常见问题与排查技巧实录6.1 编译阶段常见错误编译错误大部分是库没装对或者版本不兼容。常见的报错是找不到DHT.h、PubSubClient.h这类头文件说明对应库没装。Arduino IDE的库管理器搜索安装后一般会自动安装在用户目录下的libraries文件夹里。另一个高频报错是ArduinoJson的API不兼容。如果你用的ArduinoJson版本是v6我上面的代码可以正常编译。如果你安装的是v5版本会看到一堆关于createNestedArray和createNestedObject的报错。解决办法很简单在库管理器里搜索ArduinoJson安装6.x版本。编译时还会遇到内存溢出的提示这是ESP8266的硬限制。我上面的代码里用StaticJsonDocument256256字节的栈内存对ESP8266来说压力不大。如果你要加更多字段把256改大即可但不要超过512否则运行时会不稳定。6.2 烧录失败的处理NodeMCU出现烧录失败的概率不低而且报错信息五花八门。最常见的是Failed to connect to ESP8266: Timed out waiting for packet header。遇到这个问题先按住开发板上的FLASH键不放再插上USB线等串口识别到设备后点上传看到上传进度开始后再松开FLASH键。这个方法八成能解决。如果还是不行检查USB线很多数据线只能充电不能传数据换一根线试试。还有一个容易忽略的点上传程序时模块的GPIO0引脚不能接外部设备。如果你把DHT11接到了GPIO0上上传会失败或者上传成功后程序不运行。D4对应的是GPIO2不冲突但如果你用了GPIO0记得先把杜邦线拔掉再烧录。6.3 MQTT连接失败与离线排查MQTT连接失败优先看mqttClient.state()的返回值。-2说明网络层没通先确认WiFi连接是否正常可以在代码里打印WiFi.status()和WiFi.localIP()来排查。如果WiFi正常而MQTT还是-2可能是服务器地址解析不出来把服务器地址改回IP地址试试。-4说明服务器拒绝了连接多半是username或者password填错。检查一下产品APIKey注意不要和设备APIKey搞混。OneNet有两个APIKey一个是产品层面的一个是设备层面的MQTT连接的password用的是产品APIKey。-5则是clientId冲突确认当前没有其他设备用同一个设备ID在连接。还有一种情况比较隐蔽MQTT连接成功后loop()里的mqttClient.loop()没有被及时调用。PubSubClient库是靠loop()来维持心跳和收发消息的如果你在loop()里加入了长时间的delay(5000)这样的阻塞操作心跳包就发不出去服务器会认为设备离线并断开连接。所以我在定时上报时用了millis()非阻塞延时而不是delay()。6.4 传感器读数异常DHT11读数出现NaN或者固定不变先看串口监视器。如果每次读数都是NaN大概率是接线问题数据引脚没接对或者模块没供电。按我的接线表重新核对一遍特别注意VCC要接3V3不要接5V。如果读数恒定在某个值比如温度永远是0考虑是不是板子上的上拉电阻坏了或者排线接触不良。最简单的方法是拿万用表量一下数据引脚的电压正常工作时引脚会在0V和3.3V之间跳变如果有万用表可以看到读数在不停变化。另一个常见问题是读数跳变严重比如温度一下25度一下40度。这种情况多是数据引脚和电源线靠太近产生电磁干扰。我的解决方法是把杜邦线换成短一点的线缆或者给模块加一个100nF的滤波电容。6.5 上传数据延迟与丢失如果你发现平台上的数据曲线有明显延迟先看是否是因为WiFi信号弱导致模块和路由器之间的通信速率低下。ESP8266在信号弱的场景下TCP重传机制会显著降低有效吞吐量可以给路由器加一个中继器或者调整模块位置来解决。如果出现数据点间歇性丢失检查代码里上报间隔是否太短。DHT11的读取本身需要时间MQTT发布也需要时间两次上报之间至少留出1秒的缓冲否则可能导致数据错乱。7. 扩展与优化方向这个项目跑通之后能扩展的空间其实挺大的。我这里说几个我后续已经尝试过的方向供你参考。加OLED显示屏是最简单的扩展。DHT11读到的数据除了上传云端还可以同时显示在板载的小屏幕上这样现场也能直接看到数据不用打开手机App。接线只需要把OLED的SDA和SCL接ESP8266的D1和D2代码里加一个U8g2库翻页显示温度和湿度。加报警推送功能也很实用。如果温湿度超出设定范围可以通过OneNet平台的消息推送服务或者接入其他消息通道向手机发送告警。我做过一个版本温度超过35度就通过OneNet平台提醒效果不错。如果你后需要历史数据做分析直接用OneNet自带的API拉取就行。OneNet提供历史数据点查询接口返回JSON格式数据配合脚本或者后端服务做数据分析都很方便。我自己写过一个Python脚本每天定时拉取前一天的数据然后跑一个简单的趋势分析生成日报表发到邮箱。至此这个基于ESP8266和DHT11的远程温湿度监控系统就完整跑通了。从选型到平台配置从硬件接线的细节到代码实现的每个分支包括坑点以上就是完整的操作路径。这套方案最大的价值在于低成本、快速落地后续想加什么功能都是在现有骨架上做增量。本文还有配套的精品资源点击获取