ARTICLE DETAIL

资讯详情

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

Arduino+ESP8266+DHT11接入OneNet:温湿度上云全流程与排错指南

Arduino+ESP8266+DHT11接入OneNet:温湿度上云全流程与排错指南 搞物联网的第一个项目十个人里有八个会选温湿度上云而“Arduino DHT11 ESP8266 OneNet”这个组合基本就是中文互联网上出现频率最高的一套配方。但有意思的是很多人照着教程接线、烧码、配置最后卡住的点五花八门——串口监视器一直打印255、ESP8266的AT指令没反应、OneNet平台上设备永远离线、数据点传上去了平台却显示不出来……我自己也是从这一套组合摸爬滚打过来的。这篇不打算复述那些你已经能搜到的“复制粘贴就能跑”的教程而是把这条链路从头到尾拆开从硬件选型和接线到OneNet平台侧该准备什么再到DHT11的底层时序、HTTP上报的数据格式最后是几个我用血泪换来的排查经验。适合刚入手Arduino和ESP8266的玩家也适合拿这个组合做课设、做毕设的在校生。看完你不仅能跑通还能知道跑不通时问题到底出在哪一环。1. 硬件方案怎么选NodeMCU直连与UNOAT透传的区别先把标题里“Arduino”这个概念掰扯清楚。很多人误以为必须有块Arduino Uno板子再外接一个ESP8266模块才能完成“传感器读取 WiFi传输”两件事。实际上你手上那块ESP8266 NodeMCU开发板本身就是一块“自带WiFi的Arduino”——它用的是ESP8266芯片但你在Arduino IDE里写完代码烧进去完全没问题。所以这个项目天然有两条路线。1.1 方案ANodeMCU/ESP8266开发板直接跑Arduino代码这是我目前最推荐新手走的路一块NodeMCUESP8266核心的板子带USB口大概十几块钱一个DHT11模块三根杜邦线就完事。接线如下DHT11的VCC接NodeMCU的3V3DHT11的GND接NodeMCU的GNDDHT11的DATA接NodeMCU的D4也就是GPIO2理由很简单NodeMCU板载了USB转串口芯片供电和下载都在一根线上不需要额外折腾。DHT11的数据引脚内部有上拉电阻部分模块板自带上拉接在GPIO2上比较稳。3.3V供电对DHT11完全够用它本身支持3.3V~5.5V宽压输入只是线太长的时候时序会受影响这个后面细说。1.2 方案BArduino Uno 独立ESP8266模块AT固件透传如果你手上已经有Uno和一块裸的ESP8266模块比如ESP-01那走这条路也行但坑明显更多。整体思路是Uno负责读DHT11然后通过SoftwareSerial串口向ESP8266发AT指令让ESP8266去和OneNet云平台通信。接线方式以ESP-01为例信号ESP-01引脚接去哪VCCVCC外部3.3V电源不要用Uno的3.3V输出带GNDGND与Uno共地TXTXUno的数字引脚2SoftwareSerial RXRXRXUno的数字引脚3SoftwareSerial TXEN/CH_PDEN接3.3V必须拉高否则模块不工作GPIO0GPIO0悬空或接3.3V接GND是下载模式这里面最容易翻车的点就是供电。ESP8266在WiFi发射瞬间的峰值电流能到300mA甚至更高Uno板载的3.3V稳压芯片通常是AMS1117-3.3输出能力很勉强实测在WiFi连接瞬间电压会被拉垮表现就是模块反复重启、AT指令时有时无或者直接无响应。我建议单独用一个AMS1117-3.3模块或者手机充电头降压模块给ESP8266供电Uno和ESP8266之间只共地。1.3 为什么我后来放弃了AT透传方案AT指令方案不是不能用但调试体验确实差。你要在一堆printf里拼字符串还要手动解析ESP8266返回的IPD数据一旦服务器返回内容多了SoftwareSerial的缓冲区不够响应就被截断你根本分不清是网络问题还是代码问题。而NodeMCU直连方案里ESP8266HTTPClient库把HTTP请求封装得很干净代码结构清楚出问题好排查。如果你是全新购入直接买NodeMCU如果手里已经有Uno和ESP-01那也可以先按方案B跑通理解AT指令的工作方式会对你后面做别的项目有帮助。本文后面以上云代码部分以方案A为主线。2. OneNet平台端准备产品、设备、APIKey与数据流的关系很多教程默认你已经会注册平台了直接甩给你一段代码。但实际数据显示这个项目一半以上的问题出在平台配置和代码里的参数不匹配。先把OneNet平台的几个概念理清楚你后面填代码才有底气。2.1 创建产品和设备时该注意的选项打开OneNet官网注册登录后进入开发者中心。创建一个产品时有这几个选项容易让人纠结产品名称随便填比如“温室大棚监测”产品类型选“温湿度计”这个模板就行没有的话选“自定义”联网方式选“WiFi”数据协议选“HTTP”或“MQTT”都可以看你想用哪种方式上报。HTTP适合轮询式上报MQTT适合需要平台下发指令的场景创建完成后在“设备管理”里添加设备。设备名称随便填但设备编号Device ID要记牢这个ID在代码里要直接用。设备标识号IMEI等不用填OneNet允许为空。2.2 APIKey不是设备密钥别搞混这是新手最容易懵的地方。OneNet平台里有两种“密钥”APIKey用于HTTP接口调用的鉴权相当于你的“云平台登录凭证”在代码里要放在HTTP请求头的api-key字段里设备级接入密钥/密码用于设备与平台的连接鉴权MQTT方式会用到在经典版OneNet平台里APIKey在产品详情页或“密钥管理”里生成格式是一长串十六进制连字符的字符串。新版OneNET Studio的入口和名称略有变化但逻辑是一样的——你在平台里找到“鉴权信息”那一栏能看到APIKey和ProductID、DeviceID。这里强烈建议你开一个浏览器标签页把ProductID、DeviceID、APIKey三个值复制到记事本里保存好。后面代码里填错任何一个字符表现都是平台收不到数据而代码本身编译运行都没问题排查起来非常痛苦。2.3 HTTP上报时的数据流概念OneNet的经典HTTP上报接口是这样的POST http://api.heclouds.com/devices/{device_id}/datapoints?type3这个接口的body里要传一个JSON告诉平台你要往哪些“数据流”datastreams里写数据{ datastreams: [ { id: temp, datapoints: [ { value: 25.5 } ] }, { id: humi, datapoints: [ { value: 60.1 } ] } ] }这里面的id就是数据流名称。你可以先去平台端手动创建一个数据流叫temp也可以不建直接上报平台会自动根据上报的id生成数据流。我建议手动创建这样你的数据流名称、单位、图表配置可以提前设好。上报成功后平台返回的body里会有一个errno字段errno为0才算成功这一点很多教程没说导致有人看到HTTP 200就以为成功了其实数据可能根本没写进去。2.4 先用curl验证平台配置别急着写单片机代码在烧录之前我强烈建议你用电脑上的命令行工具curl先测一下OneNet接口通不通。这步做好了后面单片机的排查范围至少砍掉一半。Windows在cmd或PowerShell里、macOS/Linux在终端里执行curl -X POST \ http://api.heclouds.com/devices/你的设备ID/datapoints?type3 \ -H api-key: 你的APIKey \ -H Content-Type: application/json \ -d {\datastreams\:[{\id\:\temp\,\datapoints\:[{\value\:25.5}]}]}如果返回内容里包含errno:0说明平台配置、APIKey、设备ID全部没问题。这时候再去折腾单片机心里就有底了。3. DHT11读数的底层时序与Arduino代码实现DHT11虽然便宜但它用的是单总线协议——就靠一根数据线既接收主机的触发信号又往主机回传数据。不明白这个协议的人代码里随便抄个库就开始用一旦读数不对劲根本不知道问题出在哪。3.1 40位数据帧和“0/1”的区分方式DHT11回传的数据一共有40个bit格式是8bit湿度整数部分8bit湿度小数部分8bit温度整数部分8bit温度小数部分8bit校验和校验和的算法是前面四个字节相加取低8位如果等于校验字节说明这一帧数据有效。比如湿度50.0%温度26.5℃那校验值就是0x32 0x00 0x1A 0x05 0x51。这些bit本身不是用高低电平来表示的而是用高电平持续的时间长度50us低电平起始位之后如果高电平持续26~28us代表“0”如果持续70us左右代表“1”。所以读取的核心技巧就一句话测量每一位中高电平的宽度。3.2 手动读取DHT11的示例代码用于理解时序Arduino生态里常用的DHT库其实封装好了这一切但在用库之前我建议你先把这段代码跑一遍它能帮你理解时序的本质#define DHTPIN 4 // 数据引脚接D2也行 void setup() { Serial.begin(9600); pinMode(DHTPIN, INPUT_PULLUP); } void loop() { uint8_t data[5] {0, 0, 0, 0, 0}; // 1. 主机拉低至少18ms发出起始信号 pinMode(DHTPIN, OUTPUT); digitalWrite(DHTPIN, LOW); delay(20); // 2. 拉高20~40us释放总线 digitalWrite(DHTPIN, HIGH); delayMicroseconds(30); // 3. 切换为输入等待DHT11响应 pinMode(DHTPIN, INPUT_PULLUP); // 4. 等待响应信号先拉低80us再拉高80us while (digitalRead(DHTPIN) HIGH); while (digitalRead(DHTPIN) LOW); while (digitalRead(DHTPIN) HIGH); // 5. 读取40位数据 for (int bit 0; bit 40; bit) { while (digitalRead(DHTPIN) LOW); // 跳过50us低电平 unsigned long highStart micros(); while (digitalRead(DHTPIN) HIGH); // 等待高电平结束 unsigned long highTime micros() - highStart; if (highTime 40) { data[bit / 8] | (1 (7 - (bit % 8))); } } // 6. 校验 uint8_t checksum data[0] data[1] data[2] data[3]; if (checksum data[4]) { Serial.print(temp ); Serial.print(data[2]); Serial.print(.); Serial.print(data[3]); Serial.print( C, humi ); Serial.print(data[0]); Serial.print(.); Serial.println(data[1]); } else { Serial.println(checksum error); } delay(2000); // DHT11两次读取间隔至少1秒建议2秒 }这段代码的关键在于用micros()精确计时高电平宽度超过40us就当作“1”。跑通这段你就懂得DHT11数据的底层逻辑了后面万一库出问题你也知道该怎么救。3.3 实际项目中直接使用库DHT sensor library手动读取适合理解原理实际项目里还是用库更稳。Arduino IDE的库管理器里搜索“DHT sensor library”作者Adafruit这个库依赖“Adafruit Unified Sensor Lib”一并安装。用库的代码很清爽#include DHT.h #include DHT_U.h #define DHTPIN 4 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(9600); dht.begin(); } void 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(Humidity: ); Serial.print(h); Serial.print(% Temperature: ); Serial.print(t); Serial.println(C); } delay(2000); }3.4 读不到数据的三大纪律我实测下来的经验DHT11读数失败90%是下面三个原因两次读取间隔必须大于1秒最好2秒。DHT11内部是慢速元件读完后需要时间稳定你连续读取就返回NAN。很多人的代码里没注意delay(2000)或者把读DHT11和上报的循环频率设到500ms必然出问题。上电后第一次读取一定失败或数据异常。DHT11上电后要等1秒以上才能稳定建议setup()里先delay(1500)再开始读。数据线别太长。DHT11的时序是微秒级的线长超过20cm或者用了劣质杜邦线波形边沿变缓读出的校验和经常会挂。如果你必须长距离传输数据线上并联一个4.7kΩ上拉电阻到VCC会好很多。4. 数据上云的核心链路HTTP上报的URL、JSON与鉴权细节把DHT11的读数读出来只是第一步真正的核心是把数据推上OneNet。方案A里用的是ESP8266HTTPClient库代码不长但只要有一个细节不对数据就上不去。4.1 数据上报的完整NodeMCU代码#include ESP8266WiFi.h #include ESP8266HTTPClient.h #include WiFiClient.h #include DHT.h #define DHTPIN 4 #define DHTTYPE DHT11 const char* ssid 你的WiFi名; const char* password 你的WiFi密码; const char* deviceId 你的设备ID; const char* apiKey 你的APIKey; DHT dht(DHTPIN, DHTTYPE); WiFiClient wifiClient; void setup() { Serial.begin(115200); dht.begin(); WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(WiFi connected); delay(1500); // 等DHT11稳定 } void loop() { float h dht.readHumidity(); float t dht.readTemperature(); if (isnan(h) || isnan(t)) { Serial.println(DHT read failed, skip upload); delay(2000); return; } // 构造OneNet要求的JSON格式 String jsonStr {\datastreams\:[; jsonStr {\id\:\temp\,\datapoints\:[{\value\:; jsonStr String(t, 1); jsonStr }]},; jsonStr {\id\:\humi\,\datapoints\:[{\value\:; jsonStr String(h, 1); jsonStr }]}; jsonStr ]}; String url http://api.heclouds.com/devices/; url deviceId; url /datapoints?type3; HTTPClient http; http.begin(wifiClient, url); http.addHeader(Content-Type, application/json); http.addHeader(api-key, apiKey); int httpCode http.POST(jsonStr); String response http.getString(); Serial.print(HTTP code: ); Serial.println(httpCode); Serial.print(Response: ); Serial.println(response); http.end(); delay(5000); // 上传频率5秒一次比较安全 }4.2 这五个细节决定你能不能成功把这些点列出来是真的每一个都让我踩过坑第一URL必须以/datapoints?type3结尾type3表示请求体是JSON格式。有些人抄的教程用的是type1或type2那是老版本或者二进制格式格式不对平台直接忽略你的数据。第二APIKey放在Header里别拼到URL后面。有人习惯把key拼在URL上在OneNet这里也行不通。http.addHeader(api-key, apiKey)是标准写法注意api-key这个Header名大小写无所谓但冒号后面不能有空格。第三JSON里的value不能加引号。写成value:25.5平台会认为数据流类型是字符串而不是数字图表里就无法显示曲线。我见过太多人在拼字符串时顺手加了双引号。第四float转字符串时用String(t, 1)保留一位小数既满足DHT11实际精度它本身精度只有±2℃和±5%RH也避免出现一长串无意义的小数。第五每传完一次要http.end()释放连接资源否则长时间运行后NodeMCU会因为内存碎片或连接句柄耗尽而假死。4.3 上传频率别太激进OneNet对设备上报频率有一定限制普通免费用户单设备稳定运行建议5秒以上一次。而且DHT11本身两次读取必须间隔1秒以上所以你在loop()里delay(5000)完全够用没必要追求更高频。真要高频就该换DHT22或者SHT30这类传感器了。4.4 方案B的AT指令方式长什么样如果你走的是UnoESP8266透传路线上报的逻辑就是先用ATCIPSTARTTCP,api.heclouds.com,80建立TCP连接再用ATCIPSEND长度告诉模块要发送多少字节接着直接把HTTP请求字符串发出去ATCWMODE1 ATCWJAP你的WiFi名,你的WiFi密码 ATCIPSTARTTCP,api.heclouds.com,80 ATCIPSEND280 POST /devices/你的设备ID/datapoints?type3 HTTP/1.1 Host: api.heclouds.com Content-Type: application/json api-key: 你的APIKey Content-Length: 119 {datastreams:[{id:temp,datapoints:[{value:25.5}]},{id:humi,datapoints:[{value:60.1}]}]}注意结尾一定要多一个换行HTTP/1.1协议靠空行连续两个回车换行来标记Header结束你漏了这个服务器会一直等你的Header结束直到超时表现为TCP连接迟迟不返回数据。这个Content-Length要算得准多一个少一个字节都会导致服务器解析出错。5. MQTT方式接入OneNet备选路径与适用场景HTTP上报是新手成本最低的方案但如果你后面要做命令下发比如远程控制继电器、保持长连接、实时双向通信MQTT是更合适的协议。5.1 HTTP和MQTT的选择对比对比维度HTTP上报MQTT接入连接方式每次上报新建连接一次连接长连接保持实现复杂度简单HTTPClient库几行搞定需要维护连接状态和心跳消息下发不支持只能轮询支持平台可主动下发OneNet支持经典HTTP接口稳定新版Studio使用标准MQTT 3.1.1适合场景数据展示、图表记录设备控制、实时响应、告警推送我个人的建议是先按HTTP把数据传到云端的链路跑通理解“设备—平台—数据流”的整体模型再考虑MQTT。直接上MQTT容易同时面对协议解析和平台配置两个问题消化不了。5.2 使用PubSubClient库连接OneNet的基本思路如果你决定用MQTT代码框架大致是这样的#include ESP8266WiFi.h #include PubSubClient.h const char* mqttBroker 你的MQTT服务器地址; // 以OneNet平台“设备接入”文档为准 const int mqttPort 你的MQTT端口号; const char* clientId 你的设备ID; const char* username 你的产品ID; // OneNet MQTT的用户名规则 const char* password 你的设备接入密钥; // 不是APIKey WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); void setup() { // WiFi连接代码略 mqttClient.setServer(mqttBroker, mqttPort); } void loop() { if (!mqttClient.connected()) { mqttClient.connect(clientId, username, password); } mqttClient.loop(); // 上报数据时将JSON数据发布到数据流对应的Topic // 具体Topic路径以OneNet平台的MQTT文档为准 mqttClient.publish(数据流Topic, jsonStr.c_str()); }这里我刻意没有给出具体的Broker地址和Topic路径因为OneNet不同版本之间差异很大官方文档也时常调整。你注册登录后在“设备接入”或“设备详情”页面能找到当前版本的MQTT接入信息包括Broker地址、端口、ClientID、Username、Password的规则和Topic构造方式直接复制到项目里即可。凭记忆填旧版信息大概率连不上。5.3 MQTT调试的小工具MQTTX看到热搜词里有人在搜“MQTTX连接OneNet”顺便提一下。MQTTX是一个跨平台的MQTT客户端调试工具图形化界面支持填入Broker、端口、用户名密码后快速连接并收发消息。在你写单片机代码之前我建议先用MQTTX试连一下OneNet的MQTT服务器能连上、能Published一条消息就说明平台侧的参数是对的再回头查代码。这和前面用curl验证HTTP接口是同一个思路——先把平台链路打通不要把锅全甩给单片机代码。6. 实测中卡的几道关从串口到云端的完整排查链路最后这部分我把这几年帮人排查类似问题遇到的高频故障整理成一条排查链路。你严格按照这个顺序走绝大多数问题都能在十分钟内定位。6.1 第一关串口监视器里的读数正常吗无论你用什么方案先把云平台相关的代码注释掉只让板子读DHT11并在串口打印。这是最基础的隔离测试。如果串口输出的是nan或者乱码255先确认接线VCC、GND、DATA三根线有没有插反DHT11模块上的丝印有没有看清。我见过太多人把DATA插到VCC上读出来的永远是0xFF。确认库装好了没Arduino IDE里#include DHT.h能不能编译通过库管理器里搜“DHT sensor library”看看有没有装。确认代码里delay(2000)存在去掉或者太短DHT11会不稳定。换一个DHT11测试这玩意儿出厂一致性一般坏片率不低我踩过三次新传感器就是坏的坑。6.2 第二关ESP8266能不能稳定联网把DHT11代码注释掉只保留WiFi连接和串口打印WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.println(Connecting...); } Serial.println(WiFi connected); Serial.println(WiFi.localIP());如果卡在“Connecting...”不动检查WiFi名和密码是否错误、路由器是否开了AP隔离、2.4G频段是否可用。NodeMCU只支持2.4GHz连不上5G频段的路由器是正常的。如果你是裸ESP8266模块走AT指令先看最基本的AT能不能返回OK。不返回的话按这个顺序查EN脚有没有拉高、供电电流够不够前面说过AMS1117的事、串口波特率对不对常见9600、115200有些模块烧录的是74880你可以三个都试一遍、RX/TX有没有接反ESP8266的TX接Arduino的RX别接成同向。6.3 第三关OneNet平台为什么收不到数据代码和网络都没问题但平台侧就是没有数据点按这个顺序排查先用curl或Postman手动请求一次接口确认errno为0。这一步直接验证了APIKey、设备ID、数据流ID是否正确。curl报错401、404的话回到2.4节重新核对参数。看代码里的URL拼接结果。在http.begin()之前加一句Serial.println(url)把完整的URL打印出来和你在curl里用的那句比对看设备ID有没有抄错、有没有多余空格。确认JSON里数据流ID一致。你在代码里用的是id:temp如果之前手动创建数据流时填的是temperature两边对不上平台会静默丢弃这次上报返回的errno也会大于0。确认串口监视器里的HTTP返回码。返回200和errno:0才是成功。如果返回200但errno非0基本就是JSON格式或鉴权问题对照返回的error字段信息去改。6.4 第四关数据上去了数值却不靠谱这是一个容易被忽略的问题。DHT11读数确实可以能动如果温度值显著偏高比室温高3~5℃大概率是传感器太靠近NodeMCU本体了。ESP8266的WiFi射频芯片和稳压芯片工作时发热不小热量传导到DHT11的塑料外壳上测出来的就不是环境温度而是板温。解决方式很简单用杜邦线把DHT11引到距离板子10cm以上的位置让它悬空在空气中数值马上就下来了。这也是我在标题里强调“读取温湿度”而不是“读取板子温度”的原因。另外DHT11的精度本来就只有±2℃和±5%RH左右你看到它和家里温湿度计差个一两度这属于正常现象不是bug。想追求更高精度换DHT22AM2302或者SHT30代码几乎不用改库都兼容。6.5 我的调试习惯分模块验证别攒着一起调最后说一个我这些年一直坚持的习惯无论项目多简单我都把“传感器读取”“网络传输”“平台对接”拆成三个独立单元分别验证通过后再合成一个程序。这样做的原因很简单——当三块功能糅合在一起后任何一步出错报错信息都可能是混乱的。比如WiFi没连上时你去读DHT11代码会卡在等待WiFi连接的死循环里你根本看不到传感器读数然后你会误以为是传感器坏了其实只是WiFi密码错了。我先单独验证每一环再合起来联调整个过程中的心智负担会小很多。7. 项目扩展从“能上云”到“能用起来”的几条路数据传上OneNet之后这个项目其实才走了一半。OneNet平台的真正优势在于数据可视化、应用编辑器和API接口这些不利用起来可惜了。7.1 在OneNet控制台直接看曲线和报表设备详情页里有“数据流”标签点击某个数据流比如temp可以看到平台自动生成的时序图表。在这个页面上你可以设置数据的单位、小数位数、告警阈值还能生成分享链接手机浏览器打开就能实时看数据——对你这种居家监测场景来说已经够用了不用额外写前端。7.2 用OneNet应用编辑器搭一个简易仪表盘OneNet提供了一个拖拽式的应用编辑器你可以添加一个“温湿度计”组件绑定设备的数据流它会自动渲染出一个指针仪表或者数字卡片。这个仪表盘可以直接全屏投到旧手机上当桌面摆件用。这部分操作不需要写代码全程鼠标点选我建议你花半小时试试体会一下“数据从硬件到可视化”的完整闭环。7.3 云平台数据接口做二次开发如果你想让数据出现在自己的网页或微信小程序里调用OneNet的API去读历史数据即可。大致流程是先用APIKey调用设备数据点查询接口拿到JSON后渲染到前端。OneNet的接口文档写得很详细这里不展开但记住一个核心原则APIKey是核心凭证别把它直接硬编码在小程序里——应该由你自己的后端接口代理转发避免Key泄露。我后来做的几个项目都是在“上报成功”的基础上慢慢加入了告警推送、远程控制甚至简单的数据分析。回头看来当初花在DHT11ESP8266这套最基础组合上的时间完全没浪费——它把传感器读取、网络通信、云平台协议、数据可视化这几个物联网最核心的环节串成了完整闭环后续换个传感器、换个平台、换套协议都是在这套骨架上做替换而已。
返回列表