模块熟悉
第 16 课:温湿度传感器 DHT11
第一个要靠"协议"才肯开口的传感器——一根线,你一句我一句
我是吴老师。从这节课开始,传感器分成两拨了。
前面那几位——光敏、热敏、麦克风——你对付它们的方式都一样:给它电,然后读一个电压。 它不会跟你耍脾气,你什么时候读,它什么时候给。读数是多少,全看当时的物理量。
DHT11 不是这样的。
它是一颗数字传感器,里面自带一个小芯片。它不给你"一个电压",它给你一个已经算好的数——温度多少、湿度多少。但你想拿到这个数,得按它规定的规矩跟它说话。
你说错一个字,它就装死,什么也不给你。
这是你第一次遇到"要谈条件"的传感器。 后面还有 OLED(I²C)、舵机(PWM),全都是"有规矩"的。所以这节课的重点不是接线,是理解"规矩"这件事本身。
学完这节课你能干的事很实在:知道屋里到底几度、潮不潮。 第 33 课那个"桌面温度计",和第 37 课结课的智能温室,核心都是它。
一、先认识它:丝印是 + OUT −
先看实物。你手上这颗模块,多半是个蓝色小板,上面戳着一个带格栅的白色方块——那个方格窗就是它"呼吸"的地方(湿度要从这里进去)。
丝印跟前面所有模块都不一样,认准这三个字:
| 板上丝印 | 意思 | 接到哪 |
|---|---|---|
+ |
电源正 | 3.3V |
OUT |
数据线 | 一个 GPIO,我们接 GPIO4 |
− |
电源负(地) | GND |
这里我要专门停一下。
你前面见过的模块,电源都印 VCC,地都印 GND。这块板子上印的是 + 和 −。
第一次拿到它的人经常在这儿犹豫:"这个 + 是不是 VCC?这个 − 是不是 GND?"
是的,就是。 + 就是正极,− 就是负极。丝印是厂家随便印的,功能是芯片定死的——这句话第 4 课讲过,现在又碰到一次。
顺带说一句:市面上 DHT11 有三针的,也有四针的(多出来的那根是空的)。以你手上板子的丝印为准,别照着别人的图接。接反了不会立刻烧,但读数一定是错的——而且它可能看起来在正常工作,只是数字离谱。
接线:
| DHT11 | 接到 |
|---|---|
+ |
3.3V |
− |
GND |
OUT |
GPIO4(GPIO3~GPIO7 这一带都是安全脚) |
为什么避开
GPIO2、GPIO8、GPIO9,第 4 课那张引脚表讲过——那三个是 strapping 脚,上电那一刻有特殊任务,接外设容易出怪事。不确定的时候就用GPIO3~GPIO7。
二、它跟前面几个不一样:这次得"按规矩说话"
先讲清楚差别在哪——前面那些是模拟信号,DHT11 是数字信号:
光敏是什么样? 你想读就读。analogRead 一执行,它就把当前电压交出来。你俩之间没有"规矩",你说了算。
DHT11 是什么样? 你直接去读它的引脚,只会读到一个一直在高电平发呆的信号,什么信息都没有。
想拿到数据,得走这么一套流程:
- 主控把线拉低一段时间——"喂,我要读数了"
- 松手,等它应答
- 它开始一位一位地吐:湿度、温度、校验码
- 主控把这些位拼成数字
这一套"什么时候拉低、等多久、怎么算 0 怎么算 1",就叫协议。
一个很贴切的比方:对讲机。
对讲机是一根线(一个频道),但规矩是"你一句、我一句":
- 你按住说话,别人只能听
- 你说完松手,对方才回话
- 两个人同时按下去,谁都听不清,这一轮就废了
DHT11 跟主控之间就一根线,这根线既要发指令、又要收数据。所以只能轮流用——这就叫单总线(One-Wire)。
"单"是"只有一根"的意思。 好处是省引脚(就占一个
GPIO);代价是主控得等——它说完话,必须老老实实等着对方把数据吐完,中间不能干别的。
注意:这根线是双向的。 主控一会儿是输出(发问),一会儿得变成输入(听答案)。这就是为什么 DHT11 的代码看起来比光敏"复杂"——光敏那根线永远是输入,不用变。
三、为什么代码里必须"等一等"
现在讲你可能已经踩到的那个坑。
DHT11 里的"湿"和"温",不是随时都在那儿的。
它内部有个元件在感受湿度、感受温度,然后转换成一个数字。这个转换要花时间——手册上给的采样间隔是至少 1 秒,一般的代码里我们干脆写 2 秒。
所以:你读得太快,它会直接给你一个"这次不算数"。
表现出来就是——温度打印出来是 nan(Not a Number,"不是个数"),或者湿度是 0,或者干脆卡住不动。
第一次碰到的人会这么想:"是不是模块坏了?"换个新的还是这样。"是不是线接错了?"重插三遍还是这样。"是不是代码抄错了?"对着屏幕看半小时。
都不是。你只是催得太紧了。
这就是为什么代码里那个
delay(2000)不是"随便加的"。第 6 课讲闪烁的时候我说过:
delay是给"人眼"用的。这里的delay不一样——它是给传感器的,是它要求的。 你少喂它这一口时间,它就交白卷。
还有个时机问题:刚上电那一两秒,它自己也还在稳定,这时候读出来的数别当真。等程序转完一两圈再看。
四、±2°C:一颗"够用但不精确"的传感器
厂家给的参数就这两行,记住它:
| 它测什么 | 范围 | 精度 |
|---|---|---|
| 温度 | 0~50°C |
±2°C |
| 湿度 | 20~90% |
±5% |
现在把"±2°C"翻译成人话:
它报 26 度,真实温度可能在 24 到 28 之间任何地方。它不是"稍微有点误差",它是"差个一两度很正常"。
湿度更松——报 55%,真实可能 50% 也可能 60%。
我为什么要把这件事掰开讲? 因为这是学生最容易在这儿钻牛角尖的地方:
你拿它跟家里的温度计一对,差了 1.5 度。你会以为是自己的代码错了。
不是代码错了。
- 换算公式是芯片内部算好的,你想错都错不了
- 真的是读错了,它会给你
nan,不会给你一个"差一点点的数" - 差一两度,就是这颗传感器的正常表现
那它有什么用?用处大了。 因为你真正关心的事,根本不需要精确到 0.1 度:
- 屋里热不热、闷不闷 → 完全够
- 湿度是不是太高了、衣柜要不要除湿 → 完全够
- 温室里是不是太干,该浇水了 → 完全够
它是一颗"够用但不精确"的传感器。 这个定位你得从一开始就认清楚,别给自己找不痛快。
想要精确的怎么办? 换 DHT22,或者更贵一点的 SHT30(能到 ±0.3°C)。但那不是这节课的事——你先用手上这颗把"协议"这件事弄懂,比换一颗贵的传感器值多了。
记住这个态度:拿到一个元件,先看它的手册参数,再决定"它能干什么"。不是先指望它什么都能干,然后怪它不行。
五、协议已经被"封装"好了
听到"协议""时序"这几个字,你可能心里一紧:是不是要自己一位一位地拉高拉低?
不用。
第 1 课讲图形化平台的时候我说过一句话:那些脏活,平台替你干了。
两条路线都是这样:图形化路线拖一个"温湿度"积木出来,它直接给你温度和湿度两个数;代码路线用第 2 课装好的库,两行代码读出来。
第 2 课装的那两个库——DHT sensor library 和 Adafruit Unified Sensor——就是为今天准备的。当时我说"现在装好,省得上到这一课手忙脚乱",你还记得吧。
代码长这样:
#include "DHT.h"
DHT dht(4, DHT11); // 数据脚是 GPIO4,型号 DHT11
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
float h = dht.readHumidity(); // 湿度,单位 %
float t = dht.readTemperature(); // 温度,单位 °C
Serial.print("温度 ");
Serial.print(t);
Serial.print(" °C 湿度 ");
Serial.print(h);
Serial.println(" %");
delay(2000); // 别读太快,给它留转换的时间
}看,跟读光敏的代码一样短。
但我要你记住一件事:"库替你干了"不等于"底下没发生事"。
刚才那个拉低、应答、一位一位吐数据的过程,在你调 readTemperature() 的那一瞬间,全都真实地发生了一遍——只不过它藏在那两行代码底下。
这就是"会用"和"懂"的区别。
只会用的人,读到
nan的时候只能到处问;懂的人知道是"催太急了"或者"线松了",改一个数、按一下线就好了。你不用会写那套时序——但你得知道它存在。 这是我在这节课唯一较真的地方。
六、动手:读出来,然后捂住它
第一步:接好线,先只打印。
按上面的表接:+→3.3V,−→GND,OUT→GPIO4。烧进去,打开串口。
你应该看到两行数字在每两秒刷一次。
第二步:先做第 5 课那个动作——"先测,再猜"。
用万用表量 OUT 脚对 GND 的电压:空闲时大概 3.3V(它在发呆,线被拉着高);数据在传的那一瞬间,电压会来回跳。
万用表反应没那么快,多半只能看到它在 3.3V 附近晃。能看到"晃"就对了——那正是它在说话。想看清楚的,用示波器,那是后话。
第三步:重点来了——用手把它捂起来。 用你的手掌整个包住那个白色格栅,别漏风,等十几秒。
盯着串口看。温度会一点点往上爬——从 26 爬到 28、29、30,慢慢停住。 湿度也会变——而且往往比温度涨得更明显,因为它对"手心里的汗气"特别敏感。
拿开手,它会慢慢退回去。 这个"慢慢"跟第 12 课热敏的"慢慢"是同一回事——热量和水汽传进去需要时间,不是模块反应慢。
这一步有个隐藏价值: 你第一次能同时看到两个物理量在同一个传感器上变化,而且它们的变化节奏不一样。温度涨得慢、湿度的反应更快——这说明它们在测两件不同的事,不是同一个数换了个名字。
第四步:让它"看见"你的手就反应。
加一个判断,把它跟前面的课串起来:
void loop() {
float t = dht.readTemperature(); // 读温度
if (t > 29) { // 比你自己定的线大吗
Serial.println("有点热了"); // 这里换成点灯、响蜂鸣器都行
}
delay(2000);
}看,还是那个闭环:传感器(读温度)→ 判断(比 29 大吗)→ 执行器(打印 / 亮灯)。
这个阈值 29 别抄我的——按第 11 课标定那套:先测你自己的室温,再定一条线。 手捂着能不能过这条线,才是这个实验成不成立的关键。
顺便说个实际问题:DHT11 捂在手心里,湿度经常一下就冲到
90%以上。那多半是到量程上限了(它只保证20~90%)。看到数字顶住了,别以为坏了——换个地方,比如放在湿毛巾旁边,再看看。
自测
答案与解析
光敏、热敏是模拟传感器:你什么时候 analogRead,它就什么时候把当前电压交出来,没有规矩。DHT11 是数字传感器:内部自带芯片,按单总线协议一问一答才吐数据。理解这一点,你才能理解为什么代码里必须有 delay、为什么会读到 nan。
delay(2000),让它一秒读几十次,会发生什么?答案与解析
DHT11 转换一次数据需要时间,手册给的采样间隔是至少 1 秒。你催得太紧,它这一轮还没算完就被打断,只能给你一个无效值。这里的 delay 不是给人眼看的(第 6 课那种),是传感器要求的——这是两种 delay 的区别。
答案与解析
DHT11 测温精度是 ±2°C——报 27.5,真实值可能就在 25.5 到 29.5 之间。这不是故障,是它的规格。 想更准要换 DHT22 或 SHT30。这里的态度是:先看手册参数,再决定它能不能干这件事,而不是先指望它精确、再怪它不精确。
OUT 那根线,主控有时候要当输出、有时候要当输入?这跟"单总线"有什么关系?答题要点
答题要点应该包含:单总线就是发指令和收数据共用同一根线,为了省引脚。所以主控要先输出(把线拉低,发出"我要读数"的请求),然后切换成输入(听 DHT11 一位一位地吐数据),最后再把它拼成温度湿度。这就是它和光敏最大的区别——光敏那根线永远是输入,不用变方向。也正因为只有一根线,同一时刻只能有一方在说话,就像对讲机,两个人同时按下去谁都听不清。
拓展一根线,怎么区分"这一位是 0"和"这一位是 1"?它又没有第二根线可以对照。
这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。
提示:搜"DHT11 时序 高电平持续时间 26us 70us"。想一想:如果约定"高电平长一点算 1、短一点算 0",那主控要量的是什么?这跟第 15 课超声波量"高电平持续了多久"是不是有点像?
拓展为什么 DHT11 便宜,DHT22、SHT30 贵好几倍?贵在哪儿了?
这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。
提示:搜"DHT11 DHT22 SHT30 对比 精度 接口"。想一想:有的传感器用单总线,有的用 I²C(第 19 课会讲),接口方式会不会也影响价格和用法?
本节你要动手做的事
- [ ] 接好三根线:
+→3.3V,−→GND,OUT→GPIO4(接之前先断电,认丝印不是VCC/GND) - [ ] 跑通打印:串口里能看到温度和湿度两个数,每两秒刷一次
- [ ] 用万用表量一次
OUT:空闲时是不是 3.3V 左右?确认它跟光敏那根AO的表现不一样 - [ ] 用手捂住格栅,看着温度和湿度慢慢变化——记下你开始和结束的两个数
- [ ] 故意把
delay(2000)改成delay(50),看会发生什么,记住nan长什么样 - [ ] 定一条自己的温度阈值(用你测出来的室温,别抄我的 29),让它"热了就打印一句话"
这节课你做成的是一件挺不容易的事:你让一个传感器按规矩跟你说话了。
前面那几位传感器,你是偷看它们——读一下电压就走。DHT11 不一样,它是你把问题递过去,它把整理好的答案交回来。这个"问—答"的关系,是你第一次建立起来的。
我知道看到 nan 的时候你会烦。但那个 nan 是有信息量的——它在说"你催得太急了"。能听懂器件在抱怨什么,就是这门课最想教给你的本事。
下一节我们换个方向,让板子自己发出声音。那是个特别轻松愉快的小模块——而且它的用法跟 LED 一模一样。第 6 课我说过"学会点灯就学会一半的输出设备",下一节就是兑现的时候。