Product image of the project:

Bilibili video link:
https://www.bilibili.com/video/BV1AcwseTE6i/?share_source=copy_web&vd_source=097fdeaf6b6ecfed8a9ff7119c32faf2
(For material sharing, see the end of the article)
01
—
Project Introduction
1. Function Details
STM32 Smart Voice Fan
The functions are as follows:
-
STM32F103C8T6 microcontroller as the main control unit
-
Temperature collection: DS18B20 sensor collects ambient temperature
-
Human detection: Infrared sensor determines if someone is present
-
Screen display: OLED displays temperature, presence, gear, mode, and other information
-
Fan oscillation: Stepper motor simulates fan oscillation
-
Manual mode: Button adjusts fan speed
-
Automatic mode: Determines presence and controls fan switch and speed based on temperature
-
Voice mode: Controls fan speed, mode, and oscillation via voice
-
Timing function: Timed shutdown
-
APP control: Remote data viewing and control via Bluetooth
2. Bill of Materials
-
STM32F103C8T6 microcontroller
-
OLED screen
-
DS18B20 temperature sensor
-
HC-SR501 human infrared sensor
-
TB6612 motor driver module
-
ESP8266-01S (WIFI module)
-
SNR8016 voice module
-
Active buzzer
-
Fan module
-
ULN2003 stepper motor driver chip
-
Stepper motor
-
LED light
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 "ds18b20.h"
#include "motor_bujin.h"
#include "timer.h"
#include "motor_dianji.h"
int main(void){
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // Configure interrupt group 2
Delay_Init(); // Delay initialization
Gpio_Init(); // IO initialization
Key_Init(); // Key initialization
while(DS18B20_Init()); // DS18B20 initialization
Oled_Init(); // OLED initialization
Oled_Clear_All(); // Clear screen
Usart1_Init(9600); // Serial port 1 initialization
Usart2_Init(9600); // Serial port 2 initialization
Step_Motor_Init(); // Stepper motor initialization
TIM2_Init(1000-1,7200-1); // Initialize 0.1s timer
Motor_Dianji_Init(); // DC motor initialization
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
Delay_ms(1000);
Delay_ms(1000);
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 interface
flag_display++;
if(flag_display >= 6)
flag_display = 0;
Oled_Clear_All();
break;
case 2: // Key 2: Increase key
switch(flag_display) {
case 0: // Interface 0: Fan speed switch
flag_mode = 1;
motor_pwm+=300;
if(motor_pwm <= 900) {
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else {
motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
}
break;
case 1: // Interface 1: Maximum temperature +1
if(temp_max < 99)
temp_max++;
break;
case 2: // Interface 2: Minimum temperature +1
if(temp_min < temp_max-1)
temp_min++;
break;
case 3: // Interface 3: Countdown +1
if(countdown_hour < 100)
countdown_hour++;
break;
case 4: // Interface 4: Countdown minutes +1
countdown_minute++;
if(countdown_minute >= 60)
countdown_minute = 0;
break;
case 5: // Interface 5: Countdown seconds +1
countdown_second++;
if(countdown_second >= 60)
countdown_second = 0;
break;
default:
break;
}
break;
case 3: // Key 3: Decrease key
switch(flag_display) {
case 0: // Interface 0: Oscillation mode switch
if(flag_direction == 0)
flag_direction = 1;
else
flag_direction = 0;
break;
case 1: // Interface 1: Maximum temperature -1
if(temp_max > temp_min+1)
temp_max--;
break;
case 2: // Interface 2: Minimum temperature -1
if(temp_min > 0)
temp_min--;
break;
case 3: // Interface 3: Countdown -1
if(countdown_hour > 0)
countdown_hour--;
break;
case 4: // Interface 4: Countdown minutes -1
countdown_minute--;
if(countdown_minute < 0)
countdown_minute = 59;
break;
case 5: // Interface 5: Countdown seconds -1
countdown_second--;
if(countdown_second < 0)
countdown_second = 59;
break;
default:
break;
}
break;
case 4: // Key 4: Switch mode
if(flag_display == 0) {
if(flag_mode == 0) {
flag_mode = 1;
} else {
flag_mode = 0;
}
}
break;
default:
break;
}
}
if(flag_display == 0) { // Measurement interface
if(time_num % 10 == 0) { // Check every 2 seconds
temp_value = DS18B20_Get_Temp(); // Get temperature value
if(time_num % 30 == 0) { // Send data
UsartPrintf(USART1,"\r\nTemperature: %d.%dC",temp_value/10,temp_value%10);
if(HUMAN == 1)
UsartPrintf(USART1," Human: Present");
else
UsartPrintf(USART1," Human: Not Present");
if(motor_pwm == 0)
UsartPrintf(USART1,"\r\nOff");
else if(motor_pwm == 300)
UsartPrintf(USART1,"\r\nGear 1");
else if(motor_pwm == 600)
UsartPrintf(USART1,"\r\nGear 2");
else if(motor_pwm == 900)
UsartPrintf(USART1,"\r\nGear 3");
if(flag_direction == 1)
UsartPrintf(USART1," Oscillation On");
else
UsartPrintf(USART1," Oscillation Off");
if(flag_mode == 0)
UsartPrintf(USART1,"\r\nAutomatic");
else
UsartPrintf(USART1,"\r\nManual");
}
}
if(USART1_WaitRecive() == 0) { // Receive Bluetooth data
if(usart1_buf[0] == 'A') { // A: Switch mode
if(flag_mode == 0) {
flag_mode = 1;
} else {
flag_mode = 0;
}
} else if(usart1_buf[0] == 'B') { // B: Increase speed
flag_mode = 1;
motor_pwm+=300;
if(motor_pwm <= 900) {
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else {
motor_pwm = 900;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
}
} else if(usart1_buf[0] == 'C') { // C: Decrease speed
flag_mode = 1;
motor_pwm-=300;
if(motor_pwm > 0) {
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else {
motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
}
} else if(usart1_buf[0] == 'D') { // D: Toggle oscillation
flag_dir++;
if(flag_dir%2==1)
flag_direction = 1;
else
flag_direction = 0;
}
USART1_Clear();
}
if(USART2_WaitRecive() == 0) { // Receive voice commands
if(usart2_buf[0] == 0x01) // 0x01: Switch to manual mode
flag_mode = 1;
else if(usart2_buf[0] == 0x02) // 0x02: Switch to automatic mode
flag_mode = 0;
else if(usart2_buf[0] == 0x03) // 0x03: Turn off oscillation
flag_direction = 0;
else if(usart2_buf[0] == 0x04) // 0x04: Turn on oscillation
flag_direction = 1;
else if(usart2_buf[0] == 0x05) // 0x05: Decrease speed
flag_mode = 1;
motor_pwm-=300;
if(motor_pwm > 0) {
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else {
motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
}
} else if(usart2_buf[0] == 0x06) // 0x06: Increase speed
flag_mode = 1;
motor_pwm+=300;
if(motor_pwm <= 900) {
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else {
motor_pwm = 900;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
}
USART2_Clear();
}
}
switch(flag_display) { // Display different interfaces based on the display mode flag
case 0: // Interface 0: Measurement interface, display temperature, human presence, gear, oscillation, countdown time
Oled_ShowCHinese(1, 0, "Temperature:");
sprintf(display_buf,"%d.%dC ",temp_value/10,temp_value%10);
Oled_ShowString(1, 6, display_buf);
Oled_ShowCHinese(2, 0, "Human:");
if(HUMAN == 1)
Oled_ShowCHinese(2, 3, "Present");
else
Oled_ShowCHinese(2, 3, "Not Present");
if(motor_pwm == 0)
Oled_ShowCHinese(3, 0, "Off");
else if(motor_pwm == 300)
Oled_ShowCHinese(3, 0, "Gear 1");
else if(motor_pwm == 600)
Oled_ShowCHinese(3, 0, "Gear 2");
else if(motor_pwm == 900)
Oled_ShowCHinese(3, 0, "Gear 3");
if(flag_direction == 1)
Oled_ShowCHinese(3, 5, "Oscillation On");
else
Oled_ShowCHinese(3, 5, "Oscillation Off");
sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(4,0,display_buf);
if(flag_mode == 0)
Oled_ShowCHinese(4, 5, "Automatic");
else
Oled_ShowCHinese(4, 5, "Manual");
break;
case 1: // Interface 1: Display maximum temperature setting
Oled_ShowCHinese(1,0,"Set Maximum Temperature");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",temp_max);
Oled_ShowString(2, 6, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 6, " ");
}
break;
case 2: // Interface 2: Display minimum temperature setting
Oled_ShowCHinese(1,0,"Set Minimum Temperature");
if(time_num % 5 == 0) {
sprintf(display_buf,"%d ",temp_min);
Oled_ShowString(2, 6, display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2, 6, " ");
}
break;
case 3: // Interface 3: Display countdown setting
Oled_ShowCHinese(1,1,"Set Countdown");
if(time_num % 5 == 0) {
sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(2,4,display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2,4," ");
}
break;
case 4: // Interface 4: Display countdown minutes setting
Oled_ShowCHinese(1,1,"Set Countdown Minutes");
if(time_num % 5 == 0) {
sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(2,4,display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2,7," ");
}
break;
case 5: // Interface 5: Display countdown seconds setting
Oled_ShowCHinese(1,1,"Set Countdown Seconds");
if(time_num % 5 == 0) {
sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(2,4,display_buf);
}
if(time_num % 10 == 0) {
Oled_ShowString(2,10," ");
}
break;
default:
break;
}
if(flag_display == 0) { // Measurement interface
if(flag_mode == 0) { // In automatic mode, (if someone is present, temperature greater than max is gear 3, greater than average is gear 2, greater than min is gear 1, otherwise off)
if((temp_value > temp_max*10) && HUMAN == 1) {
motor_pwm = 900;
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else if((temp_value > ((temp_max+temp_min)/2)*10) && HUMAN == 1) {
motor_pwm = 600;
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else if((temp_value > temp_min*10) && HUMAN == 1) {
motor_pwm = 300;
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);
} else {
motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
}
}
if(countdown_hour != 0 || countdown_minute != 0 || countdown_second != 0) // If countdown is not zero
flag_countdown_begin = 1; // Start countdown, enter timer interrupt
if(flag_1s == 1) { // 1s reached
flag_1s = 0;
if(countdown_second > 0) // Countdown seconds > 0
countdown_second--; // Countdown seconds -1
else { // Countdown seconds = 0
if(countdown_minute > 0) // If countdown minutes > 0
countdown_minute--; // Countdown minutes -1
countdown_second = 59; // Countdown seconds = 59
} else { // If countdown minutes = 0
if(countdown_hour > 0) // If countdown hours > 0
countdown_hour--; // Countdown hours -1
countdown_minute = 59; // Countdown minutes = 59
countdown_second = 59; // Countdown seconds = 59
} else { // If countdown hours = 0, countdown ends
flag_countdown_begin = 0;
flag_countdown_end = 1;
}
}
}
if(flag_countdown_end == 1) { // Countdown ends, turn off fan and enter manual mode
flag_countdown_end = 0;
motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
flag_mode = 1;
}
if(motor_pwm == 0)
flag_direction = 0;
if(flag_direction == 1) { // If in oscillation mode, stepper motor oscillates
if(flag_bujin_state == 0) {
flag_bujin_foreward = 1;
flag_bujin_reversal = 0;
} else {
flag_bujin_foreward = 0;
flag_bujin_reversal = 1;
}
} else {
flag_bujin_foreward = 0;
flag_bujin_reversal = 0;
}
} else { // Setting interface, turn off fan
flag_countdown_begin = 0;
flag_direction = 0;
flag_bujin_foreward = 0;
flag_bujin_reversal = 0;
motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);
}
time_num++; // Timing variable +1
Delay_ms(10);
if(time_num %5 == 0) // Minimum system LED blinking
LED_SYS = ~LED_SYS;
if(time_num >= 5000) {
time_num = 0;
}
}
}
05
—
Experimental Results


Material Sharing (Baidu Cloud)
https://pan.baidu.com/s/1UMesR_4CFcDdBO_uG4q_IQ?pwd=bexm Extraction code: bexm
(Or scan the QR code below to obtain)
For physical purchase, scan the QR code below