基于STM32的环境监测系统(自制蓝牙APP)
- 人工智能
- 2025-09-11 21:09:02

目录
项目概述
实物图
演示视频
概述
硬件模块
原理图以及PCB
0.96寸OLED屏幕(SSD1306)
CubeMX配置
初始化代码
MQ-2烟雾传感器
CubeMX配置
初始化代码
DHT11温湿度模块
驱动代码
HC-05蓝牙模块
CubeMX配置
编辑
空闲中断回调函数
有源蜂鸣器和TB6612电机驱动模块
CubeMX配置
核心代码
使用AI2Offline制作蓝牙APP
UI界面设计
逻辑界面设计
项目概述 实物图 演示视频
概述
主控为stm32f103c8t6。使用DHT11温湿度传感器和MQ-2烟雾传感器,读取并实时刷新在0.96寸OLED屏幕上,同时通过蓝牙模块HC-05使用串口通信将数据上传到上位机(自制蓝牙APP)。可手动控制蜂鸣器以及电机作为报警器和风扇;在自动预警模式下,监测到温度高出设定的阈值后打开风扇降温;当监测到烟雾浓度高出设定阈值后将关闭风扇防止火情蔓延,并开启蜂鸣器报警,上位机同步更新报警状态。
硬件模块 stm32f103c8t6最小系统板0.96寸OLED屏幕MQ-2烟雾传感器(5V)DHT11温湿度传感器有源蜂鸣器HC-05蓝牙模块(5V)
TB6612电机驱动模块(5V)
直流电机(5V)面包板排母若干
原理图以及PCB
原理图:
PCB:
0.96寸OLED屏幕(SSD1306)
见我上传的资源:OLED驱动代码,设置成免费下载了。
CubeMX配置勾选I2C1,设置为快速模式即可。
初始化代码 //oled初始化 HAL_Delay(20); //屏幕启动比stm32要慢,上电延时20ms OLED_Init();MQ-2烟雾传感器
使用ADC的连续转换模式,可参考这篇博客:HAL库教程。
CubeMX配置记得配置Continuous Conversion Mode为Enabled,这样就开启了ADC的连续转换模式。
初始化代码 //烟雾传感器初始化 HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);DHT11温湿度模块
由于该模块是单数据线,需要在代码里不断改变引脚状态,因此不需要在CubeMX里配置,我这里用的是PA2引脚。
驱动代码dht11.c:
#include "dht11.h" uint8_t datas[5];//空气温湿度数据 void delay_us(uint16_t cnt) { uint8_t i; while(cnt) { for (i = 0; i < 10; i++) { } cnt--; } } void DHT_GPIO_Init(uint32_t Mode) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Mode = Mode; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void DHT11_Start(void) { DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP); DHT_HIGHT; DHT_LOW; HAL_Delay(30); DHT_HIGHT; DHT_GPIO_Init(GPIO_MODE_INPUT); while(DHT_VALUE); while(!DHT_VALUE); while(DHT_VALUE); } void Read_Data_From_DHT(void) { int i;//轮 int j;//每一轮读多少次 char tmp; char flag; DHT11_Start(); DHT_GPIO_Init(GPIO_MODE_INPUT); for(i= 0;i < 5;i++) { for(j=0;j<8;j++) { while(!DHT_VALUE);//等待卡g点 delay_us(40); if(DHT_VALUE == 1) { flag = 1; while(DHT_VALUE); } else { flag = 0; } tmp = tmp << 1; tmp |= flag; } datas[i] = tmp; } }dht11.h:
#ifndef __DHT11_H__ #define __DHT11_H__ #include "main.h" #define DHT_HIGHT HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET) #define DHT_LOW HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET) #define DHT_VALUE HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) extern uint8_t datas[5]; void Read_Data_From_DHT(void); #endif后续直接调用Read_Data_From_DHT函数读取数据就好了,数据会存放在datas数组里:datas[0]是湿度的整数数据、datas[1]是湿度的小数数据、datas[2]是温度的整数数据、datas[3]是温度的小数数据、datas[5]是校验数据。
HC-05蓝牙模块
使用串口收发数据,借助蓝牙APP,可以把这个模块当作平常用的CH340模块来用。
CubeMX配置使用串口1收发数据,波特率为115200,由于我们需要接收不定长数据,因此还要用到串口空闲中断,不妨使用DMA模式下的空闲中断:
点开DMA设置,为接收和发送都创建DMA通道(默认即可),并确保打开了串口中断:
空闲中断回调函数 //串口接收buffer #define RX_BUF_SIZE 50 uint8_t receiveData[RX_BUF_SIZE] = ""; void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { //每次进入回调函数之前判断是哪个串口触发的中断 if(huart == &huart1) { HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *)receiveData, sizeof(receiveData)); __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); } }有源蜂鸣器和TB6612电机驱动模块
全部设置为推挽输出即可,不需要对电机进行调速。
CubeMX配置PA3为蜂鸣器引脚,PA4~6分别为TB6612的PWMA、AIN2、AIN1。电机驱动模块主要操作的是AIN2和AIN1引脚,要让电机旋转只需要随便拉低一个引脚即可,蜂鸣器也是低电平触发。
核心代码 /* 头文件包含 */ #include "main.h" // HAL库主头文件 #include "adc.h" // ADC驱动 #include "dma.h" // DMA驱动 #include "i2c.h" // I2C驱动(用于OLED) #include "usart.h" // 串口驱动 #include "gpio.h" // GPIO驱动 #include "dht11.h" // DHT11温湿度传感器驱动 #include "oled.h" // OLED显示驱动 #include <stdio.h> // 标准输入输出(用于printf) #include <string.h> // 字符串操作 /* 系统状态宏定义 */ #define OFF 0 // 关闭状态 #define ON 1 // 开启状态 /* 蜂鸣器控制宏 */ #define BUZZER_ON HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET) // PA3低电平触发蜂鸣器 #define BUZZER_OFF HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET) // PA3高电平关闭蜂鸣器 /* 全局变量声明 */ float smoke = 0.0; // 烟雾浓度值(百分比) #define RX_BUF_SIZE 50 // 串口接收缓冲区大小 uint8_t receiveData[RX_BUF_SIZE] = ""; // 串口接收缓冲区 // 设备状态标志 uint8_t Buzzer_State = OFF; // 蜂鸣器状态 uint8_t Fan_State = OFF; // 风扇状态 uint8_t Auto_Alarm_State = OFF;// 自动报警模式状态 // 状态显示字符串(OLED用) char State_String[2][5] = {"OFF", "ON"}; /* 自定义printf输出重定向 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (const uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } /* 串口接收完成回调函数 */ void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { char buffer[50] = ""; memcpy(buffer, receiveData, sizeof(receiveData)); // 复制接收数据到临时缓冲区 if(huart == &huart1) { buffer[Size] = '\0'; // 添加字符串终止符 /* 自动报警模式控制 */ if(strcmp(buffer, "Auto_On\r\n") == 0) { Auto_Alarm_State = ON; HAL_UART_Transmit_DMA(&huart1, (uint8_t *)buffer, Size); } else if(strcmp(buffer, "Auto_Off\r\n") == 0) { // 关闭所有输出设备 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 风扇IN1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET); // 风扇IN2 BUZZER_OFF; // 更新状态标志 Buzzer_State = OFF; Fan_State = OFF; Auto_Alarm_State = OFF; // 返回确认信息 printf("Buzzer_Off\r\nFan_Off\r\nAuto_Off\r\n"); } /* 手动控制模式处理 */ if(Auto_Alarm_State == OFF) { // 风扇控制 if(strcmp(buffer, "Fan_On\r\n") == 0) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // IN1=0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET); // IN2=1(正转) Fan_State = ON; HAL_UART_Transmit_DMA(&huart1, (uint8_t *)buffer, Size); } else if(strcmp(buffer, "Fan_Off\r\n") == 0) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // IN1=1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET); // IN2=1(刹车) Fan_State = OFF; HAL_UART_Transmit_DMA(&huart1, (uint8_t *)buffer, Size); } // 蜂鸣器控制 else if(strcmp(buffer, "Buzzer_On\r\n") == 0) { BUZZER_ON; Buzzer_State = ON; HAL_UART_Transmit_DMA(&huart1, (uint8_t *)buffer, Size); } else if(strcmp(buffer, "Buzzer_Off\r\n") == 0) { BUZZER_OFF; Buzzer_State = OFF; HAL_UART_Transmit_DMA(&huart1, (uint8_t *)buffer, Size); } } // 重新启动DMA接收 HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, sizeof(receiveData)); __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); // 禁用传输过半中断 } } /* 自动报警处理函数 */ void Auto_Alarm(void) { /* 烟雾浓度报警(阈值60%) */ if(smoke > 60) { BUZZER_ON; if(Buzzer_State == OFF) { // 状态变更时上报 Buzzer_State = ON; printf("Buzzer_On\r\n"); } } else { BUZZER_OFF; if(Buzzer_State == ON) { Buzzer_State = OFF; printf("Buzzer_Off\r\n"); } } /* 温度控制逻辑(阈值30℃) */ if(datas[2] >= 30 && smoke < 60) { // 温度高且无烟雾危险时开启风扇 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 正转 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET); if(Fan_State == OFF) { printf("Fan_On\r\n"); Fan_State = ON; } } else { // 关闭风扇 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET); if(Fan_State == ON) { printf("Fan_Off\r\n"); Fan_State = OFF; } } } /* 主函数 */ int main(void) { // 硬件初始化 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); MX_I2C1_Init(); MX_ADC1_Init(); /* 外设初始化 */ HAL_Delay(20); // 等待硬件稳定 OLED_Init(); // OLED显示屏初始化 // ADC校准和启动 HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); // 启动串口DMA接收 HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, sizeof(receiveData)); __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); /* 主循环 */ while (1) { OLED_NewFrame(); // 准备新显示帧 // 传感器数据读取 Read_Data_From_DHT(); // 获取温湿度数据 smoke = (HAL_ADC_GetValue(&hadc1) / 4095.0) * 100.0; // 计算烟雾浓度 // 自动报警模式处理 if(Auto_Alarm_State == ON) { Auto_Alarm(); } /* OLED显示内容格式化 */ char Tem_mes[10], Hum_mes[10], Smo_mes[10], Sta_mes[20], BlueTooth_mes[30]; sprintf(Tem_mes, "Tem:%d.%d", datas[2], datas[3]); // 温度显示 sprintf(Hum_mes, "Hum:%d.%d%%", datas[0], datas[1]); // 湿度显示 sprintf(Smo_mes, "Smoke:%.1f%%", smoke); // 烟雾浓度 sprintf(Sta_mes, "A:%s B:%s F:%s", // 状态显示 State_String[Auto_Alarm_State], State_String[Buzzer_State], State_String[Fan_State]); sprintf(BlueTooth_mes, "NULL;%d.%d;%d.%d;%.1f;NULL\r\n", // 蓝牙数据格式 datas[2], datas[3], datas[0], datas[1], smoke); /* OLED显示更新 */ OLED_PrintASCIIString(0, 0, Tem_mes, &afont16x8, OLED_COLOR_NORMAL); OLED_PrintString(65, 0, "℃", &font16x16, OLED_COLOR_NORMAL); OLED_PrintASCIIString(0, 17, Hum_mes, &afont16x8, OLED_COLOR_NORMAL); OLED_PrintASCIIString(0, 33, Smo_mes, &afont16x8, OLED_COLOR_NORMAL); OLED_PrintASCIIString(0, 49, Sta_mes, &afont12x6, OLED_COLOR_NORMAL); OLED_ShowFrame(); // 刷新显示 /* 蓝牙数据传输 */ HAL_UART_Transmit_DMA(&huart1, (uint8_t *)BlueTooth_mes, strlen(BlueTooth_mes)); HAL_Delay(1000); // 主循环周期1秒 } } /* 注意事项: 1. GPIO分配: - PA3: 蜂鸣器控制 - PA5/PA6: TB6612电机驱动控制引脚(IN1/IN2) - 确保实际硬件连接与代码一致 2. 传感器数据格式: - datas数组来自DHT11驱动,索引: 0: 湿度整数部分 1: 湿度小数部分 2: 温度整数部分 3: 温度小数部分 3. 蓝牙数据协议: "NULL;温度;湿度;烟雾;NULL\r\n" 格式示例: "NULL;25.5;60.0;30.5;NULL\r\n" 4. 改进建议: - 增加传感器数据校验 - 添加看门狗防止死机 - 使用RTOS进行任务管理 - 添加EEPROM存储报警阈值 */
使用AI2Offline制作蓝牙APP UI界面设计
控件如下:
逻辑界面设计蓝牙连接逻辑:
按键发送逻辑:
接收数据逻辑:
需要实物或者完整源码的可以私信我。
基于STM32的环境监测系统(自制蓝牙APP)由讯客互联人工智能栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“基于STM32的环境监测系统(自制蓝牙APP)”