Project finished product images:

Bilibili video link:
https://www.bilibili.com/video/BV1CQkTYzEQ9/?share_source=copy_web&vd_source=097fdeaf6b6ecfed8a9ff7119c32faf2
(Material sharing can be found at the end of the article)
01
—
Project Introduction
1. Function Details
STM32 Smart Wardrobe
Functions are as follows:
-
DHT11 detects temperature and humidity, sets thresholds for heating and ventilation
-
RTC obtains real-time time and regularly performs UV disinfection
-
Photoelectric sensor detects cabinet door status, disinfects when closed, and determines lighting based on illumination when opened
-
Light sensor collects light intensity, displays environmental data, and determines whether to turn on the light based on this
-
MQ2 smoke sensor detects smoke concentration; if exceeded, triggers buzzer alarm
-
Buttons can adjust time and all threshold information and timing information
-
Bluetooth app can remotely control the wardrobe system to execute heating, ventilation, lighting, and disinfection functions
-
Bluetooth app can receive real-time environmental information data
2. Bill of Materials
-
STM32F103C8T6 microcontroller
-
OLED screen
-
DHT11 temperature and humidity sensor
-
BT04A Bluetooth module
-
Light sensor
-
Photoelectric infrared sensor
-
MQ-2 smoke sensor
-
Relay
-
Fan module
-
High-power LED light module
-
Active buzzer
-
Stepper motor
-
UV disinfection lamp module
-
Heating plate 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 "rtc.h"
#include "dht11.h"
#include "adc.h"
#include "motor_bujin.h"
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); // Serial port 1 initialization
Adc_Init(); // ADC initialization
Step_Motor_Init(); // Stepper motor initialization
while(RTC_Init()); // RTC initialization
while(DHT11_Init()); // DHT11 initialization
Delay_ms(1000);
Delay_ms(1000);
// DHT11_Init();
while(1) {
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 >= 15)
flag_display = 0;
if(flag_display >= 1 && flag_display <= 6) {
RTC_ITConfig(RTC_IT_SEC, DISABLE);
} else {
RTC_Set(calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);
RTC_ITConfig(RTC_IT_SEC, ENABLE);
}
Oled_Clear_All(); // Clear screen
break;
case 2: // Key 2
switch(flag_display) {
case 0:
if(flag_bujin_state == 0)
flag_bujin_foreward = 1;
break;
case 1: // Interface 1: Modify year +1
calendar.w_year++;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 2: // Interface 2: Modify month +1
calendar.w_month++;
if(calendar.w_month > 12)
calendar.w_month = 1;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 3: // Interface 3: Modify day +1
calendar.w_date++;
if(calendar.w_date > 31)
calendar.w_date = 1;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 4: // Interface 4: Modify hour +1
calendar.hour++;
if(calendar.hour > 23)
calendar.hour = 0;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 5: // Interface 5: Modify minute +1
calendar.min++;
if(calendar.min > 59)
calendar.min = 0;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 6: // Interface 6: Modify second +1
calendar.sec++;
if(calendar.sec > 59)
calendar.sec = 0;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 7: // Interface 7: Interval timing start hour +1
time_shi_begin++;
if(time_shi_begin >= 24)
time_shi_begin = 0;
break;
case 8: // Interface 8: Interval timing start minute +1
time_fen_begin++;
if(time_fen_begin >= 60)
time_fen_begin = 0;
break;
case 9: // Interface 9: Interval timing end hour +1
time_shi_end++;
if(time_shi_end >= 24)
time_shi_end = 0;
break;
case 10: // Interface 10: Interval timing end minute +1
time_fen_end++;
if(time_fen_end >= 60)
time_fen_end = 0;
break;
case 11: // Interface 11: Minimum temperature +1
if(temp_min < 99)
temp_min++;
break;
case 12: // Interface 12: Maximum humidity +1
if(humi_max < 99)
humi_max++;
break;
case 13: // Interface 13: Minimum light +1
if(light_min < 99)
light_min++;
break;
case 14: // Interface 14: Maximum smoke +1
if(smog_max < 200)
smog_max++;
break;
default:
break;
}
break;
case 3: // Key 3
switch(flag_display) {
case 0:
if(flag_bujin_state == 1)
flag_bujin_reversal = 1;
break;
case 1: // Interface 1: Modify year -1
calendar.w_year--;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 2: // Interface 2: Modify month -1
calendar.w_month--;
if(calendar.w_month < 1)
calendar.w_month = 12;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 3: // Interface 3: Modify day -1
calendar.w_date--;
if(calendar.w_date < 1)
calendar.w_date = 31;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 4: // Interface 4: Modify hour -1
if(calendar.hour == 0)
calendar.hour = 23;
else
calendar.hour--;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 5: // Interface 5: Modify minute -1
if(calendar.min == 0)
calendar.min = 59;
else
calendar.min--;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 6: // Interface 6: Modify second -1
if(calendar.sec == 0)
calendar.sec = 59;
else
calendar.sec--;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 7: // Interface 7: Interval timing start hour -1
time_shi_begin--;
if(time_shi_begin < 0)
time_shi_begin = 23;
break;
case 8: // Interface 8: Interval timing start minute -1
time_fen_begin--;
if(time_fen_begin < 0)
time_fen_begin = 59;
break;
case 9: // Interface 9: Interval timing end hour -1
time_shi_end--;
if(time_shi_end < 0)
time_shi_end = 23;
break;
case 10: // Interface 10: Interval timing end minute -1
time_fen_end--;
if(time_fen_end < 0)
time_fen_end = 59;
break;
case 11: // Interface 11: Minimum temperature -1
if(temp_min > 0)
temp_min--;
break;
case 12: // Interface 12: Maximum humidity -1
if(humi_max > 0)
humi_max--;
break;
case 13: // Interface 13: Minimum light -1
if(light_min > 0)
light_min--;
break;
case 14: // Interface 14: Maximum smoke -1
if(smog_max > 0)
smog_max--;
break;
default:
break;
}
break;
default:
break;
}
}
if(flag_display == 0) // Measurement interface
{
if(time_num % 10 == 0) // Get data
{
DHT11_Read_Data(&temp_value,&humi_value); // Get temperature and humidity values
light_value = 99-30*(Get_Adc_Average(1,3)*3.3/4096.0); // Get light intensity
if(SMOG == 0)
smog_value = 60*(Get_Adc_Average(0,3)*3.3/4096.0); // Get smoke value
else
smog_value = 0;
}
if(time_num % 30 == 0) // Send data
{
UsartPrintf(USART1,"Temperature: %d.%dC\r\n",temp_value/10,temp_value%10);
UsartPrintf(USART1,"Humidity: %d.%dC\r\n",humi_value/10,humi_value%10);
UsartPrintf(USART1,"Light: %dlux\r\n",light_value);
UsartPrintf(USART1,"Smoke: %dPPM\r\n",smog_value);
}
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: PA7
flag_mode = 1;
RELAY_TF = !RELAY_TF;
break;
case('C'): // C: PA6
flag_mode = 1;
RELAY_JR =!RELAY_JR;
break;
case('D'): // D: PA5
flag_mode = 1;
RELAY_XD =!RELAY_XD;
break;
case('E'): // E: PA4
flag_mode = 1;
RELAY_ZM =!RELAY_ZM;
break;
default:
break;
}
USART1_Clear();
}
}
switch(flag_display) // Display different interfaces based on different display mode flags
{
case 0: // Interface 0:
sprintf(display_buf,"%d-%d%d-%d%d",calendar.w_year,calendar.w_month/10,calendar.w_month%10,calendar.w_date/10,calendar.w_date%10);
Oled_ShowString(1, 2, display_buf);
sprintf(display_buf,"%d%d:%d%d:%d%d",calendar.hour/10,calendar.hour%10,calendar.min/10,calendar.min%10,calendar.sec/10,calendar.sec%10);
Oled_ShowString(2, 3, display_buf);
Oled_ShowString(3,0,"T:");
sprintf(display_buf,"%d.%dC ",temp_value/10,temp_value%10);
Oled_ShowString(3,2,display_buf);
Oled_ShowString(3,8,"H:");
sprintf(display_buf,"%d.%dC ",humi_value/10,humi_value%10);
Oled_ShowString(3,10,display_buf);
Oled_ShowString(4,0,"L:");
sprintf(display_buf,"%dlux ",light_value);
Oled_ShowString(4,2,display_buf);
Oled_ShowString(4,8,"S:");
sprintf(display_buf,"%dppm ",smog_value);
Oled_ShowString(4,10,display_buf);
break;
case 1: // Interface 1: Display set time year
Oled_ShowCHinese(1,2,"Set Time");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d-%d%d-%d%d",calendar.w_year,calendar.w_month/10,calendar.w_month%10,calendar.w_date/10,calendar.w_date%10);
Oled_ShowString(2, 2, display_buf);
sprintf(display_buf,"%d%d:%d%d:%d%d",calendar.hour/10,calendar.hour%10,calendar.min/10,calendar.min%10,calendar.sec/10,calendar.sec%10);
Oled_ShowString(3, 3, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(2, 2, " ");
}
break;
case 2: // Interface 2: Display set time month
Oled_ShowCHinese(1,2,"Set Time");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d-%d%d-%d%d",calendar.w_year,calendar.w_month/10,calendar.w_month%10,calendar.w_date/10,calendar.w_date%10);
Oled_ShowString(2, 2, display_buf);
sprintf(display_buf,"%d%d:%d%d:%d%d",calendar.hour/10,calendar.hour%10,calendar.min/10,calendar.min%10,calendar.sec/10,calendar.sec%10);
Oled_ShowString(3, 3, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(2, 7, " ");
}
break;
case 3: // Interface 3: Display set time day
Oled_ShowCHinese(1,2,"Set Time");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d-%d%d-%d%d",calendar.w_year,calendar.w_month/10,calendar.w_month%10,calendar.w_date/10,calendar.w_date%10);
Oled_ShowString(2, 2, display_buf);
sprintf(display_buf,"%d%d:%d%d:%d%d",calendar.hour/10,calendar.hour%10,calendar.min/10,calendar.min%10,calendar.sec/10,calendar.sec%10);
Oled_ShowString(3, 3, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(2, 10, " ");
}
break;
case 4: // Interface 4: Display set time hour
Oled_ShowCHinese(1,2,"Set Time");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d-%d%d-%d%d",calendar.w_year,calendar.w_month/10,calendar.w_month%10,calendar.w_date/10,calendar.w_date%10);
Oled_ShowString(2, 2, display_buf);
sprintf(display_buf,"%d%d:%d%d:%d%d",calendar.hour/10,calendar.hour%10,calendar.min/10,calendar.min%10,calendar.sec/10,calendar.sec%10);
Oled_ShowString(3, 3, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(3, 3, " ");
}
break;
case 5: // Interface 5: Display set time minute
Oled_ShowCHinese(1,2,"Set Time");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d-%d%d-%d%d",calendar.w_year,calendar.w_month/10,calendar.w_month%10,calendar.w_date/10,calendar.w_date%10);
Oled_ShowString(2, 2, display_buf);
sprintf(display_buf,"%d%d:%d%d:%d%d",calendar.hour/10,calendar.hour%10,calendar.min/10,calendar.min%10,calendar.sec/10,calendar.sec%10);
Oled_ShowString(3, 3, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(3, 6, " ");
}
break;
case 6: // Interface 6: Display set time second
Oled_ShowCHinese(1,2,"Set Time");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d-%d%d-%d%d",calendar.w_year,calendar.w_month/10,calendar.w_month%10,calendar.w_date/10,calendar.w_date%10);
Oled_ShowString(2, 2, display_buf);
sprintf(display_buf,"%d%d:%d%d:%d%d",calendar.hour/10,calendar.hour%10,calendar.min/10,calendar.min%10,calendar.sec/10,calendar.sec%10);
Oled_ShowString(3, 3, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(3, 9, " ");
}
break;
case 7: // Interface 7: Display set interval timing start hour
Oled_ShowCHinese(1,0,"Set Timing Start Hour");
if(time_num % 5 == 0)
{
Oled_ShowNum_2(2,5,time_shi_begin);
Oled_ShowNum_2(2,8,time_fen_begin);
Oled_ShowString(2,7,":");
}
if(time_num % 10 == 0)
{
Oled_ShowString(2,5," :");
}
break;
case 8: // Interface 8: Display set interval timing start minute
Oled_ShowCHinese(1,0,"Set Timing Start Minute");
if(time_num % 5 == 0)
{
Oled_ShowNum_2(2,5,time_shi_begin);
Oled_ShowNum_2(2,8,time_fen_begin);
Oled_ShowString(2,7,":");
}
if(time_num % 10 == 0)
{
Oled_ShowString(2,7,": ");
}
break;
case 9: // Interface 9: Display set interval timing end hour
Oled_ShowCHinese(1,0,"Set Timing End Hour");
if(time_num % 5 == 0)
{
Oled_ShowNum_2(2,5,time_shi_end);
Oled_ShowNum_2(2,8,time_fen_end);
Oled_ShowString(2,7,":");
}
if(time_num % 10 == 0)
{
Oled_ShowString(2,5," :");
}
break;
case 10: // Interface 10: Display set interval timing end minute
Oled_ShowCHinese(1,0,"Set Timing End Minute");
if(time_num % 5 == 0)
{
Oled_ShowNum_2(2,5,time_shi_end);
Oled_ShowNum_2(2,8,time_fen_end);
Oled_ShowString(2,7,":");
}
if(time_num % 10 == 0)
{
Oled_ShowString(2,7,": ");
}
break;
case 11: // Interface 11: Display set minimum temperature
Oled_ShowCHinese(1,0,"Set Minimum Temperature");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d ",temp_min);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(2, 7, " ");
}
break;
case 12: // Interface 12: Display set maximum humidity
Oled_ShowCHinese(1,0,"Set Maximum Humidity");
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;
case 13: // Interface 13: Display set minimum light
Oled_ShowCHinese(1,0,"Set Minimum Light");
if(time_num % 5 == 0)
{
sprintf(display_buf,"%d ",light_min);
Oled_ShowString(2, 7, display_buf);
}
if(time_num % 10 == 0)
{
Oled_ShowString(2, 7, " ");
}
break;
case 14: // Interface 14: Display set maximum smoke
Oled_ShowCHinese(1,0,"Set Maximum Smoke");
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;
default:
break;
}
u8 time_shi,time_fen; // Current time
if(flag_display == 0) // Measurement interface
{
if(flag_mode == 0) {
time_shi = calendar.hour; // Get current time
time_fen = calendar.min;
if(time_shi_begin*60+time_fen_begin > time_shi_end*60+time_fen_end) // Start time > End time
{
if((time_shi_end*60+time_fen_end <= time_shi*60+time_fen) && (time_shi*60+time_fen < time_shi_begin*60+time_fen_begin)) // Current time not in set time
flag_time_on = 0;
else // Otherwise
flag_time_on = 1;
}
else if(time_shi_begin*60+time_fen_begin < time_shi_end*60+time_fen_end) // Start time < End time
{
if((time_shi_begin*60+time_fen_begin <= time_shi*60+time_fen) && (time_shi*60+time_fen < time_shi_end*60+time_fen_end)) // Current time in set time
flag_time_on = 1;
else // Otherwise
flag_time_on = 0;
}
if(DOOR == 1) // If the wardrobe door is closed, turn off the light
{
RELAY_ZM = 0;
if(flag_time_on == 1) // If within timing time, turn on disinfection
{
RELAY_XD = 1;
}
else // If outside timing time, turn off disinfection
{
RELAY_XD = 0;
}
}
else // If the wardrobe door is open, turn off disinfection, if light is below minimum value, turn on light
{
RELAY_XD = 0;
if(light_value < light_min)
{
RELAY_ZM = 1;
}
else
{
RELAY_ZM = 0;
}
}
if(temp_value < temp_min*10 || humi_value > humi_max*10) // If temperature is below minimum value or humidity is above maximum value, then turn on heating, if humidity is above maximum value also need to ventilate
{
RELAY_JR = 1;
if(humi_value > humi_max*10)
RELAY_TF = 1;
else
RELAY_TF = 0;
}
else
{
RELAY_JR = 0;
RELAY_TF = 0;
}
if(smog_value > smog_max) // Smoke value exceeds set maximum value, sound and light alarm
{
if(time_num % 3 == 0)
{
LED=~LED;
BEEP=~BEEP;
}
}
else
{
LED = 1;
BEEP = 0;
}
}
}
else // Settings interface, turn off all relays
{
LED = 1;
BEEP = 0;
RELAY_ZM = 0;
RELAY_TF = 0;
RELAY_JR = 0;
RELAY_XD = 0;
}
time_num++; // Timing variable +1
Delay_ms(10);
if(time_num %10 == 0)
LED_SYS = ~LED_SYS;
if(time_num >= 5000)
{
time_num = 0;
}
}
05
—
Experimental Results


Material Sharing (Baidu Cloud)
https://pan.baidu.com/s/1aADIDQRNWVJwFHP4u-GZIg?pwd=9ekh Extraction code: 9ekh
(Or scan the QR code below to obtain)
Purchase physical items by scanning the QR code below