打印

用 STM32 做个环境监测小站:从原理到代码的完整实践

[复制链接]
771|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
jerry_jn|  楼主 | 2025-7-18 10:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 jerry_jn 于 2025-7-18 10:19 编辑

我常听到提问:“入门单片机该从哪个项目练手?” 其实没必要一开始就啃复杂项目,今天分享的环境监测小站就很适合 —— 既能掌握 STM32 的基础外设用法,又能做出有实际价值的成品,关键是成本可控在百元内。

项目定位与核心功能

这个小站的核心需求很明确:实时监测室内温湿度、光照强度,并通过 OLED 屏幕显示数据,当数值超出预设阈值时触发蜂鸣器报警。之所以选这些参数,是因为对应的传感器都属于入门级,接口简单且价格低廉。

为什么用 STM32F103C8T6 而不是 51 单片机?主要考虑两点:一是 STM32 的 ADC 精度更高(12 位 vs 8 位),测光照时数据更稳定;二是内置 I2C 接口,驱动 OLED 屏幕无需占用太多 GPIO。当然如果手头只有 51 板也能改,后面会说适配方案。

硬件选型与电路设计

核心元器件清单

•STM32 最小系统板(45 元,带 USB 下载功能)

•DHT11 温湿度传感器(8 元,单总线通信,精度够用)

•BH1750 光照传感器(12 元,I2C 接口,支持自动量程)

•0.96 寸 OLED 屏幕(15 元,SSD1306 驱动,I2C 通信)

•有源蜂鸣器(3 元,低电平触发)

•10K 电位器(2 元,用于阈值调节)

这些元件在某立创商城都能配齐,记得领新人券能省 10 元。特别提醒:买 DHT11 时选带杜邦线的套餐,新手焊针脚容易出问题。

电路连接要点

电源部分要注意:DHT11 和 BH1750 都能直接用 3.3V 供电,和 STM32 电平兼容。但蜂鸣器如果用 5V 供电,需要加个 NPN 三极管(如 8050)做电平转换,否则 3.3V 可能驱动不起来。

I2C 总线接线技巧:OLED 和 BH1750 可以共用一组 I2C 引脚(PB6=SCL,PB7=SDA),但要注意两者的地址是否冲突(BH1750 默认 0x46,OLED 默认 0x3C,刚好不冲突)。

ADC 接口推荐用 PA0,接电位器中间引脚,两端分别接 3.3V 和 GND,这样转动旋钮就能实时调节报警阈值。

软件架构与关键代码

程序框架设计

采用模块化编程,分为这几个核心文件:

•main.c:主程序逻辑,负责任务调度

•dht11.c:温湿度采集驱动

•bh1750.c:光照强度读取

•oled.c:屏幕显示控制

•beep.c:报警模块控制

这样后期想加功能(比如蓝牙模块),直接新增模块文件即可,不用大改主程序。

核心代码解析

DHT11 读取函数的关键是时序控制,新手容易在这里掉坑:


uint8_t DHT11_ReadData(uint8_t *temp, uint8_t *humi)

{

    uint8_t buf[5];

    // 起始信号
    DHT11_RST();

    if(DHT11_Check() == 0)

    {

        // 读取40位数据
        for(uint8_t i=0;i<5;i++)

        {

            buf = DHT11_ReadByte();

        }

        // 校验数据
        if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4])

        {

            *humi = buf[0];

            *temp = buf[2];

            return 0; // 读取成功
        }

    }

    return 1; // 读取失败
}

这段代码里加了数据校验,能过滤掉 80% 的错误读数。实际测试发现,DHT11 在湿度低于 20% 时误差较大,建议在代码里加个补偿算法:当检测值 < 20% 时,实际值 = 检测值 + 5%。

OLED 显示部分推荐用字符库函数,比直接写点阵简单:


void OLED_ShowMonitorData(uint8_t temp, uint8_t humi, uint16_t light)

{

    OLED_ShowString(0,0,"Temp:");

    OLED_ShowNum(48,0,temp,2,16);

    OLED_ShowChar(64,0,'C',16);

   

    OLED_ShowString(0,2,"Humi:");

    OLED_ShowNum(48,2,humi,2,16);

    OLED_ShowChar(64,2,'%',16);

   

    OLED_ShowString(0,4,"Light:");

    OLED_ShowNum(56,4,light,4,16);

    OLED_ShowString(88,4,"lux",16);

}

调试过程中的典型问题

1.DHT11 读取不稳定:

现象:偶尔会读到 0xFF 的错误值

解决:在读取前加 10ms 延时,并且连续读取 3 次取平均值

2.OLED 屏幕闪烁:

原因:I2C 通信速率过高(默认 400K)

解决:将 I2C 时钟频率降到 100K,修改这行代码:


hi2c1.Init.ClockSpeed = 100000;

1.光照值跳变剧烈:

处理:在 BH1750 读取函数里加滑动滤波:


uint16_t BH1750_GetLight()

{

    static uint16_t light_buf[5];

    static uint8_t i=0;

    uint32_t sum=0;

   

    light_buf[i++] = BH1750_ReadRaw();

    if(i>=5)i=0;

   

    for(uint8_t j=0;j<5;j++)

        sum += light_buf[j];

   

    return sum/5;

}

功能扩展思路

基础版做好后,可以尝试这些进阶功能:

1.加个 ESP8266 模块,通过 MQTT 协议上传数据到阿里云

2.改用锂电池供电,加个 TP4056 充电模块和电压检测电路

3.增加按键功能,支持阈值参数保存到 EEPROM

我正在做第一个扩展,遇到的坑是 ESP8266 的复位电路需要和 STM32 共地,否则会出现连接不稳定的情况。解决办法是在两者 GND 之间接一个 100 欧电阻,抑制地噪声。

写给新手的建议

如果是第一次用 STM32,建议先从标准库入手,HAL 库虽然方便但封装太多,不利于理解底层原理。调试时一定要用串口打印中间变量,比如在读取传感器后加一句:


printf("temp=%d, humi=%d\r\n", temp, humi);

通过观察数值变化能快速定位问题。这个项目全部做完大概需要 15 小时,建议分 3 天完成:第一天搭硬件,第二天写驱动,第三天调试和优化。

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

11

主题

11

帖子

0

粉丝