1. 项目概述
实验室环境监测是个看似简单却暗藏玄机的活儿。作为一名在嵌入式领域摸爬滚打多年的工程师,我见过太多实验室因为环境监测不到位导致的数据丢失、设备损坏甚至安全事故。这次我们用STM32打造的环境监测系统,就像给实验室装上了"电子感官",能实时感知温湿度、烟雾和有害气体的变化。
这个系统最核心的价值在于:它不只是被动采集数据,还能主动预警和处置环境异常。当检测到烟雾或有害气体超标时,系统会自动开启排风扇并通过WiFi向手机APP发送报警信息,整个过程无需人工干预。这种自动化响应机制在关键时刻能避免重大损失。
2. 硬件选型与设计
2.1 主控芯片选择
STM32F103C8T6是我们这个项目的核心大脑。选择这款芯片主要基于三点考虑:
- 丰富的外设接口:它自带多个ADC通道、定时器和USART接口,完美适配我们的传感器阵列
- 性价比优势:相比同性能的Arduino方案,价格低30%左右
- 开发便利性:基于ARM Cortex-M3内核,有成熟的工具链和丰富的库函数支持
提示:如果预算充足,可以考虑STM32F4系列,其内置硬件浮点运算单元在处理传感器数据时会更高效。
2.2 传感器阵列配置
2.2.1 温湿度检测
DHT11虽然精度(±2℃, ±5%RH)不如更高级的SHT30,但其单总线接口和5V供电特性使其成为入门级项目的理想选择。实际部署时要注意:
- 传感器应远离空调出风口和阳光直射
- 安装高度建议1.2-1.5米(人体呼吸带高度)
- 每20平方米至少部署1个监测点
2.2.2 烟雾检测
MQ-2烟雾传感器的关键参数:
- 检测范围:300-10000ppm
- 预热时间:≥24小时(首次使用)
- 响应时间:<10秒
- 工作电流:150mA
这个传感器对液化气、丙烷、氢气等多种可燃气体都有响应,但需要注意:
- 需要定期校准(建议每月一次)
- 避免安装在通风过强的位置
- 检测阈值需要根据具体环境调整
2.2.3 空气质量检测
我们选用的是CCS811 VOC传感器,它能检测:
- 总挥发性有机物(TVOC):0-1187ppb
- 等效二氧化碳(eCO2):400-8192ppm
这个传感器需要I2C接口驱动,且内置算法可以补偿温湿度影响。使用时要注意:
- 需要48小时老化时间才能获得稳定数据
- 每7天需要执行一次基线校准
- 避免高浓度酒精、香水等干扰源
2.3 外围电路设计
2.3.1 电源管理
系统采用双电源设计:
- 主电路:5V/2A开关电源
- 备份电源:3.7V/2000mAh锂电池(支持断电后继续工作2小时)
关键保护电路:
- 反接保护:SS34肖特基二极管
- 过压保护:SMBJ5.0A TVS管
- 滤波电路:100μF电解电容 + 0.1μF陶瓷电容组合
2.3.2 显示模块
0.96寸OLED显示屏(I2C接口)的驱动要点:
- 刷新率控制在30fps以内避免闪烁
- 使用内部RAM缓冲减少I2C通信次数
- 实现灰度显示需要自定义字符掩码
2.3.3 通信模块
ESP8266-01S WiFi模块的配置技巧:
- 固件建议使用ATv2.2.0以上版本
- 发送数据前先检查连接状态(AT+CIPSTATUS)
- 启用省电模式(AT+SLEEP=2)可降低30%功耗
3. 软件架构设计
3.1 系统流程图
整个系统的软件流程可以分为三个主要线程:
-
传感器采集线程(优先级最高)
- 温湿度:每5秒采集一次
- 烟雾浓度:每2秒采集一次
- 空气质量:每10秒采集一次
-
数据处理线程
- 滑动平均滤波
- 阈值比较
- 异常检测算法
-
通信线程
- 本地显示刷新:每秒1次
- 远程数据上传:每30秒一次
- 异常事件即时上报
3.2 关键算法实现
3.2.1 传感器数据滤波
采用加权滑动平均滤波算法:
c复制#define FILTER_SIZE 5
#define FILTER_WEIGHTS {0.4, 0.3, 0.15, 0.1, 0.05} // 近期数据权重更高
float weighted_filter(float new_val) {
static float buffer[FILTER_SIZE] = {0};
static uint8_t index = 0;
const float weights[FILTER_SIZE] = FILTER_WEIGHTS;
// 更新缓冲区
buffer[index] = new_val;
index = (index + 1) % FILTER_SIZE;
// 计算加权平均值
float sum = 0;
float weight_sum = 0;
for(uint8_t i=0; i<FILTER_SIZE; i++) {
uint8_t j = (index + i) % FILTER_SIZE;
sum += buffer[j] * weights[i];
weight_sum += weights[i];
}
return sum / weight_sum;
}
这种算法相比简单滑动平均更能快速响应突变信号,同时有效抑制随机干扰。
3.2.2 异常检测算法
我们实现了三级预警机制:
-
初级预警(超过阈值80%)
- OLED显示黄色警告图标
- 本地蜂鸣器短鸣一声
-
中级预警(超过阈值)
- 开启排风扇
- 蜂鸣器间歇报警
- APP推送通知
-
高级预警(持续超过阈值30秒)
- 触发邮件报警
- 记录详细数据日志
- 启动备用通风系统
3.3 通信协议设计
3.3.1 数据上报格式
采用精简JSON格式减少传输数据量:
json复制{
"dev_id":"LAB01",
"temp":23.5,
"humi":45.2,
"smoke":356,
"tvoc":125,
"eco2":650,
"alert":0
}
字段说明:
- dev_id:设备唯一标识
- alert:报警状态(0-正常,1-初级,2-中级,3-高级)
3.3.2 命令下发格式
手机APP下发的控制命令采用固定8字节格式:
| 字节 | 含义 | 取值 |
|---|---|---|
| 0 | 起始符 | 0xAA |
| 1 | 命令类型 | 0x01-查询,0x02-设置 |
| 2-3 | 参数地址 | 0x0001-温度阈值 |
| 4-7 | 参数值 | 小端格式 |
4. 关键代码解析
4.1 传感器驱动实现
4.1.1 DHT11温湿度传感器
c复制#define DHT11_TIMEOUT 100 // 超时时间(ms)
uint8_t DHT11_Read(float *temp, float *humi) {
uint8_t data[5] = {0};
GPIO_InitTypeDef gpio;
// 配置为推挽输出
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DHT11_PORT, &gpio);
// 启动信号(拉低18ms)
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET);
HAL_Delay(18);
// 切换为浮空输入
gpio.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(DHT11_PORT, &gpio);
// 等待传感器响应
uint32_t timeout = HAL_GetTick();
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET) {
if(HAL_GetTick() - timeout > DHT11_TIMEOUT) return 0;
}
// 读取40位数据
for(uint8_t i=0; i<5; i++) {
for(uint8_t j=0; j<8; j++) {
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_RESET);
uint32_t start = HAL_GetTick();
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET);
uint8_t bit_val = (HAL_GetTick() - start > 40) ? 1 : 0;
data[i] = (data[i] << 1) | bit_val;
}
}
// 校验和数据
if(data[4] != (data[0]+data[1]+data[2]+data[3])) return 0;
*humi = data[0] + data[1]*0.1;
*temp = data[2] + data[3]*0.1;
return 1;
}
注意:DHT11的时序要求严格,在读取数据位时建议使用硬件定时器而不是软件延时,否则在高主频下容易出错。
4.1.2 MQ-2烟雾传感器
c复制#define ADC_SMOOTHING 10 // ADC采样次数
uint16_t MQ2_Read(void) {
uint32_t sum = 0;
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
for(uint8_t i=0; i<ADC_SMOOTHING; i++) {
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
sum += HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
}
return sum / ADC_SMOOTHING;
}
4.2 报警逻辑实现
c复制void check_alert(void) {
static uint8_t alert_count = 0;
uint16_t smoke = MQ2_Read();
uint16_t tvoc = CCS811_GetTVOC();
// 烟雾报警判断
if(smoke > SMOKE_THRESHOLD) {
alert_count++;
if(alert_count > 3) {
set_alert_level(ALERT_HIGH);
send_email_alert();
alert_count = 0;
} else {
set_alert_level(ALERT_MEDIUM);
}
}
// TVOC报警判断
else if(tvoc > TVOC_THRESHOLD) {
alert_count++;
if(alert_count > 3) {
set_alert_level(ALERT_HIGH);
send_email_alert();
alert_count = 0;
} else {
set_alert_level(ALERT_MEDIUM);
}
}
// 恢复正常
else {
if(get_alert_level() > ALERT_NONE) {
clear_alert();
}
alert_count = 0;
}
}
5. 系统优化与调试
5.1 功耗优化技巧
-
动态时钟调整:
- 传感器采集时切换到72MHz
- 空闲时降频到24MHz
- 使用HSI内部时钟源减少外部晶振功耗
-
外设电源管理:
- 非连续使用的传感器通过MOSFET控制供电
- WiFi模块在数据传输间隙进入睡眠模式
- 显示屏在没有操作时降低亮度
-
代码级优化:
- 使用DMA传输减少CPU干预
- 将频繁调用的函数放入RAM运行
- 禁用未使用的硬件外设时钟
5.2 常见问题排查
5.2.1 传感器数据异常
可能原因及解决方案:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 温湿度读数固定 | 时序不符合要求 | 检查延时函数精度,改用硬件定时器 |
| 烟雾值持续偏高 | 传感器老化 | 更换传感器或重新校准 |
| TVOC数据跳变 | I2C干扰 | 缩短接线长度,增加上拉电阻 |
5.2.2 WiFi连接不稳定
优化建议:
- 增加天线:外接2.4GHz胶棒天线
- 调整发送间隔:从30秒延长到60秒
- 添加重连机制:
c复制void wifi_reconnect(void) { uint8_t retry = 0; while(retry < 3) { if(ESP8266_ConnectAP()) return; HAL_Delay(5000); retry++; } system_reset(); }
6. 项目扩展方向
6.1 硬件扩展
- 增加PM2.5检测:使用激光粉尘传感器
- 添加门禁联动:当环境异常时自动锁闭实验室
- 部署多个节点:构建无线传感网络
6.2 软件升级
- 实现OTA远程升级
- 增加数据本地存储(SD卡)
- 开发Web管理界面
6.3 数据分析
- 建立环境变化趋势模型
- 设置智能预警阈值
- 生成周/月环境报告
这个项目最让我满意的是它的可扩展性。随着实验室规模扩大,只需要简单复制硬件配置,修改设备ID就能快速部署新节点。在实际运行三个月后,系统成功预警了两次因设备过热导致的潜在火灾风险,验证了其可靠性。