ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Arduino三合一DHT库:DHT11/DHT21/DHT22驱动安装与避坑要点

Arduino三合一DHT库:DHT11/DHT21/DHT22驱动安装与避坑要点

简介:面向Arduino开发者的DHT11、DHT21、DHT22温湿度传感器库文件压缩包,专为需要在物联网及DIY电子项目中快速接入环境监测功能的用户准备。包内包含核心库文件与示例程序,可直接在Arduino IDE中调用,免去手动解析单总线通信协议的繁琐过程。资源共10个文件,以C++源码(.cpp/.h)为主,辅以Arduino示例(.ino)、说明文档(.md)、属性配置(.properties)及关键字文件(.txt),整体仅15KB,轻量且便于移植。DHT22支持-40℃至80℃测量范围,相对DHT11具有更高精度,可满足不同场景的精度需求。该资源已吸引1369人学习下载,适合正在学习传感器应用或准备快速完成环境监测项目的初中级开发者,拿到手即可参考示例完成初始化与数据读取。

1. 一份 DHT11-DHT21-DHT22 库文件:三款传感器的 Arduino 读取被一个小 zip 收编了

我在某温湿度采集 Demo 里第一次把 DHT11 换成 DHT22 时,沿用旧的单型号驱动,串口全返回 -999,一度怀疑买到坏件。后来才明白,DHT11、DHT21、DHT22 虽然长相和引脚接近,底层单总线时序、校验和逻辑并不共用,光在代码里改型号名没用。这份 DHT11-DHT21-DHT22 库文件.zip,解决的就是“同一套 Arduino 代码,按宏定义切换三款传感器型号”的问题:构造 DHT 对象时指一下型号,温湿度统一用 readHumidity() 和 readTemperature(),不用自己对着数据手册抠时序。新手直接跑 examples 里的示例就行,熟手可以拿来做多传感器轮询与低功耗改造的底子。这篇文章就把选型、安装、接线、避坑、进阶验证一次说清楚。

2. DHT11、DHT21、DHT22 的差异与库安装:参数表决定你该选谁

2.1 三款传感器关键参数对比

先看参数,再写代码,不然你不理解为什么同一个库可以传三种型号却不通用。下面是三款传感器的常用参数对比,采购和调参都会用到。

参数项DHT11DHT21(AM2301)DHT22(AM2302)
湿度精度±5%RH±3%RH±2%RH
温度精度±2°C±0.5°C±0.5°C
湿度量程20%-80%RH0%-100%RH0%-100%RH
温度量程0-50°C-40°C-80°C-40°C-80°C
Bus 类型单总线单总线单总线
最小采样间隔1 秒2 秒2 秒
温度分辨率1°C0.1°C0.1°C
湿度分辨率1%RH0.1%RH0.1%RH

DHT11 适合对环境数据只做大趋势判断的场景,比如教室温湿度展示、简易空气站刷 LED 屏。DHT22 适合需要判断 0.5°C 变化的场景,比如机房温度告警、药品柜存储监测。DHT21 相对冷门,但它对长线和工业环境的抗干扰能力通常比 DHT22 好一些,我做过一个配电柜环境监测方案,走的 DHT21 + 4.7kΩ 上拉,数据就很稳。

这三款都是单总线,通信协议里都包含起始信号、响应信号和 40 bit 数据帧,但每一位高低电平时序的长度不一样。DHT21 与 DHT22 的时序比较接近,DHT11 则明显不同,所以库里必须用枚举类型区分,这也解释了为什么一份库打包三款传感器值得下载:自己按 datasheet 写协议识别 40 bit 数据,至少要多花两天调试。

2.2 解压 zip 后应该放在哪个目录

Arduino IDE 的库目录有固定位置。Windows 一般是%USERPROFILE%/Documents/Arduino/libraries,macOS 是~/Documents/Arduino/libraries,Linux 是~/Arduino/libraries。Arduino IDE 1.x 和 2.x 共用这几个目录,装一次两边都能识别。

很多人装完库编译报DHT.h: No such file or directory,原因就是 zip 解压后直接把 DHT.cpp 和 DHT.h 丢进了 .ino 所在目录。IDE 编译时只搜索工程目录、库目录和内置核心目录,不会递归搜索工程下面的所有子文件夹,所以必须规范放到 libraries 下。另外,解压工具如果不小心解出两层同名目录,比如DHT-master/DHT-master/DHT.h,也会导致 IDE 找不到正确路径。规范路径是libraries/DHT/DHT.h,中间不多包一层。

# macOS / Linux 命令行安装示例 mkdir -p ~/Documents/Arduino/libraries cp -r ~/Downloads/DHT-master ~/Documents/Arduino/libraries/DHT

这段命令做了两件事:先确保 libraries 目录存在,再把解压出来的整体复制成 DHT 目录。复制完成后可以检查一下目录结构:

ls ~/Documents/Arduino/libraries/DHT # 预期能看到 DHT.h 和 DHT.cpp

Windows 用户直接用资源管理器把整个文件夹移动到文档/Arduino/libraries下并改名 DHT 即可。文件夹名不要带空格、中文或-master后缀,新版 IDE 的库管理索引对目录名有约束,带特殊字符有时能编译过,但库管理器里显示会异常。

装好后务必重启一次 Arduino IDE。IDE 启动时才扫描 libraries 目录,不重启直接编译,偶尔会遇到头文件索引没刷新的玄学问题。

2.3 如何确认库真的被装入 IDE

重启 IDE 后,新建一个空工程,写入#include <DHT.h>并编译。编译通过说明头文件路径正确。菜单栏“项目→加载库”列表里如果能看到 DHT 相关条目,说明 library.properties 被正确解析;如果列表看不到但编译通过,多半是这份库缺少规范的 library.properties 元数据,不影响实际使用。

注意一种特殊情况:某些库压缩包内部带src子目录,真正的源码有两套路径,根目录一份、src 里一份。Arduino 库规范会把src当作根目录,所以编译不会报错。如果出现multiple definition这类重定义错误,通常是根目录和 src 下都放了同名 .cpp,手动保留一份即可。

这份 zip 的实际内容我没有逐字节核对,但按 DHT 库常见打包方式,至少包含 DHT.h、DHT.cpp、examples 目录和 keywords.txt。examples 里通常有一个 DHTtester 示例程序,这是验证安装是否成功最快的方式。建议优先打开 examples 里的 .ino 编译,因为示例里的引脚和型号宏是作者验证过的组合。

3. 接线与第一个读取程序:用 DHT_TYPE 宏一次兼容三种传感器

3.1 传感器引脚定义与上拉电阻

三款传感器都是四针封装,直插式通常从左到右是 VCC、DATA、NC、GND,但不同厂家的丝印排序可能有差异,接线前一定要看丝印,不要只认颜色。模块式一般引出 VCC、DATA、GND 三线,板载电容和上拉电阻已经集成,接起来更省心。

裸传感器则需要自己补一个 4.7kΩ 到 10kΩ 的上拉电阻,接在 DATA 和 VCC 之间。原因很简单:DHT 数据线是开漏输出,没有上拉电阻时高电平会漂移,单片机会把 1 判断成 0,出现偶发乱码或者读值偶尔 NaN。我在项目里见过直接把数据线悬空接杜邦线的,跑了半天全是固定 0,加上拉后立刻正常。

传感器引脚接线去向说明
VCCArduino 5V 或 3.3V推荐 5V,低压下个别批次不稳定
DATAArduino D2数据脚,外接 4.7k-10k 上拉
GNDArduino GND共地
NC悬空不要接任何网络

上拉电阻的选择也有讲究。DHT11 用 10kΩ 没问题,DHT21 在长线下建议用 4.7kΩ,因为 DHT21 的内部上拉能力偏弱,电阻太大高电平建立时间不够,时序就容易超时。如果传感器距离单片机超过 20cm,把线换成双绞屏蔽线,Data 与 GND 同轴走线,能减少环境干扰。

3.2 主程序:初始化、读取、串口打印

拿到库后最快的跑通方式是打开 examples 下的 DHTtester,修改 DHT_PIN 和 DHT_TYPE 两个宏即可。核心代码的结构大致如下:

#include <DHT.h> #define DHT_PIN 2 #define DHT_TYPE DHT22 // 切型号就改这一行:DHT11 / DHT21 / DHT22 DHT dht(DHT_PIN, DHT_TYPE); void setup() { Serial.begin(115200); dht.begin(); Serial.println("DHT program started"); } void loop() { float humidity = dht.readHumidity(); float temperature = dht.readTemperature(); if (isnan(humidity) || isnan(temperature)) { Serial.println("Read failed, check wire or pull-up"); delay(2000); return; } Serial.print("Humidity: "); Serial.print(humidity); Serial.print(" %\t"); Serial.print("Temperature: "); Serial.print(temperature); Serial.println(" *C"); delay(3000); }

逻辑说明:第一行引入库头文件,第三行定义数据引脚,第四行定义传感器型号,这是整份库的核心抽象。第七行构造 DHT 对象,构造顺序是引脚在前、型号在后,写反了在编译期不会报错,但运行时读到的是乱码。setup 里的 begin() 负责完成内部状态初始化,包括引脚模式设置。loop 里 readHumidity 返回湿度百分比,readTemperature 默认返回摄氏温度,底层把 20 bit 温湿度原始值除以 10。

参数上需要记住三个细节。第一,DHT_TYPE 只能填 DHT11、DHT21、DHT22 三种,大小写敏感,填错不报错但读不出来。第二,readTemperature() 可以传 true 返回华氏温度,比如dht.readTemperature(true);不传参数默认是摄氏度。第三,读取失败时两个函数会返回 NaN,所以代码里必须用 isnan 拦截,否则后续用这个值算热指数会输出一串无效数。部分库版本还带 readHeatIndex() 热指数计算,但热指数公式只在高温高湿区间有意义,低温环境算出来反而离谱,不要无脑用。

3.3 首次运行:串口输出长什么样与异常值识别

上传程序后,打开串口监视器,波特率必须设成 115200。如果设成 9600,串口打印出来全是乱码——这不是传感器问题,是波特率不匹配。正常情况下的输出类似:

DHT program started Humidity: 51.20 % Temperature: 26.30 *C Humidity: 51.30 % Temperature: 26.30 *C Humidity: 51.50 % Temperature: 26.60 *C

如果读完一次后按复位键,传感器第二次上电通常输出会更快稳定,因为模块内部电容已经完成初充电。第一次上电出现一次 NaN 是比较常见的现象,原因是 DHT 起始信号发出后等待响应超时,代码里已经做了 NaN 拦截和重试,问题不大。

我把首次运行常见的串口现象整理成一个表,排查时对号入座:

串口现象可能原因下一步操作
完全没有输出串口选错或波特率不对核对端口和 115200
一直 NaN线松、型号宏错检查上拉和 DHT_TYPE
固定 0.00 / -999型号宏选错DHT11 写成 DHT22 典型的症状
数值定住不动采样间隔没到确认 delay 大于等于 2 秒
隔一段跳一个大值干扰、热源缩短线长或软件滤波

这里尤其要提醒 DHT21:某些批次的模块上电后内部晶振起振很慢,setup 里 begin() 完立刻读会稳定返回 NaN。我在模拟项目X 里处理 DHT21 时,会在 begin() 后面加一个delay(3000),让传感器先完成自检和内部状态机复位,再进入 loop 循环,一次成功率明显提升。

如果你读到的温湿度一直是“整数”,也可能不是代码问题,而是 DHT11 的分辨率只有 1°C 和 1%RH,25.3°C 显示成 25°C 属于物理特性,不是精度缺陷。

4. 避坑与排查:装完库第一次读数翻车,逐条解决

这一章整理五条实际调试中很常见的坑,每条按“现象→原因→解决”展开。这些坑单看都简单,但组合出现时真会耗掉一整个下午。

4.1 现象:编译报错 DHT.h: No such file or directory

这是安装库最常见的问题。原因几乎都是库文件没有真正放进 libraries 目录,或者放进了但目录层级不对。Arduino IDE 搜索头文件时只认固定位置,不会帮你自动遍历 zip 解压出来的多级目录。

解决方法是先确认路径层级,正确结构必须是libraries/DHT/DHT.h。如果你的 libraries 目录下同时存在 DHT 和 DHT-master 两个文件夹,IDE 会优先加载其中一个,也可能两个都加载然后产生重复符号,建议只保留一份最干净的 DHT 目录。我一般装完库会顺手执行一次目录树检查,确认头文件在正确路径后再关闭 IDE 重启,很少再出现找不到库的情况。

4.2 现象:DHT11 读出固定 0.00 或 -999.00

这大概率不是传感器坏了,而是 DHT_TYPE 宏填成了 DHT22。DHT11 和 DHT22 的时序参数不同,库内部靠宏决定延时长度和位判断逻辑,用错了型号解析出来的 40 bit 数据全是无效值。代码层面没有报错机制,因为读回的数据校验和可能恰好通过,但数值永远是假的。

解决方式简单直接:

#define DHT_TYPE DHT11

改完宏,删除旧固件重新上传。还有一个小坑:市面上部分模块丝印写 DHT11,但内部芯片实际是 DHT21 或 DHT22 的兼容替代,这类模块用 DHT11 模式读可能读到的是无效值。遇到这种“怪模块”,把 DHT_TYPE 挨个换一遍,哪个读出的数据合理就用哪个,这是最朴素的排除法。

4.3 现象:DHT21 一直读 NaN,DHT11 在同一引脚正常

硬件没坏,但换成 DHT21 就读不出来,常见原因有三个:上电稳定时间不够、上拉电阻偏大、数据线过长。DHT21 对下降沿采样时序更敏感,10kΩ 上拉在长线场景下恢复时间太长,导致库读到无效位。

解决步骤:第一,begin() 后 delay 3000ms 再进主循环;第二,把上拉电阻换成 4.7kΩ;第三,杜邦线尽量缩短在 10cm 内。如果这三个条件都满足还是 NaN,换一颗传感器试一下,个别批次模块内部焊点不良也会造成长期读失败。

4.4 现象:数值每隔几秒跳一个异常大值

数据大部分时间正常,偶尔冒出一个 70%RH 或者 40°C,之后又恢复正常。这个现象的本质是单总线上的毛刺被当成了一位有效数据,常见诱因是传感器离开关电源或者继电器太近,数据线与电源线长距离并行走线。

解决思路分两步,硬件上让数据线远离电源线和继电器输出线,软件上做滤波。最简单的做法是中位数滤波而非平均滤波:连续采三次,取中间值。平均滤波会把尖峰摊到结果里,中位数则能直接丢弃单点异常。

float stableHumidity() { float readings[3]; for (int i = 0; i < 3; i++) { readings[i] = dht.readHumidity(); delay(100); } if ((readings[0] >= readings[1] && readings[0] <= readings[2]) || (readings[0] <= readings[1] && readings[0] >= readings[2])) return readings[0]; if ((readings[1] >= readings[0] && readings[1] <= readings[2]) || (readings[1] <= readings[0] && readings[1] >= readings[2])) return readings[1]; return readings[2]; }

逻辑说明:三次读取之间 delay 100ms,避免连续读时拿到缓存值。中间值判断逻辑兼容了相等情况,返回的一定是三次读数的中位数,而不是平均值。这样处理之后,单次毛刺不会被纳入最终结果。

4.5 现象:Arduino IDE 库管理器搜不到这份库,但示例能编译通过

搜不到通常会让人心里打鼓,但不用慌。库管理器列表依赖 library.properties 文件里的版本字段,如果这份 zip 内的元数据不完整,IDE 的库管理器就不会显示它。可只要头文件和源码在 libraries 目录下,编译器依然能正常找到。

解决:不依赖库管理器列表,直接用 examples 里的 .ino 编译验证。注意不要在库管理器界面里手动修改文件夹名,那样会造成索引混乱,重启后列表反而消失。物理上直接复制文件夹到 libraries 目录是最可靠的安装方式,IDE 的扫描机制对这种手动安装的兼容性是最好的。

5. 多传感器轮询与低功耗采样:把库用进真实环境监控

5.1 单板多传感器轮询

一个 Arduino Nano 挂四路 DHT22 做空间温湿度网格测量,是室内环境监测里很常见的用法。四路传感器共用 VCC 和 GND,数据线分别接 D2、D3、D4、D5。代码层面就是实例化多个 DHT 对象:

DHT dht1(2, DHT22); DHT dht2(3, DHT22); DHT dht3(4, DHT22); DHT dht4(5, DHT22); void setup() { Serial.begin(115200); dht1.begin(); dht2.begin(); dht3.begin(); dht4.begin(); } void loop() { Serial.print("t1="); Serial.print(dht1.readTemperature()); Serial.print(" t2="); Serial.print(dht2.readTemperature()); Serial.println(); delay(2000); }

逻辑说明:每个对象绑定独立引脚,begin 全部调用一次,loop 里逐个读取。这里最容易翻车的是把四路数据线并到同一个引脚上,因为 DHT 单总线没有片选机制,四颗传感器同时驱动一根线,电平互相钳制,结果就是全部 NaN。数据线必须一引脚一传感器。

如果需要动态管理传感器数量,可以用数组配合模板,但在 Arduino 这种资源受限的环境里,直接声明对象更直观,占用的 RAM 也更容易估算。每个 DHT 对象只维护引脚、型号、私有缓冲区,整体内存开销很小,不用担心资源耗尽。

注意轮询的读数顺序:多路传感器中如果有一颗响应特别慢,它会拖慢整轮循环,因为库的 read 函数是阻塞式等待。对实时性要求高的场景,可以把不同传感器安排在不同时隙读取,防止某颗异常传感器卡住整个系统。

5.2 低功耗场景:睡眠唤醒与采样周期

电池供电的温湿度记录仪,不能一直用 delay 阻塞循环。常规做法是用 Arduino 低功耗库或者直接操作 AVR 寄存器让芯片睡眠,定时唤醒后读一次传感器再睡。核心约束是传感器的采样周期:DHT11 至少 1 秒,DHT21 和 DHT22 至少 2 秒,唤醒后立刻读会拿到缓存值或触发超时。

我一般建议的时序是:唤醒 → delay 300ms 让传感器内部状态稳定 → 读数据 → 处理数据 → 睡眠。睡多久取决于业务需求,半小时采样一次对多数环境监测已经足够。

器件状态典型电流说明
Arduino Nano 睡眠约 0.2mA关闭 ADC、掉电模式
DHT22 空闲约 0.2mA数据线保持高电平
DHT22 读取瞬间约 1mA持续几十毫秒
整体每小时采样一次平均约 0.2mA取决于唤醒次数

低功耗最容易踩的坑是:传感器一直供电没问题,读取前没有给足稳定时间。深度睡眠唤醒后主控立刻执行 dht.readHumidity(),读进一个 NaN,然后马上又睡了,这组数据就成了缺测值。修正方式是在读之前固定加 200ms 延时,虽然浪费一点唤醒时间,但换来的是高一次成功率。

这里还要补一个边界知识:如果要求真正的微安级功耗,不能只靠传感器待机电流,LDO 的静态电流往往比传感器本身还大。需要把传感器 VCC 用 MOSFET 或 IO 口控制,只在采样前给传感器供电几十毫秒。但库本身不关心供电什么时候来,只要保证读时序期间 VCC 稳定就可以。

5.3 移植到 ESP32 及其他开发板的边界

这份库面向 Arduino IDE,但本质是普通 C++ 类,ESP32、ESP8266、STM32 的 Arduino 核心都能编译。我实测过在 ESP32 上跑 DHT22,只需要把 DHT_PIN 改成支持的 GPIO 编号即可。ESP32 的引脚编号不一定等于板面丝印编号,比如某些开发板丝印 D4 实际是 GPIO4,直接用 GPIO 号就没错。

需要注意的是 ESP32 的 GPIO 输入模式默认带内部上拉,但内部上拉阻值约 30kΩ-50kΩ,对 DHT21 这种对上升沿敏感的传感器偏弱。我仍然会外接一个 4.7kΩ 上拉电阻,不依赖内部上拉。电源用 3.3V 即可,但数据线不要从 5V 模块上取电,电平不匹配会造成读值漂移。

STM32 通过 Arduino IDE 使用时引脚编号要参考具体板型映射,不要直接套 UNO 的 D2 概念。只要硬件上拉满足条件,库源码不需要改动,因为单总线协议只依赖引脚高低电平翻转,与底层芯片架构关系不大。如果你是做产品原型,先用 Arduino 板验证传感器和算法,再整体迁移到低功耗 MCU,这套库代码可以原样带走。

6. 十分钟验货:用串口绘图仪确认库和硬件都正常

拿到这份 DHT11-DHT21-DHT22 库文件.zip 后,我的第一个动作不是改代码,而是原样跑 examples 里的 DHTtester 示例。修改三处:DHT_PIN 设置成实际接线引脚,DHT_TYPE 设置成传感器型号,波特率保持 115200。编译上传打开串口绘图仪,Arduino IDE 的菜单路径是“工具→串口绘图仪”。

串口绘图仪会同时画出两条曲线,一条是湿度,一条是温度。因为温湿度数值范围不同,正常时曲线能看出明显区分:对着传感器哈气,湿度曲线从 50%RH 左右快速抬升到 70%RH 以上,然后自然回落;温度曲线也会跳高 0.5°C 左右再慢慢降回来。如果曲线是一条完全水平的线,先用手握住传感器体加热,温度曲线出现小台阶,说明传感器活着;若温度纹丝不动,大概率是型号宏写错或接线问题。

串口绘图仪还有一个容易被忽略的好处:它打印的数据就是库输出的原始 float。湿度正常范围在 0-100%RH,温度在传感器量程内,如果你看到湿度 200%RH 这种超量程数值,说明库内部解析出来的 8 bit 湿度整数位出了问题,先把 DHT_TYPE 换一个排列组合试,通常能解决。

我习惯在验货完后把 loop 里的 delay(3000) 改成 500ms,并对着传感器吹气观察波形变化,这样能在十分钟内确认三件事:库安装正确、硬件接线正确、传感器型号匹配。DHT 系列的“玄学”问题大多是安装和时序问题,很少是芯片本身损坏。

从那以后,我每次拿到新的 DHT 传感器型号,都强制自己先走一遍“改宏、跑示例、对着传感器哈气”的流程,再也没有被莫名其妙的 NaN 骗过。这套办法如果你照着做,应该也能少走几天弯路,希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进