

Bilibili video link:
https://www.bilibili.com/video/BV1bndfYfEhM/?share_source=copy_web&vd_source=097fdeaf6b6ecfed8a9ff7119c32faf2
01
—
Project Overview
1. Function Details
STM32 Air Purifier
The functions are as follows:
-
MQ-2 detects smoke, triggers sound and light alarm, and activates the purifier
-
PM2.5 module detects dust
-
MQ-7 detects carbon monoxide gas
-
MQ-135 detects air quality
-
MS1100 detects formaldehyde levels
-
Smoke, gas, air quality, dust, and formaldehyde exceedance triggers buzzer and indicator light alarms and activates the purifier
-
DHT11 detects environmental temperature and humidity, with voice prompts when temperature or humidity exceeds thresholds
-
OLED displays current environmental data in real-time
-
Buttons can adjust thresholds and manually control the purifier switch
-
Data is sent to a mobile device via Bluetooth, allowing control of the fan and purifier, and mode switching
2. Bill of Materials
-
STM32F103C8T6 microcontroller
-
OLED screen
-
DHT11 temperature and humidity sensor
-
MQ2 smoke sensor
-
MQ7 carbon monoxide sensor
-
MQ135 air quality sensor
-
PM2.5 dust sensor
-
MS1100 formaldehyde sensor
-
BT04A Bluetooth module
-
Active buzzer
-
Relay
-
Fan module
-
Humidifier module
02
—
Schematic Design

03
—
PCB Hardware Design
PCB Diagram


04
—
Program Design
#include "sys.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "delay.h"
#include "gpio.h"
#include "key.h"
#include "oled.h"
#include "usart.h"
#include "adc.h"
#include "dht11.h"
#include "pm25.h"
/**********************************Variable Definitions**********************************/
uint8_t key_num = 0; // Key scan flag
uint8_t flag_display = 0; // Display interface flag
uint32_t time_num = 0; // 10ms timer
char display_buf[32]; // Display buffer
_Bool flag_mode = 0; // Current mode flag
extern uint8_t usart1_buf[256]; // USART1 receive array
u16 smog_value = 0; // Smoke value
u16 smog_max = 80; // Maximum smoke value
u16 trq_value = 0; // Natural gas value
u16 trq_max = 80; // Maximum natural gas value
u16 har_value = 0; // Harmful gas value
u16 har_max = 80; // Maximum harmful gas value
u16 jq_value = 0; // Formaldehyde gas value
u16 jq_max = 80; // Maximum formaldehyde gas value
u16 temp_value = 0; // Temperature value
u16 temp_max = 35; // Maximum temperature value
_Bool flag_yy_temp = 0; // Voice flag
u16 humi_value = 0; // Humidity value
u16 humi_max = 70; // Maximum humidity value
_Bool flag_yy_humi = 0; // Voice flag
u16 pm_value = 0; // PM2.5 value
u16 pm_max = 300; // Maximum PM2.5 value
/**********************************Function Declarations**********************************/
void Key_function(void); // Key function
dvoid Monitor_function(void); // Monitoring function
void Display_function(void); // Display function
void Manage_function(void); // Processing function
/*********** Main Function ************/
int main(void) {
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // Configure interrupt priority group
Delay_Init(); // Delay initialization
Gpio_Init(); // IO initialization
Key_Init(); // Key initialization
Oled_Init(); // OLED initialization
Oled_Clear_All(); // Clear screen
Usart1_Init(9600); // USART1 initialization
Usart2_Init(9600); // USART2 initialization
Adc_Init(); // ADC initialization
DHT11_Init(); // Temperature initialization
PM25_Init(); // PM2.5 initialization
UsartPrintf(USART2,"AF:30");
Delay_ms(1000);
UsartPrintf(USART2,"A7:00001");
while(1) {
Key_function(); // Key function
Monitor_function(); // Monitoring function
Display_function(); // Display function
Manage_function(); // Processing function
time_num++; // Timer variable +1
Delay_ms(10);
if(time_num % 10 == 0) LED_SYS = ~LED_SYS;
if(time_num >= 5000) {
time_num = 0;
}
}
}
/*********** Key Function ************/
void Key_function(void) {
key_num = Chiclet_Keyboard_Scan(0); // Key scan
if(key_num != 0) { // If a key is pressed
switch(key_num) {
case 1: // Key 1, switch to settings interface
flag_display++;
if(flag_display >= 8) flag_display = 0;
Oled_Clear_All(); // Clear screen
break;
case 2: // Key 2
switch(flag_display) {
case 0: // Interface 0:
flag_mode = 1;
RELAY_FS = ~RELAY_FS;
RELAY_JH = ~RELAY_JH;
break;
case 1: // Interface 1: Set maximum smoke value +1
if(smog_max < 300) smog_max++;
break;
case 2: // Interface 2: Set maximum PM2.5 value +1
if(pm_max < 600) pm_max++;
break;
case 3: // Interface 3: Set maximum natural gas value +1
if(trq_max < 300) trq_max++;
break;
case 4: // Interface 4: Set maximum harmful gas value +1
if(har_max < 300) har_max++;
break;
case 5: // Interface 5: Set maximum formaldehyde gas value +1
if(jq_max < 300) jq_max++;
break;
case 6: // Interface 6: Set maximum temperature value +1
if(temp_max < 100) temp_max++;
break;
case 7: // Interface 7: Set maximum humidity value +1
if(humi_max < 100) humi_max++;
break;
default: break;
}
break;
case 3: // Key 3
switch(flag_display) {
case 0: // Interface 0:
flag_mode = 0;
break;
case 1: // Interface 1: Set maximum smoke value -1
if(smog_max > 0) smog_max--;
break;
case 2: // Interface 2: Set maximum PM2.5 value -1
if(pm_max > 0) pm_max--;
break;
case 3: // Interface 3: Set maximum natural gas value -1
if(trq_max > 0) trq_max--;
break;
case 4: // Interface 4: Set maximum harmful gas value -1
if(har_max > 0) har_max--;
break;
case 5: // Interface 5: Set maximum formaldehyde gas value -1
if(jq_max > 0) jq_max--;
break;
case 6: // Interface 6: Set maximum temperature value -1
if(temp_max > 0) temp_max--;
break;
case 7: // Interface 7: Set maximum humidity value -1
if(humi_max > 0) humi_max--;
break;
default: break;
}
break;
default: break;
}
}
}
/*********** Monitoring Function ************/
void Monitor_function(void) {
if(flag_display == 0) // Measurement interface
{
if(time_num % 2 == 0) // Get data
{
if(CHECK_S == 0) // If smoke triggers
smog_value = 90*(Get_Adc_Average(0,3)*3.3/4096.0);
else
smog_value = 0;
if(CHECK_T == 0) // If natural gas triggers
trq_value = 90*(Get_Adc_Average(1,3)*3.3/4096.0);
else
trq_value = 0;
if(CHECK_H == 0) // If harmful gas triggers
har_value = 90*(Get_Adc_Average(4,3)*3.3/4096.0);
else
har_value = 0;
if(CHECK_JQ == 0) // If formaldehyde gas triggers
jq_value = 90*(Get_Adc_Average(5,3)*3.3/4096.0);
else
jq_value = 0;
pm_value = Pm25_Get_Value(3);
if(pm_value < 200) pm_value = 0;
Dht11_Get_Temp_Humi_Value(&temp_value,&humi_value);
}
if(time_num % 10 == 0) // Send data
{
UsartPrintf(USART1,"\r\nAir Quality: %dPPM",har_value);
UsartPrintf(USART1,"\r\nPM2.5: %dPPM",pm_value);
UsartPrintf(USART1,"\r\nCombustible Gas: %dPPM",trq_value);
UsartPrintf(USART1,"\r\nFormaldehyde Gas: %dPPM",jq_value);
UsartPrintf(USART1,"\r\nSmoke: %dPPM",smog_value);
UsartPrintf(USART1,"\r\nTemperature: %d.%dC Humidity: %d.%d%%",temp_value/10,temp_value%10,humi_value/10,humi_value%10);
if(flag_mode == 0) UsartPrintf(USART1," Mode: Automatic");
else UsartPrintf(USART1," Mode: Manual");
}
if(temp_value > temp_max*10) {
if(flag_yy_temp == 0) {
UsartPrintf(USART2,"A7:00002");
flag_yy_temp = 1;
}
} else {
flag_yy_temp = 0;
}
if(humi_value > humi_max*10) {
if(flag_yy_humi == 0) {
UsartPrintf(USART2,"A7:00003");
flag_yy_humi = 1;
}
} else {
flag_yy_humi = 0;
}
if(USART1_WaitRecive() == 0) // If Bluetooth data is received
{
switch(usart1_buf[0]) {
case 'A': // A: Switch to automatic mode
flag_mode = 0;
break;
case 'B': // B: Start purification
flag_mode = 1;
RELAY_JH = 1;
RELAY_FS = 1;
break;
case 'C': // C: Stop purification
flag_mode = 1;
RELAY_JH = 0;
RELAY_FS = 0;
break;
default: break;
}
USART1_Clear();
}
}
}
/*********** Display Function ************/
void Display_function(void) {
switch(flag_display) // Display different interfaces based on display mode flag
{
case 0: // Interface 0:
Oled_ShowCHinese(1,0,"Temperature");
sprintf(display_buf,":%d",temp_value/10); // Display temperature value
Oled_ShowString(1, 4, display_buf);
Oled_ShowCHinese(1,4,"Humidity");
sprintf(display_buf,":%d%%",humi_value/10); // Display humidity value
Oled_ShowString(1, 12, display_buf);
Oled_ShowCHinese(2,0,"Smoke");
if(smog_value!=0) {
sprintf(display_buf,":%d",smog_value); // Display harmful gas value
Oled_ShowString(2, 4, display_buf);
} else Oled_ShowString(2, 4, ":0 ");
Oled_ShowCHinese(3,0,"Gas");
if(trq_value!=0) {
sprintf(display_buf,":%d",trq_value); // Display combustible gas value
Oled_ShowString(3, 4, display_buf);
} else Oled_ShowString(3, 4, ":0 ");
Oled_ShowCHinese(3,4,"Air");
if(har_value!=0) {
sprintf(display_buf,":%d",har_value); // Display smoke value
Oled_ShowString(3, 12, display_buf);
} else Oled_ShowString(3, 12, ":0 ");
Oled_ShowCHinese(4,0,"Formaldehyde");
sprintf(display_buf,":%d ",jq_value); // Display formaldehyde value
Oled_ShowString(4, 4, display_buf);
Oled_ShowCHinese(2,4,"Particles");
if(pm_value!=0) {
sprintf(display_buf,":%d",pm_value); // Display PM2.5 value
Oled_ShowString(2, 12, display_buf);
} else {
Oled_ShowString(2, 12, ":0 ");
}
if(flag_mode == 0) Oled_ShowCHinese(4,4,"Automatic");
else Oled_ShowCHinese(4,4,"Manual");
break;
case 1: // Interface 1:
Oled_ShowCHinese(1,0,"Set Maximum Smoke Value");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",smog_max);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 7, " ");
}
break;
case 2: // Interface 2:
Oled_ShowCHinese(1,0,"Set Maximum PM2.5 Value");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",pm_max);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 7, " ");
}
break;
case 3: // Interface 3:
Oled_ShowCHinese(1,0,"Set Maximum Gas Value");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",trq_max);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 7, " ");
}
break;
case 4: // Interface 4:
Oled_ShowCHinese(1,0,"Set Air Quality");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",har_max);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 7, " ");
}
break;
case 5: // Interface 5:
Oled_ShowCHinese(1,0,"Set Maximum Formaldehyde Value");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",jq_max);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 7, " ");
}
break;
case 6: // Interface 6:
Oled_ShowCHinese(1,0,"Set Maximum Temperature Value");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",temp_max);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 7, " ");
}
break;
case 7: // Interface 7:
Oled_ShowCHinese(1,0,"Set Maximum Humidity Value");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",humi_max);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 7, " ");
}
break;
default: break;
}
}
/*********** Processing Function ************/
void Manage_function(void) {
if(flag_display == 0) // Measurement interface
{
if(flag_mode == 0) {
if(smog_value > smog_max || har_value > har_max || trq_value > trq_max || jq_value > jq_max || pm_value > pm_max) // If any value exceeds the set maximum, ventilate, purify, and trigger alarm
{
RELAY_FS = 1;
RELAY_JH = 1;
if(time_num % 2 == 0) {
LED = ~LED;
BEEP = ~BEEP;
}
} else {
RELAY_FS = 0;
RELAY_JH = 0;
LED = 1;
BEEP = 0;
}
} else {
LED = 1;
BEEP = 0;
}
} else // Settings interface
{
RELAY_FS = 0;
RELAY_JH = 0;
LED = 1;
BEEP = 0;
}
}
05
—
Experimental Results
