STM32实战:从零搭建智能温湿度监控与本地报警系统
1. 项目背景与硬件选型
最近在整理工作室时翻出一个闲置的STM32F103开发板,正好手头有DHT11温湿度传感器和1602液晶屏,就想着做个实用的环境监测装置。这个项目特别适合刚接触嵌入式开发的朋友,既能学习基础外设驱动,又能掌握完整的系统开发流程。
核心硬件清单:
- 主控芯片:STM32F103C8T6(性价比之王,72MHz主频完全够用)
- 温湿度传感器:DHT11(精度±2℃/±5%RH,实测响应速度不错)
- 显示模块:1602液晶屏(带I2C转接板节省IO口)
- 报警模块:有源蜂鸣器+LED灯(声光双重提醒)
- 调试工具:ST-Link V2下载器(建议买带串口功能的版本)
提示:DHT11虽然精度一般,但胜在价格便宜(某宝5元包邮),对于非精密测量场景完全够用。若需要更高精度,可以考虑SHT30或BME280。
我在硬件连接上踩过两个坑:一是DHT11的数据线没加上拉电阻导致通信不稳定,二是蜂鸣器直接接GPIO口烧过一个IO(后来改用三极管驱动)。建议新手务必参考下面的接线表:
| 模块 | STM32引脚 | 备注 |
|---|---|---|
| DHT11 DATA | PA1 | 需接4.7K上拉电阻 |
| I2C SCL | PB6 | 1602液晶屏时钟线 |
| I2C SDA | PB7 | 1602液晶屏数据线 |
| 蜂鸣器 | PA8 | 通过S8050三极管驱动 |
| LED指示灯 | PC13 | 板载LED也可复用 |
2. 开发环境搭建
刚开始玩STM32时被各种开发工具搞晕过,现在推荐用最轻量的组合:VSCode + PlatformIO插件。相比Keil和IAR,这个方案有代码补全、语法高亮等现代IDE功能,关键是跨平台支持好。
具体安装步骤:
- 安装VSCode后搜索安装PlatformIO IDE插件
- 创建新项目时选择"STM32F103C8"开发板
- 在platformio.ini中添加依赖库:
lib_deps = dht11-library LiquidCrystal_I2C遇到库版本冲突时,我总结的解决方法是:
- 删除.pio/libdeps目录下冲突的库
- 在platformio.ini中指定库版本号
- 手动下载库文件放到lib目录
调试阶段建议开启串口打印功能,在main.cpp中加入:
void setup() { Serial.begin(115200); while (!Serial); // 等待串口连接 printf("System init OK!\n"); }3. 传感器驱动开发
DHT11的时序控制是第一个难点,这个单总线器件对时序要求严格。我最初用延时函数实现,结果发现温度读取不稳定,后来改用状态机方式重写才解决。
优化后的读取流程:
- 主机拉低总线18ms后释放
- 等待传感器响应信号(83us低电平+87us高电平)
- 接收40bit数据(每位以50us低电平开头)
- 校验前32bit数据和校验位
实测有效的读取代码片段:
uint8_t DHT11_Read() { uint8_t data[5] = {0}; // 触发信号 pinMode(DHT_PIN, OUTPUT); digitalWrite(DHT_PIN, LOW); delay(18); digitalWrite(DHT_PIN, HIGH); delayMicroseconds(30); // 接收响应 pinMode(DHT_PIN, INPUT); while(digitalRead(DHT_PIN) == HIGH); while(digitalRead(DHT_PIN) == LOW); while(digitalRead(DHT_PIN) == HIGH); // 读取数据位 for(int i=0; i<5; i++) { for(int j=0; j<8; j++) { while(digitalRead(DHT_PIN) == LOW); delayMicroseconds(30); data[i] <<= 1; if(digitalRead(DHT_PIN) == HIGH) data[i] |= 1; while(digitalRead(DHT_PIN) == HIGH); } } // 校验数据 if(data[4] == (data[0]+data[1]+data[2]+data[3])) { temperature = data[2]; humidity = data[0]; return 1; } return 0; }4. 人机交互实现
显示部分我选用了最常见的1602液晶屏,通过PCF8574转接板可以只用两根线(I2C)控制。这里有个坑点:不同厂家的转接板I2C地址可能不同,需要用扫描工具确认。
完整的显示控制流程:
- 初始化I2C接口(STM32的硬件I2C有时不稳定,可以用软件模拟)
- 设置液晶屏显示模式(2行16字符,5x8点阵)
- 实现自定义字符功能(比如温度符号℃)
- 定期刷新显示内容
实测稳定的刷新策略:
- 每1秒更新一次温湿度数值
- 当数值超过阈值时闪烁显示
- 报警状态下显示特殊标识符
关键代码示例:
LiquidCrystal_I2C lcd(0x27, 16, 2); void updateDisplay() { lcd.setCursor(0,0); lcd.print("Temp: "); lcd.print(temperature); lcd.write(0xDF); // ℃符号 lcd.print("C"); lcd.setCursor(0,1); lcd.print("Humi: "); lcd.print(humidity); lcd.print("% "); if(alarm_status) { lcd.print("[ALARM]"); digitalWrite(BUZZER_PIN, !digitalRead(BUZZER_PIN)); // 蜂鸣器鸣叫 } }5. 报警逻辑设计
报警功能看似简单,但要做好用户体验需要考虑多个细节。我的实现方案包含三级预警:
- 初级预警(温度>30℃或湿度>70%):LED慢闪
- 中级预警(温度>35℃或湿度>80%):LED快闪
- 严重预警(温度>40℃或湿度>90%):声光全开
状态机实现的关键点:
- 使用定时器中断实现非阻塞式闪烁控制
- 报警阈值通过按键可调(长按进入设置模式)
- 增加报警延时判断(持续3秒超限才触发)
按键消抖的实用技巧:
uint8_t readKey() { static uint32_t last_time = 0; if(millis() - last_time < 50) return 0; // 消抖周期 if(digitalRead(KEY_PIN) == LOW) { last_time = millis(); return 1; } return 0; }6. 系统优化与调试
项目基本功能完成后,我花了大量时间优化稳定性。这里分享几个实用技巧:
电源管理优化:
- 添加100uF电容稳压(防止传感器工作时电压波动)
- 在DHT11电源脚并联0.1uF去耦电容
- 使用看门狗定时器防死机
抗干扰措施:
- 传感器线长超过20cm时加磁珠滤波
- I2C总线加1K上拉电阻(默认4.7K可能不够)
- 关键代码段禁用中断
调试时发现一个诡异问题:蜂鸣器鸣叫时温湿度读数异常。最终发现是电源电流不足,解决方法:
- 给蜂鸣器单独供电
- 在蜂鸣器控制信号加光耦隔离
- 错开传感器采集和蜂鸣器工作时间
7. 项目进阶方向
完成基础功能后,可以尝试这些扩展:
- 增加历史数据记录功能(使用SPI Flash存储)
- 通过蓝牙模块连接手机APP
- 添加光强传感器实现联动控制
- 移植FreeRTOS实现多任务管理
一个实用的数据存储方案:
void saveData() { EEPROM.write(addr++, temperature); EEPROM.write(addr++, humidity); if(addr >= EEPROM.length()) addr = 0; // 每10次写入执行一次提交 if(++writeCount >= 10) { EEPROM.commit(); writeCount = 0; } }在面包板上测试通过后,建议设计PCB板固化作品。使用立创EDA绘制电路图时,注意:
- 给STM32预留SWD调试接口
- 电源走线适当加粗
- 传感器接口用排针兼容多种模块
