这个STM32环境监测蓝牙终端项目,是我在看完江协科技的STM32入门教程后设计的第一个实战项目。它完美衔接了教程中的基础知识点,通过实际应用场景把这些零散的知识点串联起来。项目核心功能是通过STM32采集温湿度、空气质量等环境数据,再通过蓝牙模块将数据实时传输到手机端显示。
选择这个项目作为入门实战有几个明显优势:硬件成本低(整套设备不到100元)、代码量适中(300行左右)、涉及STM32核心外设(GPIO、ADC、UART、定时器等)。对于刚学完基础教程的新手来说,既能巩固知识点,又不会因为复杂度太高而放弃。
我在实际开发中发现,很多新手在学完教程后不知道如何开始第一个项目。这个环境监测终端正好填补了这个空白 - 它用到了教程中80%的基础外设,但又加入了实用的无线通信功能,让学习者能立即看到自己的代码产生了实际价值。
主控芯片我选择了STM32F103C8T6最小系统板,也就是常说的"蓝板"。这块板子价格不到20元,但包含了我们需要的所有外设资源。相比更便宜的"黑板",蓝板的USB转串口芯片更稳定,烧录成功率更高。
传感器方面使用了DHT11温湿度传感器和MQ-135空气质量传感器。这两个都是数字输出的传感器,不需要复杂的信号调理电路。DHT11通过单总线协议通信,MQ-135则使用ADC采集模拟电压值。
蓝牙模块选用了HC-05主从一体模块。它支持AT指令配置,默认波特率9600,与STM32的USART外设完美兼容。模块自带板载天线,实测传输距离在无障碍环境下能达到8-10米。
整个项目的硬件连接非常简单,只需要6根杜邦线:
特别注意:HC-05模块虽然标称工作电压3.3V-5V,但RXD引脚只能接受3.3V电平。如果STM32板子的IO口是5V电平,需要在RXD线上加电平转换电路,否则可能损坏蓝牙模块。
电源部分,所有模块都可以直接从STM32板子的3.3V引脚取电。如果发现DHT11数据不稳定,可以单独给它供电并共地。
我坚持使用Keil MDK作为开发环境,虽然它收费,但针对STM32F1系列的代码限制是32KB,对我们这个项目完全够用。社区版注册后就没有使用时间限制。
安装时需要特别注意两点:
对于刚入门的新手,我建议直接使用江协科技教程中提供的环境包,里面已经配置好了所有必需组件。这样可以避免在环境搭建阶段浪费太多时间。
在Keil中新建工程时,选择STM32F103C8器件,然后勾选以下库文件:
工程创建完成后,需要修改两个关键配置:
DHT11的通信时序要求非常严格,必须用微秒级延时。我在实际调试中发现,直接用库函数提供的延时会导致数据读取失败。解决方法是用SysTick定时器实现精确延时:
c复制void DHT11_Delay_us(uint32_t us)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
ticks = us * 72; // 72MHz主频下1us对应的ticks数
told = SysTick->VAL;
while(1) {
tnow = SysTick->VAL;
if(tnow != told) {
if(tnow < told) tcnt += told - tnow;
else tcnt += reload - tnow + told;
told = tnow;
if(tcnt >= ticks) break;
}
}
}
数据读取流程如下:
MQ-135的输出是模拟电压,需要使用STM32的ADC进行采集。配置ADC时要注意:
ADC值需要转换为实际的空气质量指数。我的做法是先读取传感器在洁净空气中的基准值(通常为1.6V左右),然后根据当前电压与基准值的比值来判断污染程度:
c复制float GetAirQuality(void)
{
uint16_t adc_value = ADC_GetValue(); // 获取ADC原始值
float voltage = adc_value * 3.3 / 4095; // 转换为电压值
float ratio = voltage / CLEAN_AIR_VOLTAGE;
if(ratio < 1.1) return 0; // 空气质量优
else if(ratio < 1.5) return 1; // 轻度污染
else return 2; // 严重污染
}
在使用HC-05前,需要先进入AT模式进行基本配置:
配置完成后重启模块,LED快闪表示进入可配对状态。
为了简化手机端APP开发,我设计了一个简单的文本协议:
code复制T:25.5,H:60%,A:1\n
其中T表示温度,H表示湿度,A表示空气质量等级(0-2)。每条数据以换行符结束。
在STM32端,使用printf通过USART发送数据:
c复制void SendSensorData(float temp, float humi, uint8_t air)
{
printf("T:%.1f,H:%.0f%%,A:%d\n", temp, humi, air);
}
// 重定向printf到USART1
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t)ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
对于不熟悉Android开发的新手,我推荐使用MIT App Inventor这个图形化开发工具。只需要拖拽组件就能完成基础功能开发。
主要界面组件:
关键逻辑是当收到"\n"字符时,解析整条数据并更新UI。App Inventor提供了现成的蓝牙组件,大大降低了开发难度。
如果想实现更专业的数据曲线显示,可以使用专业的IoT平台如Blinker或EasyIoT。这些平台提供了现成的手机APP,只需要按照文档发送特定格式的数据即可。
例如Blinker平台的数据格式:
code复制{"temp":25.5,"humi":60,"air":1}
在STM32端构造这样的JSON字符串发送,就能在APP上看到实时曲线。
作为环境监测设备,低功耗是一个重要考量。我通过以下方法降低系统功耗:
实测下来,系统平均电流从25mA降到了8mA左右,用2000mAh的锂电池可以连续工作约10天。
传感器数据准确性对监测设备至关重要。我的校准方法是:
c复制// 简单的移动平均滤波实现
#define FILTER_LEN 5
float temp_filter[FILTER_LEN] = {0};
uint8_t filter_index = 0;
float Filter_AddValue(float new_val)
{
temp_filter[filter_index] = new_val;
filter_index = (filter_index + 1) % FILTER_LEN;
float sum = 0;
for(int i=0; i<FILTER_LEN; i++) {
sum += temp_filter[i];
}
return sum / FILTER_LEN;
}
现象:数据时有时无,或者连接经常断开
解决方法:
DHT11数据为0或255:
MQ-135值不变化:
常见错误提示:
这个基础项目完成后,可以考虑以下几个扩展方向:
我个人最喜欢的是增加一个0.96寸OLED屏的方案。只需要额外连接4根线(SCL、SDA、VCC、GND),就能在设备上直接看到实时数据。使用U8g2库可以轻松实现各种显示效果:
c复制// OLED显示示例
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_helvB08_tr);
u8g2_DrawStr(&u8g2, 0, 15, "Env Monitor");
u8g2_DrawStr(&u8g2, 0, 30, buf); // 显示传感器数据
u8g2_SendBuffer(&u8g2);
这个项目最让我有成就感的是,它把STM32的各个外设真正用到了一个实际产品中。当你看到自己编写的代码让硬件"活"起来,并能解决实际问题时,那种感觉是看多少教程都替代不了的。建议每个新手都能完成这样一个完整的项目,它会让你对嵌入式开发有全新的认识。