STM32单片机+DHT11温湿度传感器+OLED屏幕+蜂鸣器报警+串口数据可视化与阈值动态配置

发布时间:2026/7/14 20:54:22

STM32单片机+DHT11温湿度传感器+OLED屏幕+蜂鸣器报警+串口数据可视化与阈值动态配置 1. 系统架构设计思路这个温湿度监控系统的核心在于实现环境数据的本地显示、异常报警和远程交互三大功能。我选择STM32F103C8T6作为主控芯片主要看中它性价比高且外设丰富。整个系统的数据流是这样的DHT11传感器采集数据→STM32处理数据→OLED实时显示→蜂鸣器阈值报警→串口上传PC端。这种架构既保证了实时性又实现了数据可视化。在实际搭建时我建议采用模块化编程思路。把传感器驱动、显示控制、报警逻辑、串口通信分别封装成独立模块这样后期维护会非常方便。比如当你想把DHT11换成更精确的SHT30时只需要替换传感器驱动模块其他部分几乎不用改动。硬件连接上有个小技巧DHT11的数据线最好加上拉电阻4.7KΩ我刚开始没加这个电阻结果读取数据经常失败。后来查手册才发现这个传感器对时序要求严格上拉电阻能保证信号稳定性。2. DHT11传感器驱动开发DHT11是款性价比很高的温湿度传感器但它的单总线协议需要特别注意时序控制。我翻过它的数据手册发现最大的坑在于主机STM32必须先发送开始信号拉低18ms以上然后等待传感器响应。这里分享一个调试经验用逻辑分析仪抓取数据线波形。有次我遇到读取数据全为0的情况抓波形发现是延时函数不准确导致的。后来改用STM32的硬件定时器做us级延时问题就解决了。具体驱动代码关键部分如下// DHT11初始化函数 u8 DHT11_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_11); DHT11_Rst(); return DHT11_Check(); } // 读取温湿度数据 u8 DHT11_Read_Data(u8 *temp,u8 *humi) { u8 buf[5]; u8 i; DHT11_Rst(); if(DHT11_Check()0) { for(i0;i5;i) { buf[i]DHT11_Read_Byte(); } if((buf[0]buf[1]buf[2]buf[3])buf[4]) { *humibuf[0]; *tempbuf[2]; } } else return 1; return 0; }实测发现DHT11的响应速度约2秒一次太快读取会失败。建议在主循环里加个状态机每隔2秒读取一次数据即可。3. OLED显示界面优化我用的是0.96寸SSD1306 OLED屏通过I2C接口连接。显示内容需要精心设计第一行显示实时温湿度数值第二行用进度条直观展示当前值第三行显示报警阈值底部留状态栏显示系统运行时间。有个显示优化技巧使用自定义字符。比如温度图标可以用8x8像素自己画比用现成的字体更美观。以下是OLED刷新函数的关键代码void OLED_Refresh(float temp, float humi) { char str[16]; OLED_Clear(); // 显示温度 sprintf(str, Temp: %.1fC, temp); OLED_ShowString(0, 0, (u8*)str, 16); OLED_DrawProgressBar(60, 0, 50, 8, temp, 50); // 温度进度条 // 显示湿度 sprintf(str, Humi: %.1f%%, humi); OLED_ShowString(0, 2, (u8*)str, 16); OLED_DrawProgressBar(60, 2, 50, 8, humi, 100); // 湿度进度条 // 显示阈值 sprintf(str, Thresh: %dC/%d%%, temp_threshold, humi_threshold); OLED_ShowString(0, 4, (u8*)str, 16); // 状态栏 sprintf(str, Uptime: %ds, HAL_GetTick()/1000); OLED_ShowString(0, 6, (u8*)str, 16); OLED_Refresh_Gram(); }注意OLED不要刷新太频繁否则会出现闪烁。我实测每秒刷新1-2次就足够关键数据变化时才立即刷新。4. 蜂鸣器报警逻辑实现报警模块采用有源蜂鸣器驱动电路很简单STM32的IO口→三极管放大→蜂鸣器。但报警逻辑需要仔细设计我建议采用滞回比较算法避免阈值临界点时蜂鸣器频繁开关。比如设置温度阈值为30°C当温度30°C时触发报警只有当温度28°C时才解除报警 这样2°C的回差能有效防止震荡报警驱动代码示例void Buzzer_Control(float temp, float humi) { static uint8_t alarm_state 0; // 报警触发条件 if((temp temp_threshold) || (humi humi_threshold)) { alarm_state 1; } // 报警解除条件带滞回 else if((temp (temp_threshold-2)) (humi (humi_threshold-5))) { alarm_state 0; } // 控制蜂鸣器 if(alarm_state) { GPIO_SetBits(BUZZER_PORT, BUZZER_PIN); // 可以添加闪烁LED的代码 } else { GPIO_ResetBits(BUZZER_PORT, BUZZER_PIN); } }实际使用时发现蜂鸣器声音太刺耳后来我在软件里加了PWM控制让报警声变成嘀-嘀-嘀的节奏音用户体验好很多。5. 串口通信协议设计串口通信是整个系统的关键既要上传数据又要接收PC端的配置指令。我设计了一套简单的文本协议格式如下数据上报[DATA]25.3,45.2\r\n阈值设置[SET]30,70\r\n系统应答[ACK]OK\r\n或[ERR]Invalid\r\nSTM32端使用DMA空闲中断接收数据大大降低CPU占用率。以下是串口初始化和中断处理的关键代码// 串口初始化 void USART1_Init(void) { huart1.Instance USART1; huart1.Init.BaudRate 115200; huart1.Init.WordLength UART_WORDLENGTH_8B; huart1.Init.StopBits UART_STOPBITS_1; huart1.Init.Parity UART_PARITY_NONE; huart1.Init.Mode UART_MODE_TX_RX; huart1.Init.HwFlowCtl UART_HWCONTROL_NONE; HAL_UART_Init(huart1); // 启用空闲中断 __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); // 启动DMA接收 HAL_UART_Receive_DMA(huart1, uart_rx_buf, BUF_SIZE); } // 空闲中断回调函数 void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { if(huart huart1) { // 处理接收到的数据 Process_UART_Data(); // 重新启动DMA接收 HAL_UART_Receive_DMA(huart1, uart_rx_buf, BUF_SIZE); } }在PC端我推荐使用串口调试助手Python脚本的方案。调试助手用于日常监测Python脚本则可以实现数据记录和可视化import serial import matplotlib.pyplot as plt ser serial.Serial(COM3, 115200, timeout1) temp_data [] humi_data [] while True: line ser.readline().decode().strip() if line.startswith([DATA]): temp, humi map(float, line[6:].split(,)) temp_data.append(temp) humi_data.append(humi) # 简单绘图 plt.clf() plt.plot(temp_data, labelTemperature) plt.plot(humi_data, labelHumidity) plt.legend() plt.pause(0.01)6. 动态阈值配置实现为了让系统更灵活我增加了通过串口动态修改报警阈值的功能。在STM32端维护一个配置结构体typedef struct { uint8_t temp_threshold; uint8_t humi_threshold; uint8_t buzzer_enable; } SystemConfig; SystemConfig config { .temp_threshold 30, .humi_threshold 70, .buzzer_enable 1 };当收到[SET]指令时解析并更新配置void Process_Config_Cmd(char *cmd) { // 示例指令[SET]35,75 if(sscanf(cmd, [SET]%d,%d, config.temp_threshold, config.humi_threshold) 2) { if(config.temp_threshold 0 config.temp_threshold 50 config.humi_threshold 20 config.humi_threshold 95) { Send_UART_Response([ACK]OK\r\n); return; } } Send_UART_Response([ERR]Invalid range\r\n); }为了验证配置是否生效可以在OLED上实时显示当前阈值。我还加了个小功能当阈值被修改时蜂鸣器短鸣一声作为反馈这个细节让交互更人性化。7. 系统稳定性优化在实际运行中我发现几个需要优化的地方电源管理当使用电池供电时给DHT11和OLED单独加LDO稳压避免电压波动影响传感器精度。看门狗启用STM32的独立看门狗(IWDG)防止程序跑飞void IWDG_Init(void) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); IWDG_SetPrescaler(IWDG_Prescaler_32); // 32分频 IWDG_SetReload(0xFFF); // 约1.6s超时 IWDG_ReloadCounter(); IWDG_Enable(); }数据滤波对DHT11的读数进行滑动平均滤波减少波动#define FILTER_SIZE 5 float temp_history[FILTER_SIZE] {0}; uint8_t filter_index 0; float Filter_Data(float new_val) { temp_history[filter_index] new_val; filter_index (filter_index 1) % FILTER_SIZE; float sum 0; for(int i0; iFILTER_SIZE; i) { sum temp_history[i]; } return sum / FILTER_SIZE; }异常处理当DHT11连续3次读取失败时在OLED显示错误图标并通过串口发送错误代码。8. 完整系统集成测试把所有模块集成后需要进行系统测试。我的测试方案是功能测试用嘴对DHT11哈气观察湿度值变化和报警触发用手握住传感器观察温度上升情况通过串口发送阈值修改指令验证响应压力测试连续运行24小时检查内存泄漏快速插拔串口线测试通信恢复能力模拟电源波动验证系统稳定性用户体验优化报警时OLED显示闪烁的警告标志长按按键可临时静音蜂鸣器串口指令支持查询当前状态发送[GET]\r\n返回所有参数测试通过后就可以考虑产品化设计了。比如设计个3D打印的外壳或者开发手机APP替代串口调试助手。这个系统框架其实很有扩展性后期可以轻松添加更多传感器比如CO2检测、光照强度等。

相关新闻