1. 项目概述
这个STM32环境监测蓝牙终端项目是我在学习完江协科技STM32入门教程后的第一个综合实践项目。作为一个嵌入式开发新手,我选择这个项目是因为它涵盖了传感器数据采集、无线通信和数据显示等多个实用功能模块,非常适合用来巩固基础知识和提升实战能力。
项目核心功能是通过STM32F103C8T6开发板采集环境温湿度(DHT11)和一氧化碳浓度(MQ7)数据,然后通过HC-08蓝牙模块无线传输到另一块开发板,最终在OLED屏幕上轮询显示。整个系统分为两个部分:
- 传感器发送端:负责采集数据并通过蓝牙发送
- OLED显示端:接收数据并每10秒刷新显示
2. 硬件准备与接线
2.1 所需硬件清单
| 硬件模块 | 数量 | 备注 |
|---|---|---|
| STM32F103C8T6开发板 | 2 | 最小系统板即可 |
| DHT11温湿度传感器 | 1 | 数字信号输出 |
| MQ7一氧化碳传感器 | 1 | 需要ADC采集 |
| HC-08蓝牙模块 | 2 | 主从配对使用 |
| 0.96寸OLED屏幕(IIC) | 1 | 用于数据显示端 |
| 杜邦线 | 若干 | 建议使用不同颜色区分 |
2.2 硬件连接说明
传感器发送端接线
| STM32引脚 | 连接模块 | 备注 |
|---|---|---|
| PA2 | DHT11 DATA | 单总线通信 |
| PA0 | MQ7 AO | ADC采集引脚 |
| PA9(TX) | HC-08 RX | 蓝牙模块串口 |
| PA10(RX) | HC-08 TX | 蓝牙模块串口 |
| 3.3V | 各模块VCC | 电源正极 |
| GND | 各模块GND | 电源负极 |
注意:MQ7传感器需要预热时间,初次上电需要等待1-2分钟才能稳定输出数据
OLED显示端接线
| STM32引脚 | 连接模块 | 备注 |
|---|---|---|
| PB6 | OLED SCL | IIC时钟线 |
| PB7 | OLED SDA | IIC数据线 |
| PA9(TX) | HC-08 RX | 蓝牙模块串口 |
| PA10(RX) | HC-08 TX | 蓝牙模块串口 |
| 3.3V | 各模块VCC | 电源正极 |
| GND | 各模块GND | 电源负极 |
3. 核心模块实现详解
3.1 DHT11温湿度传感器驱动
DHT11是一款低成本数字温湿度传感器,采用单总线通信协议。它的测量范围为:
- 温度:0-50℃ (±2℃精度)
- 湿度:20-90%RH (±5%RH精度)
3.1.1 通信时序解析
DHT11的通信过程分为5个阶段:
-
主机启动信号:
- 拉低数据线至少18ms
- 然后拉高20-40μs等待响应
-
从机响应信号:
- 从机拉低80μs
- 然后拉高80μs
-
数据传输:
- 每次传输40位数据(5字节)
- 数据格式:湿度整数+湿度小数+温度整数+温度小数+校验和
-
数据位表示:
- 位"0":50μs低电平+26-28μs高电平
- 位"1":50μs低电平+70μs高电平
-
结束信号:
- 从机拉低50μs后释放总线
3.1.2 关键代码实现
c复制// DHT11初始化函数
uint8_t DHT_Init(void){
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(DHT11_GPIOx, &GPIO_InitStructure);
// 发送起始信号
GPIO_WriteBit(DHT11_GPIOx, DHT11_GPIO_Pin, Bit_RESET);
Delay_ms(20);
GPIO_WriteBit(DHT11_GPIOx, DHT11_GPIO_Pin, Bit_SET);
Delay_us(30);
// 切换为输入模式等待响应
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DHT11_GPIOx, &GPIO_InitStructure);
// 检测从机响应
uint8_t time = 0;
while(GPIO_ReadInputDataBit(DHT11_GPIOx, DHT11_GPIO_Pin) == SET){
time++;
Delay_us(1);
if(time > 100) return 0; // 响应超时
}
// ...省略后续响应检测代码
return 1;
}
3.1.3 数据校验与处理
DHT11发送的5字节数据中,前4字节的和应该等于第5字节(校验和)。如果校验失败,应该丢弃本次数据。
c复制if(Data[0] + Data[1] + Data[2] + Data[3] == Data[4]){
*W = Data[0]; // 湿度整数
*W2 = Data[1]; // 湿度小数
*T = Data[2]; // 温度整数
*T2 = Data[3]; // 温度小数
}
注意事项:DHT11的采样间隔不应小于1秒,否则可能导致数据不准确。在实际应用中,建议每2秒采集一次数据。
3.2 MQ7一氧化碳传感器驱动
MQ7是一种半导体气敏传感器,对一氧化碳(CO)具有高灵敏度。它输出模拟电压信号,需要通过ADC转换为数字值。
3.2.1 传感器特性
- 检测范围:10-1000ppm CO
- 预热时间:不少于48小时(首次使用)
- 工作电压:5V±0.1V
- 加热电压:需要周期切换(高5V/低1.4V)
3.2.2 ADC采集实现
STM32的ADC配置需要注意以下几点:
- 时钟配置:ADC时钟不应超过14MHz
- 采样时间:对于MQ7这种变化较慢的信号,55.5周期采样时间足够
- 参考电压:使用开发板上的3.3V作为VREF
c复制uint16_t MQ7_ADC_Read(void){
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
3.2.3 ppm值计算
MQ7的电阻值(Rs)与气体浓度(ppm)的关系可以用以下公式表示:
code复制Rs = (Vc - Vout) / Vout * RL
ppm = a * (Rs/R0)^b
其中:
- Vc:电路电压(5V)
- Vout:ADC测量电压
- RL:负载电阻(项目中使用1kΩ)
- R0:传感器在洁净空气中的电阻
- a,b:传感器特性参数
项目中的具体实现:
c复制float MQ7_GetData_PPM(void){
float tempData = MQ7_ADC_Read();
float Vol = (tempData*5/4096); // 转换为电压值
float RS = (5-Vol)/(Vol*0.5); // 计算传感器电阻
float R0 = 6.64; // 洁净空气中电阻
float ppm = pow(11.5428*R0/RS, 0.6549f); // 计算ppm值
return ppm;
}
实测技巧:MQ7需要定期校准,建议在洁净空气中运行24小时后,记录此时的ADC值作为R0基准。
3.3 HC-08蓝牙模块配置
HC-08是一款低功耗蓝牙4.0模块,支持主从模式切换,串口透传。
3.3.1 模块初始化
- 使用USB转TTL工具连接电脑
- 打开串口调试助手,设置波特率9600
- 发送AT指令进行配置:
bash复制AT+DEFAULT # 恢复出厂设置
AT+ROLE=M # 设置为主机(M)或从机(S)
AT+NAME=EnvMonitor # 设置设备名称
AT+PSWD=1234 # 设置配对密码
3.3.2 主从模式配置
本项目需要两个HC-08模块,一个配置为主机(M),一个配置为从机(S)。配置完成后,它们会自动配对连接。
配置验证指令:
bash复制AT+RX # 查看模块信息
预期输出应包含:
code复制Role:Master/Slave
Name:EnvMonitor
3.3.3 数据包格式设计
为了可靠传输多传感器数据,设计了以下数据包格式:
code复制0xFF T1 T2 W1 MQ7_H MQ7_L 0x00
其中:
- 0xFF:数据包头
- T1,T2:温度数据(整数+小数)
- W1:湿度数据
- MQ7_H,MQ7_L:MQ7 ADC值(16位分高低字节)
- 0x00:数据包尾
接收端通过判断包头包尾来解析有效数据:
c复制void USART1_IRQHandler(void){
if(USART_GetITStatus(USARTx, USART_IT_RXNE) == SET){
uint16_t value = USART_ReceiveData(USARTx);
if(value == 0xFF){
ReceiveFlag = 1; // 开始接收数据
}else if(ReceiveFlag == 1){
if(value == 0x00){
ReceiveFlag = 0; // 结束接收
}else{
Result[Count] = value; // 存储数据
Count++;
}
}
}
}
3.4 OLED显示实现
OLED显示端使用IIC接口的0.96寸屏幕,通过STM32硬件IIC或软件模拟IIC驱动。
3.4.1 显示界面设计
项目采用两屏轮显方式,每10秒切换一次:
-
温湿度界面:
code复制Temperature: 25.6℃ Humidity: 45% -
CO浓度界面:
code复制CO: 1024 23.5ppm
3.4.2 定时轮询实现
使用TIM2定时器实现10秒轮询功能:
c复制void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){
second++;
if(second==9){ // 10秒切换
second = 0;
ShowResultFlag = !ShowResultFlag; // 切换显示标志
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
主循环中根据标志位显示不同内容:
c复制while(1){
if(ShowResultFlag){
DHT11_BlueTeeth_ShowResult(); // 显示温湿度
}else{
MQ7_BlueTeeth_ShowResult(); // 显示CO浓度
}
}
4. 系统整合与调试
4.1 发送端程序设计
发送端主程序流程:
- 初始化各外设
- 采集传感器数据
- 打包数据并通过蓝牙发送
- 延时后重复步骤2-3
c复制int main(){
// 初始化
BlueTeeth_Init();
DHT_Init();
MQ7_Init();
while(1){
// 采集数据
SaveData(&T1, &T2, &W1, &W2);
uint16_t co_value = MQ7_GetData();
// 发送数据包
BlueTeeth_SentByte(0xFF); // 包头
BlueTeeth_SentByte(T1);
BlueTeeth_SentByte(T2);
BlueTeeth_SentByte(W1);
BlueTeeth_SentByte(co_value >> 8); // CO高字节
BlueTeeth_SentByte(co_value & 0xFF); // CO低字节
BlueTeeth_SentByte(0x00); // 包尾
Delay_ms(1000); // 1秒间隔
}
}
4.2 接收端程序设计
接收端主程序流程:
- 初始化外设
- 等待蓝牙数据
- 根据定时器标志刷新OLED显示
- 重复步骤2-3
c复制int main(){
BlueTeeth_Init();
OLED_Init();
TIM_Init();
while(1){
if(ShowResultFlag){
DHT11_BlueTeeth_ShowResult(); // 显示温湿度
}else{
MQ7_BlueTeeth_ShowResult(); // 显示CO浓度
}
}
}
4.3 常见问题排查
问题1:DHT11无响应
可能原因:
- 接线错误,DATA线未正确连接
- 时序不符合要求,起始信号时间不足
- 上电后未等待足够时间(DHT11需要1秒稳定时间)
解决方案:
- 检查DATA线连接,确保接触良好
- 使用逻辑分析仪检查时序是否符合规格书要求
- 增加上电后的延时
问题2:MQ7数据不稳定
可能原因:
- 预热时间不足
- ADC参考电压不稳定
- 传感器老化
解决方案:
- 确保MQ7预热至少48小时(首次使用)
- 检查开发板3.3V电源质量,必要时增加滤波电容
- 定期校准传感器,或更换新传感器
问题3:蓝牙连接不稳定
可能原因:
- 主从模式配置错误
- 距离过远或有障碍物
- 波特率不匹配
解决方案:
- 使用AT+RX指令确认主从模式
- 确保通信距离在10米内,无大型金属障碍
- 检查双方波特率均为9600
问题4:OLED显示乱码
可能原因:
- IIC地址不匹配
- 初始化序列不正确
- 电源不稳定
解决方案:
- 确认OLED的IIC地址(通常0x78或0x7A)
- 检查OLED初始化代码是否符合规格书
- 测量VCC电压,确保在3.3V±0.2V范围内
5. 项目优化与扩展
5.1 当前系统优化建议
- 增加数据校验:当前数据包只有简单头尾校验,可增加CRC校验提高可靠性
- 优化电源管理:MQ7和蓝牙模块功耗较高,可增加休眠模式
- 改进用户界面:OLED可增加更多状态指示,如信号强度、电池电量等
5.2 功能扩展方向
- 多传感器支持:增加PM2.5、甲醛等环境传感器
- 云端上传:通过ESP8266将数据上传到物联网平台
- 移动端APP:开发Android/iOS应用接收显示数据
- 数据记录:增加SD卡模块存储历史数据
5.3 低功耗优化方案
对于电池供电的应用场景,可采取以下措施降低功耗:
- 传感器采集间隔:将采集间隔从1秒延长到1分钟
- 蓝牙连接策略:仅在需要发送数据时建立连接
- STM32低功耗模式:使用STOP模式,通过RTC定时唤醒
c复制// 进入STOP模式示例
void Enter_StopMode(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
// 唤醒后需要重新配置系统时钟
SystemInit();
}
6. 开发心得与经验分享
在完成这个项目的过程中,我积累了一些宝贵的实战经验,特别适合STM32初学者参考:
-
时序调试技巧:
- 对于DHT11这类时序敏感的器件,建议先用逻辑分析仪抓取波形
- 调试时可以先使用较宽松的延时参数,稳定后再逐步优化
- 关键时序点添加调试输出,方便定位问题
-
蓝牙模块配对经验:
- 两个HC-08模块最好先单独用USB转TTL测试,确认AT指令响应正常再接入电路
- 配对时LED指示灯状态:
- 快闪:未连接
- 慢闪:配对中
- 常亮:已连接
- 如果无法配对,尝试先恢复出厂设置(AT+DEFAULT)
-
ADC采集优化:
- 对于MQ7这类模拟传感器,采集时可多次采样取平均值
- 注意ADC参考电压的稳定性,必要时增加参考基准芯片
- 采样时间要足够长,特别是高阻抗信号源
-
项目调试方法论:
- 模块化调试:先单独测试每个模块功能正常再整合
- 从简到繁:先实现最基本功能,再逐步添加复杂特性
- 版本控制:使用Git管理代码,方便回退和比较
-
硬件设计经验:
- 电源去耦:每个IC附近放置0.1μF电容
- 信号保护:长距离信号线可串联22-100Ω电阻
- 接地规范:模拟和数字地单点连接
这个项目虽然不大,但涵盖了嵌入式开发的多个核心技能点:外设驱动开发、通信协议实现、无线数据传输等。通过实践,我对STM32的时钟系统、中断机制、外设寄存器配置有了更深入的理解。特别是在调试DHT11时序和蓝牙数据包解析过程中,解决各种问题的经验尤为宝贵。