ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

51单片机温湿度采集监控:DHT11驱动与单总线时序实战

51单片机温湿度采集监控:DHT11驱动与单总线时序实战

简介:一套基于51单片机的温湿度采集监控系统工程,面向嵌入式初学者和物联网环境监测开发者,解决温湿度实时采集、日历时钟显示与超标报警等常见需求,适用于农业大棚、工业仓储、家庭环境等场景。工程以Keil C51为开发环境,包含SHT10温湿度传感器驱动、DS1302日历时钟驱动、LCD12864液晶显示与调时功能,并支持温湿度越限时蜂鸣器/LED报警,源码结构清晰,便于学习外设编程。压缩包共28个文件,主要涵盖.c源码、.h头文件、.lst列表、.obj目标文件、.hex烧录文件以及Keil工程配置等,整体仅84KB,轻量完整,可快速编译烧录验证。目前已有353人学习浏览,可用于课程设计、毕业设计或实际环境监控项目的功能参考与二次开发。借助工程中的hex文件可直接烧录到单片机查看效果,而通过源文件可深入理解传感器时序、时钟芯片读写、液晶显示等关键代码。

1. 温湿度采集监控这个题目,51单片机为什么还在被反复做

我最早接触“51单片机实现的温湿度采集监控”是帮一个朋友改库房里的温度报警器:要求不高,能定时测温湿度、能显示、超过阈值能响。当时我下意识想换个大牌MCU,结果朋友甩过来一板子经典51最小系统,说成本就十几块钱,折腾坏了也不心疼。后来我把这个方案打通,发现它几乎是入门到实战最顺的一条路:一根单总线连着传感器,一块1602液晶显示,一个蜂鸣器报警,逻辑清楚到新手三天能跑通,老手也能拿它做产品原型。这篇文章就把这个标题拆开讲:传感器怎么选、单总线时序怎么读、电路怎么搭、代码怎么写、哪些坑最常踩,最后再说串口上报和掉电保存怎么加。

2. 硬件选型与温湿度传感器原理:DHT11/DHT22怎么选,单总线时序到底在说什么

2.1 传感器选型:DHT11够用、DHT22什么时候才值得

温湿度采集监控的传感器有很多种,SHT系列、AHT系列、DHT系列都有,但51项目里最常见、资料最全的还是数字单总线方案。这里不绕圈子,直接说结论:普通库房、机房、暖房监控,DHT11完全够用;如果你要测精度要求高的环境,或者经常在低温低湿下工作,才去考虑DHT22。

对比项DHT11DHT22
温度精度±2℃±0.5℃
湿度精度±5%RH±2%RH
温度范围0~50℃-40~80℃
湿度范围20~90%RH0~100%RH
采样周期≥1s≥2s
价格很便宜贵约3倍
接口单总线单总线

从表格能看出,DHT11精度虽然一般,但监控场景真正关心的不是0.1℃的差别,而是“温度有没有超过28℃”“湿度是不是低于40%”这类阈值判断。DHT11便宜,而且数据帧格式、驱动逻辑比I2C传感器更直白,非常适合51这种没有硬件外设的老内核。DHT22适合做环境记录仪或者对温湿度波动敏感的场景,比如档案室、实验室,这时多花几十块钱换精度是值得的。

选型还有一个容易忽略的点:DHT11采样周期是1秒,你不可能像读按键一样连续读它,主循环里每次读完后至少要等500ms再发起下一次读。这个限制不是传感器“性能差”,而是单总线协议的设计,后面写代码时会再提到。

2.2 单总线协议:一根线怎么既送命令又回数据

DHT11只用一根数据线,既能接收主机的启动信号,又能把温湿度数据吐回来。听起来很玄,其实原理就是一段严格的时间电平变化。开始通信时,主机先把数据线拉低至少18ms,然后释放并由主机拉高20~40us,这是告诉传感器“我要读数据”。接着DHT11会主动把线拉低80us,再拉高80us,表示“我准备好了”。之后连续输出40位数据,格式固定:湿度整数、湿度小数、温度整数、温度小数、校验和。

校验和的算法是前四个字节相加,结果的低8位等于第五个字节。比如湿度整数50、湿度小数0、温度整数26、温度小数0,加起来是76,那么校验字节就是76。如果不相等,这一帧数据直接丢弃,这是单总线通信里最实用的自校验手段。

把这40位读完,每一位的读法也靠时序区别:每个数据位开始都是50us低电平,然后拉高。如果高电平维持约26~28us,表示这一位是0;如果高电平维持约70us,表示这一位是1。所以读数据的关键不是数高电平持续时间,而是采样点:从低电平结束开始,延时40us再去读数据线,此时读到低就是0,读到高就是1。这个40us的延时很讲究,51单片机用循环延时会有误差,后面避坑章节会专门说。

2.3 最小系统与引脚分配:P2口做什么,P3口做什么

硬件上除了单片机本身,还需要晶振、复位电路、电源、下载串口。我用最常见的51增强型芯片,晶振用11.0592MHz,原因很简单:这个频率给串口波特率计算比较容易,9600波特率误差很小。如果你确定不上位机串口,用12MHz去驱动DHT11也没问题,但后续扩展串口时会麻烦,所以建议一步到位。

引脚分配我习惯这样定:DHT11数据线接P2.0,LCD1602数据线接P0.0~P0.7,LCD控制线RS、RW、E分别接P2.5、P2.6、P2.7,蜂鸣器接P1.0,两个按键接P3.2和P3.3。P0口内部没有上拉,必须外接一排10k排阻,否则LCD显示会出现乱码或者完全无显示。P2.0作为单总线也最好加上4.7k上拉电阻,这样传感器空闲时数据线能稳定在高电平,抗干扰更强。

引脚功能说明
P0.0~P0.7LCD1602数据口D0~D7必须接上拉电阻
P2.5 / P2.6 / P2.7LCD1602 RS / RW / E控制线
P2.0DHT11 DATA加上拉4.7k
P1.0蜂鸣器低电平驱动或三极管驱动
P3.2 / P3.3阈值调整按键加消抖

P3.0和P3.1留给串口,这也解释了为什么要选11.0592MHz晶振。如果你打算做完整的上报功能,P3口就不要被其他外设占用太多,否则代码连软件串口都不好编。整个模块化思路是:采集一块、显示一块、控制一块,后面写代码会按这个结构拆文件。

3. 电路连接与实物搭建:面包板到洞洞板的接线顺序

3.1 51最小系统的三件套:晶振、复位、电源去耦

51最小系统看起来简单,但新手翻车最多的地方往往不是传感器,而是最小系统本身。晶振电路一般用两个22pF左右的瓷片电容,分别接到晶振两端再接地,电容值不用太较真,20pF到30pF都能起振。复位电路用10uF电解电容和一个10k电阻,上电瞬间电容充电,RST引脚短暂为高,完成复位。有条件的话在电源VCC和GND之间并一个104(0.1uF)去耦电容,防止电机、继电器动作时把电压拉偏。

搭电路顺序我建议先不要插DHT11和LCD,先把最小系统做出来,下载一个点亮LED的小程序验证单片机在运行。这个验证很重要,因为在面包板上插了传感器和液晶后,万用表都很难量,出了问题会分不清是单片机没跑还是LCD没初始化。等点亮LED成功,再接LCD显示“Hello”,最后再接DHT11。按这个顺序走,能省掉大量排查时间。

3.2 DHT11和LCD1602接线表

LCD1602是16脚直插,注意背光供电和对比度调节。第3脚VL要通过一个10k电位器接到GND,或者分压到VCC与GND之间。很多新手接完发现“液晶亮了但屏幕上什么字都没有”,十有八九是VL脚电位不对,要么太亮、要么太暗,调电位器就能解决。

LCD1602脚位接法
1 VSSGND
2 VDD+5V
3 VL10k电位器中间脚
4 RSP2.5
5 RWGND(只写不读)
6 EP2.7
7~14 D0~D7P0.0~P0.7
15 A+5V(背光)
16 KGND(背光)

RW直接接地,意思是液晶永远处于写模式,不需要读忙标志,代码会更简单一点,但因为不用读忙,每次写命令后要固定给几毫秒延时,不要连续写。DHT11三个引脚如果是直插式,通常是VCC接5V、DATA接P2.0、GND接地,DATA和VCC之间再加一个4.7k上拉到5V。有些模块板板上已经带上拉电阻,如果是裸传感器就必须自己加。

3.3 供电和上拉:为什么DHT11数据线要加4.7k上拉

DHT11的数据线是漏极开路输出,本身没有主动驱动高电平的能力。空闲时它需要靠外部上拉电阻把线拉高到5V,通信时传感器通过拉低电平来表示信号。如果省掉这个上拉,数据线会浮空,读出来的数据要么全是0,要么偶尔正常、偶尔乱跳。上拉电阻阻值选择4.7k到10k都可以,我用4.7k,如果线长超过30cm,还可以换成2.2k增强驱动能力,但不要用1k以下,否则传感器拉低时电流偏大,容易影响电平判断。

还有一个容易忽略的点:DHT11模块如果用的是3.3V供电,而单片机是5V系统,那输出的高电平可能达不到5V,但3.3V高电平对51来说基本能识别,最好还是统一5V供电,不要混电。面包板上用的杜邦线不要超过20cm,DHT11数据线太长、线间电容增大,单总线时序容易被拉变,轻则校验失败,重则读不到数据。

3.4 下载电路与调试口

下载电路常见做法是USB转TTL串口连接单片机的P3.0和P3.1,芯片还需要冷启动:先点击下载,再给单片机上电。很多新手卡在这一步,以为点下载就能烧录,其实是程序在等待握手信号,没有冷启动时自然一直失败。下载器输出的TXD接单片机的RXD(P3.0),RXD接TXD(P3.1),GND共地,接反了也连不上。

我一般还会留一个串口调试的排针座,把P3.0/P3.1/GND/VCC引出来,这样后期调试上位机时不用再飞线。调试口不是必须,但强烈建议预留,后面所有“串口上报”功能都要用到它。如果手头是ISP下载线,注意下载线和串行调试口不要同时占用相同的引脚,否则通信互相干扰。

4. 软件实现:从DHT11驱动到阈值报警的完整流程

4.1 开发环境与工程结构

C51开发环境基本上是一套IDE加编译器,支持标准C的子集,但和桌面C有一些区别:变量类型要显式用unsigned char减少RAM占用,函数参数尽量少,中断函数有固定写法。我习惯把工程拆成dht11.c、lcd1602.c、main.c三个文件,再加一个公共头文件。这样传感器驱动、显示驱动和业务逻辑分开,以后想换成SHT传感器或OLED屏,只动对应模块。

// main.c #include <reg52.h> #include "dht11.h" #include "lcd1602.h" #include "alarm.h" void main(void) { unsigned char temp, humi; lcd_init(); lcd_write_str(0, 0, "System Ready"); delay_ms(500); while (1) { if (dht11_read_data()) // 读传感器并校验 { humi = dht11_buf[0]; // 湿度整数 temp = dht11_buf[2]; // 温度整数 lcd_show_temp_humi(temp, humi); alarm_judge(temp, humi); } else { lcd_write_str(0, 1, "Sensor Error"); } delay_ms(2000); // 2秒读一次,满足DHT11采样周期 } }

代码里最需要注意的是dht11_read_data()返回1时才算读到有效数据,否则LCD显示“Sensor Error”。温度湿度都只取了整数位,做监控完全够用;如果你显示到小数位,就把dht11_buf[1]和dht11_buf[3]也取出来,拼成字符串。主循环用2000ms延时是故意不让你读太快,DHT11两次采样间隔必须大于1秒,读太快传感器会不响应。

4.2 DHT11驱动代码:起始信号、读40位、校验

DHT11驱动是整个项目的核心,也是最容易出问题的部分。下面是完整驱动代码,包含起始信号、读字节和校验。

// dht11.c #include <reg52.h> #include <intrins.h> sbit DHT11_DQ = P2^0; unsigned char dht11_buf[5]; // 湿度整、湿度小、温度整、温度小、校验 void delay_us(unsigned int us) { while (us--) { _nop_(); _nop_(); } } void delay_ms(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) { for (j = 0; j < 110; j++); } } unsigned char dht11_read_byte(void) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { while (!DHT11_DQ); // 等待50us低电平结束 delay_us(40); // 延时到数据位中间 if (DHT11_DQ) dat = (dat << 1) | 0x01; // 读到高电平,这一位是1 else dat = (dat << 1); // 读到低电平,这一位是0 while (DHT11_DQ); // 等待高电平结束,准备下一位 } return dat; } unsigned char dht11_read_data(void) { unsigned char i; DHT11_DQ = 1; DHT11_DQ = 0; delay_ms(18); // 起始信号,拉低至少18ms DHT11_DQ = 1; delay_us(30); // 拉高20~40us if (!DHT11_DQ) // 传感器响应信号 { while (!DHT11_DQ); // 等待80us低电平结束 delay_us(40); while (DHT11_DQ); // 等待80us高电平结束 for (i = 0; i < 5; i++) { dht11_buf[i] = dht11_read_byte(); } if ((dht11_buf[0] + dht11_buf[1] + dht11_buf[2] + dht11_buf[3]) == dht11_buf[4]) return 1; } return 0; }

逻辑说明:dht11_read_byte()里第一个while (!DHT11_DQ)是把数据位开头的50us低电平跳过,稳定后延时40us采样。delay_us(40)不是绝对精确,但必须保证在26us到70us之间采样,太早会分辨不出0和1,太晚可能已经跳到下一位的低电平。delay_ms(18)是用循环近似18ms,晶振频率不同实际时间略有偏差,只要不低于18ms就行,多几百微秒不影响。

参数说明:DHT11只有三个引脚,数据线必须接P2.0;如果换了引脚,只需改sbit DHT11_DQ = P2^0;这一行。校验失败返回0后,主程序会显示Sensor Error,不要连续重试,等下一次主循环的2秒间隔再读,给传感器留出恢复时间。

4.3 LCD1602显示与报警状态

LCD1602驱动代码网上版本很多,但我更推荐只写不读版本:RW接地,每次写命令或写数据后都延时几毫秒。下面这段初始化代码兼容市面上大多数1602液晶。

// lcd1602.c #include <reg52.h> #include <intrins.h> sbit LCD_RS = P2^5; sbit LCD_RW = P2^6; sbit LCD_E = P2^7; #define LCD_DATA P0 void delay_ms(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 110; j++); } void lcd_write_cmd(unsigned char cmd) { LCD_RS = 0; LCD_RW = 0; LCD_DATA = cmd; LCD_E = 1; delay_ms(2); LCD_E = 0; } void lcd_write_data(unsigned char dat) { LCD_RS = 1; LCD_RW = 0; LCD_DATA = dat; LCD_E = 1; delay_ms(2); LCD_E = 0; } void lcd_init(void) { delay_ms(15); lcd_write_cmd(0x38); // 8位数据、2行显示、5x7字型 lcd_write_cmd(0x0C); // 显示开、光标关 lcd_write_cmd(0x06); // 写入后地址自动加1 lcd_write_cmd(0x01); // 清屏 delay_ms(2); } void lcd_write_str(unsigned char line, unsigned char col, unsigned char *str) { unsigned char addr = 0; if (line == 0) addr = 0x80 + col; else if (line == 1) addr = 0xC0 + col; lcd_write_cmd(addr); while (*str) { lcd_write_data(*str++); } }

逻辑说明:0x38是必须的初始化命令,8位数据模式。0x0C关掉光标,不然屏幕上会有一个闪烁的方块,影响读数。写字符串函数里第一行和第二行地址不同,第一行从0x80开始,第二行从0xC0开始,这个地址是硬件规定的,写错会跑到另一行去。如果你LCD的数据线不是P0口,记得把#define LCD_DATA P0改成对应端口。

显示温湿度时,我一般直接把字符拼接好再调用lcd_write_str,避免用sprintf增加代码量。比如温度变量temp是26,就拼成“T:26C H:60%”。这里有一个小坑:数字要转换成ASCII字符,temp / 10 + '0'取十位,temp % 10 + '0'取个位。忘了加字符偏移,‘0’会让液晶显示一堆乱码或花屏。

4.4 主循环与按键阈值设置:一个可上手的完整逻辑

报警逻辑是“超阈值”才会响,不是温湿度一变就响。我设置两个阈值,温度上限默认28℃,湿度下限默认40%。按键P3.2进入设置模式,按键P3.3调整当前值。项目里还要加入报警标志,否则蜂鸣器会一直响个不停。

// alarm.c #include <reg52.h> sbit BEEP = P1^0; sbit KEY_SET = P3^2; sbit KEY_UP = P3^3; unsigned char temp_max = 28; // 温度上限 unsigned char humi_min = 40; // 湿度下限 void delay_ms(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 110; j++); } unsigned char key_scan(void) { if (!KEY_SET) { delay_ms(10); if (!KEY_SET) return 1; } if (!KEY_UP) { delay_ms(10); if (!KEY_UP) return 2; } return 0; } void alarm_judge(unsigned char temp, unsigned char humi) { if (temp > temp_max || humi < humi_min) { BEEP = 0; // 低电平触发蜂鸣器 } else { BEEP = 1; } }

逻辑说明:蜂鸣器接法如果是三极管驱动,低电平导通,所以报警时把引脚拉低。按键扫描用了两次判断加10ms延时消抖,第一个if里检测到按下,第二个if里确认还是按下,才返回键值,避免抖动导致误触发。设置模式里,如果键值等于1就切换当前调节项,等于2就在当前项上加1。这个实现没有处理长按连续加,只是演示;实际产品里建议每200ms检测一次按键状态,按下超过500ms就自动连续加。

报警判断很简单,但很多人在这个模块翻车:阈值设置一不小心和报警判断耦合在一起,调完上限再去调下限,当前项指针被复位,出现误报警。解决办法是设置模式下不执行报警判断,只有退出设置模式后才调用alarm_judge()。你可以在主循环里加一个mode标志,当mode不为0时只显示和调阈值,不做报警,这样既能看到调整结果,也不会让蜂鸣器打扰操作。

5. 温湿度采集监控排查指南:时序、上拉、供电与显示这些坑

5.1 DHT11一直返回0或超时:晶振频率与延时误差

现象:LCD上始终显示Sensor Error,偶尔能出一次数据,但很快又报错。用万用表量数据线电压,发现通信时只有几个很窄的脉冲,不像正常数据帧。

原因:DHT11对起始信号有严格要求,主机拉低不能低于18ms。很多人直接套网上代码,里面延时函数假设晶振是12MHz,而你用的是11.0592MHz,循环延时参数就不对了。更严重的是while(!DHT11_DQ)没有超时保护,如果传感器没响应,程序会一直死等,整个采集监控系统看起来像死机了。

解决:先用示波器或逻辑分析仪看DHT11数据脚的波形,确认起始信号低电平宽度是否够。没有示波器的话,把delay_ms(18)改成delay_ms(25),多给点余量。所有while等待循环都加上一个超时计数,超过一定次数就返回失败,比如等待低电平时定义一个timeout=100,循环里每次减1,减到0就return。这个习惯能救很多次,不只是DHT11,任何单总线设备都适用。

5.2 温度正常、湿度跳变:电源噪声与采样间隔

现象:温度读数稳定,湿度显示忽大忽小,比如上一秒60%,下一秒变成0%,而且传感器距单片机很近。

原因:湿度数据对时序比温度数据更敏感,因为湿度字节是数据帧前两个字节,如果起始信号不干净,第一个字节最容易出错。另一个常见因素是电源纹波,继电器、风扇启动瞬间把5V拉低,DHT11的数据输出也跟着乱。如果供电用的是电脑USB口,线太长也会有压降。

解决:给单片机电源并一个大电容,比如100uF电解电容,最好加在DHT11模块旁边。同时保证采样间隔至少2秒,不要在主循环里频繁读取。我测试过,把采样间隔从500ms改成2s,湿度跳变概率明显下降。如果还跳,排查4.7k上拉电阻有没有虚焊,上拉电阻和DHT11模块之间不要用长跳线。

5.3 液晶只亮不显示字符:对比度电位器与忙检测

现象:LCD背光亮,屏幕有黑块或者全空白,无论写什么命令都不显示,换一块新LCD也一样。

原因:背光正常只能说明电源接对了,不代表LCD初始化成功。第一个常见原因是VL脚电压不对,对比度太高或太低都会导致看不到字。第二个原因是数据线P0口没有上拉,P0输出高电平时实际上高阻态,LCD识别不了命令。第三个原因是初始化命令里连续写太快,没给LCD足够的处理时间。

解决:先把VL脚电位器调到中间偏GND位置,再观察屏幕有没有黑块出现。如果没有黑块,检查P0排阻;如果有黑块但不显示字符,把初始化每条命令后的延时加大到5ms,特别是0x38和0x01。再不行就把RW接地改成读忙标志方式,或者直接购买带转接板的I2C LCD模块,省去大量接线,但驱动代码就要改成I2C协议。

5.4 蜂鸣器乱叫:按键消抖与阈值比较逻辑

现象:系统刚上电蜂鸣器就响,或者按一下设置键报警状态反复变化,甚至不按任何键也会触发。

原因:按键扫描没有消抖,设置键按下时产生多次高低电平抖动,被当成多次按键操作,阈值被意外改大改小。阈值比较逻辑也有问题,比如比较运算符写反,或者把温度和湿度上限混在一起。另一个隐蔽原因是P3.2、P3.3引脚内部没有上拉,按键悬空时引脚状态不确定,读到低电平就以为被按下。

解决:按键引脚外部加上拉电阻,软件消抖延时10ms以上;如果按键接在P3口,还要确认开发板上有没有默认上拉。报警判断里要先判断是否处于设置模式,只有非设置模式下才更新蜂鸣器状态。我习惯再加一个“报警锁存”标志,出现报警后必须按一次键才能解除,避免蜂鸣器一直响到断电。

5.5 烧录后没有任何反应:“冷启动”与复位电路

现象:程序编译通过,点击下载,进度条一直停在“正在检测目标单片机”,单片机没有任何反应,LED也不闪。

原因:绝大多数是下载握手时序不对,51下载需要先让单片机处于复位状态,下载器发出连接信号,再上电。很多人直接把下载器和单片机连好,单片机已经上电了,点击下载永远等不到冷启动。也有可能是复位电路不正常,RST引脚一直为高,单片机卡在复位状态。

解决:连接好串口后,先把下载器切换到“下载模式”,点击下载按钮,然后立刻给目标板重新上电。如果目标板有电源开关,每次下载都拨一次开关。检查复位电容和电阻是否接反,复位电容正极接VCC,负极接RST,电解电容方向接反会导致上电复位时间极短甚至没有复位。用USB供电时,还要先把USB线插好,再按下载器的冷启动按钮。

6. 进阶玩法:串口上报、EEPROM掉电保存和低功耗改造

6.1 用串口把温湿度报给上位机

把温湿度监控从“本地显示”升级成“远程可看”,最小改动就是加串口上报。使用11.0592MHz晶振,波特率设为9600。发送数据时我加帧头和帧尾,上位机按帧解析,避免把连续两帧数据混在一起。

void uart_init(void) { TMOD = 0x20; // 定时器1工作在模式2,8位自动重装 TH1 = 0xFD; // 9600波特率对应初值 TL1 = 0xFD; TR1 = 1; SM0 = 0; SM1 = 1; // 串口工作在方式1 } void uart_send_byte(unsigned char dat) { SBUF = dat; while (!TI); TI = 0; } void uart_send_temp_humi(unsigned char temp, unsigned char humi) { uart_send_byte(0xAA); // 帧头 uart_send_byte(temp); uart_send_byte(humi); uart_send_byte(0x55); // 帧尾 }

逻辑说明:TMOD=0x20把定时器1设成8位自动重装模式,这是串口波特率最常用的配置。TH1=0xFD对应11.0592MHz下的9600,如果你用12MHz晶振,这个值误差偏大,接收端可能会偶尔收到乱码,这也是前面建议用11.0592MHz的原因。uart_send_byte里等待TI标志置1,发送完成后TI要手动清零,否则下一字节发不出去。

6.2 阈值掉电保存与验证方法

每次手工设置阈值,断电后恢复默认值会很麻烦。增强型51芯片大多自带EEPROM,可以拿来存阈值。写入前要先把目标地址擦除,写入完成后读回验证。我只存temp_max和humi_min两个字节,读出后和预期值比对,不一致再写一次。注意EEPROM写次数有限制,不要在循环里反复写,只有按键调整且确认退出时才写。

验证方法也很朴素:把温度上限临时改成28℃,用电烙铁靠近传感器,等温度超过28℃蜂鸣器响,然后把温度上限调回30℃,再靠近传感器确认不响。湿度下限验证可以用保鲜袋包住传感器,向袋子里哈气,湿度超过下限后蜂鸣器停止或报警解除。整个验证过程不需要任何额外仪器,但能同时检验传感器、阈值、报警三条链路是否都正常。

6.3 低功耗改造与最终校准技巧

如果这个采集监控要长期放在仓库里,功耗问题就来了。最简单有效的办法是把采样周期从2秒改到30秒或60秒,DHT11只在唤醒后读一次,其余时间单片机进入空闲模式。继电器和蜂鸣器改成由三极管/MOS管驱动,别用单片机引脚直驱,既能省电也能保护引脚。用电池供电时,把LCD背光调暗或定时关闭,这样整机平均电流能压到几毫安。

最后做一次传感器校准:把DHT11和标准温湿度计放在同一个密封袋里,等温度稳定后记录偏差。偏差是固定值时,直接在显示代码里加上修正偏移,比如显示温度为“读到的温度+1”。这种校准方法没有温度箱也能做,比盲目相信传感器厂商标称精度更可靠。我自己做这类项目时,最大的教训不是代码难写,而是太相信硬件默认值,几乎每次都需要用实物验证一遍阈值。希望这篇内容能让你少走几趟弯路,做完之后也能自己加上串口上报和掉电保存,把一个小监控真正用在需要的地方。希望帮到你。

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

RELATED READING

延伸阅读

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