1. 项目概述
温湿度检测是环境监测中最基础也最实用的功能之一。作为一名电子爱好者,我最近用单片机完成了一个温湿度检测系统,成本不到50元,精度却能达到专业级水平。这个项目非常适合刚接触嵌入式开发的新手练手,也适合需要环境监测的智能家居、农业大棚等场景。
市面上常见的温湿度传感器模块价格从几元到上百元不等,我选择了性价比较高的DHT22模块,搭配STM32F103C8T6最小系统板(也就是大家常说的"蓝板")。整个系统可以实时采集环境温湿度数据,并通过串口输出到电脑,或者连接OLED屏幕就地显示。下面我就详细分享这个项目的完整实现过程。
2. 硬件选型与电路设计
2.1 核心器件选型
单片机选择:
STM32F103C8T6是最经典的入门级ARM Cortex-M3芯片,72MHz主频,64KB Flash,20KB RAM,完全能满足这个项目的需求。相比Arduino,STM32的性能更强、价格更低(最小系统板仅10元左右),而且可以学习更底层的嵌入式开发。
传感器选择:
DHT22(也称AM2302)是数字式温湿度传感器,测量范围-40~80℃(±0.5℃精度),0~100%RH(±2%RH精度),单总线通信。相比更便宜的DHT11,DHT22的精度更高;相比I2C接口的SHT30,DHT22的接线更简单。
其他配件:
- 0.96寸OLED显示屏(SSD1306驱动,I2C接口)
- 杜邦线若干
- MicroUSB数据线
- 可选:洞洞板、电阻、LED等
2.2 电路连接示意图
code复制DHT22引脚说明:
1. VCC -> 3.3V
2. DATA -> PA1 (需接4.7K上拉电阻)
3. NC
4. GND -> GND
OLED引脚说明:
SCL -> PB6
SDA -> PB7
VCC -> 3.3V
GND -> GND
注意:DHT22的工作电压是3.3V-5.5V,但DATA引脚信号电平是3.3V,如果使用5V供电的单片机(如Arduino),需要在DATA线上加电平转换电路。
3. 软件开发环境搭建
3.1 工具链安装
我选择使用PlatformIO + VSCode作为开发环境,相比Keil或IAR更轻量且跨平台:
- 安装VSCode
- 在扩展商店搜索安装PlatformIO IDE
- 新建项目,选择板型为"ST STM32F103C8T6"
- 安装所需库:
- DHT sensor library by Adafruit
- U8g2 (用于OLED驱动)
3.2 代码框架设计
项目主要分为三个功能模块:
- 传感器数据采集
- 数据显示处理
- 数据输出(串口/屏幕)
cpp复制#include <DHT.h>
#include <U8g2lib.h>
#define DHTPIN PA1
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
void setup() {
Serial.begin(115200);
dht.begin();
u8g2.begin();
}
void loop() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("读取传感器失败!");
return;
}
// 串口输出
Serial.print("湿度: "); Serial.print(h); Serial.print("%");
Serial.print(" 温度: "); Serial.print(t); Serial.println("°C");
// OLED显示
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.setCursor(0, 20);
u8g2.print("Temp: "); u8g2.print(t); u8g2.print("C");
u8g2.setCursor(0, 45);
u8g2.print("Humi: "); u8g2.print(h); u8g2.print("%");
u8g2.sendBuffer();
delay(2000); // 2秒更新一次
}
4. 关键技术与实现细节
4.1 单总线通信协议解析
DHT22使用单总线协议,通信时序非常严格:
- 主机拉低总线至少1ms(启动信号)
- 主机释放总线,等待20-40us
- DHT22拉低总线80us作为响应
- DHT22拉高总线80us准备发送数据
- 发送40位数据(16bit湿度+16bit温度+8bit校验和)
每位数据的"0"和"1"通过高电平持续时间区分:
- 0: 26-28us高电平
- 1: 70us高电平
实操技巧:在STM32上最好用硬件定时器捕获边沿时间,软件延时精度不够可能导致读取失败。
4.2 温度补偿算法
DHT22的湿度读数会受温度影响,需要进行补偿。补偿公式如下:
code复制真实湿度 = 传感器读数 / (1.0546 - 0.00216 * t) (t为当前温度℃)
在代码中可以这样实现:
cpp复制float realHumidity = h / (1.0546 - 0.00216 * t);
4.3 低功耗优化
如果项目需要电池供电,可以采取以下优化措施:
- 将STM32设置为睡眠模式,定时唤醒采集数据
- 采集完成后立即关闭传感器电源
- 减少OLED刷新频率(如每10秒刷新一次)
- 降低主频到8MHz
典型电流消耗对比:
- 全速运行:约30mA
- 优化后:平均<5mA
5. 项目扩展与应用
5.1 无线传输版本
通过添加ESP8266模块,可以将数据上传到物联网平台:
- 硬件连接:ESP8266的TX/RX接STM32的串口
- 使用AT指令连接WiFi
- 通过MQTT协议上传数据到服务器
cpp复制// 示例MQTT发布代码
client.publish("sensor/temperature", String(t).c_str());
client.publish("sensor/humidity", String(h).c_str());
5.2 数据记录与分析
添加SD卡模块,可以创建CSV格式的数据日志:
code复制2023-08-20 14:30:00, 26.5, 45.2
2023-08-20 14:35:00, 26.7, 44.8
...
使用Python可以轻松分析这些数据:
python复制import pandas as pd
df = pd.read_csv('dht22_log.csv')
df.plot(title='温湿度变化曲线')
5.3 报警功能实现
当温湿度超出设定范围时,可以触发蜂鸣器或LED报警:
cpp复制if(t > 30.0 || h > 80.0) {
digitalWrite(BUZZER_PIN, HIGH);
u8g2.setFont(u8g2_font_unifont_t_symbols);
u8g2.drawUTF8(0, 60, "⚠ 警报!");
}
6. 常见问题与解决方法
6.1 传感器读取失败
现象:串口输出"读取传感器失败"
可能原因:
- 接线错误(特别是上拉电阻)
- 供电不足(电流至少2.5mA)
- 时序问题(STM32主频太高导致延时不准)
解决方法:
- 检查DATA线是否接了4.7K上拉电阻
- 尝试给DHT22单独供电
- 降低STM32主频或改用硬件定时器
6.2 OLED显示乱码
现象:屏幕显示异常字符或闪烁
可能原因:
- I2C地址不匹配(通常0x3C或0x78)
- 刷新太快导致缓冲区冲突
- 电源干扰
解决方法:
cpp复制// 初始化时指定正确地址
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, PB6, PB7, U8X8_PIN_NONE, 0x3C);
6.3 数据波动大
现象:温湿度读数不稳定
解决方法:
- 软件滤波(取多次平均值)
cpp复制#define SAMPLE_TIMES 5
float sum = 0;
for(int i=0; i<SAMPLE_TIMES; i++){
sum += dht.readTemperature();
delay(10);
}
float avgTemp = sum / SAMPLE_TIMES;
- 避免将传感器靠近发热元件
- 在传感器周围加装防尘罩
7. 项目优化建议
-
外壳设计:使用3D打印或塑料盒制作防水外壳,特别适合农业应用
-
校准方法:用标准温湿度计对比读数,在代码中添加校准偏移量
cpp复制#define TEMP_OFFSET -0.5
#define HUMI_OFFSET 2.0
float calibratedTemp = t + TEMP_OFFSET;
float calibratedHumi = h + HUMI_OFFSET;
-
多传感器组网:通过RS485总线连接多个DHT22,实现大面积监测
-
太阳能供电:搭配18650电池和TP4056充电模块,实现完全无线化
这个项目虽然简单,但涵盖了嵌入式开发的完整流程:硬件选型、电路设计、编程实现、调试优化。我在实际开发中发现,DHT22的响应速度比规格书上标的要慢,连续读取时需要间隔至少2秒,否则容易失败。另外,STM32的GPIO配置要特别注意,PA0默认是调试端口,如果用作普通IO需要先禁用调试功能。