目錄
項目概述
實物圖
演示視頻
概述
硬件模塊
原理圖以及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); // 風扇IN1HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET); // 風扇IN2BUZZER_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=0HAL_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=1HAL_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界面設計
控件如下:
邏輯界面設計
藍牙連接邏輯:
按鍵發送邏輯:
接收數據邏輯:
需要實物或者完整源碼的可以私信我。