STM32 Project Share: Smart Voice Fan

Product image of the project:

STM32 Project Share: Smart Voice Fan

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:

  1. STM32F103C8T6 microcontroller as the main control unit

  2. Temperature collection: DS18B20 sensor collects ambient temperature

  3. Human detection: Infrared sensor determines if someone is present

  4. Screen display: OLED displays temperature, presence, gear, mode, and other information

  5. Fan oscillation: Stepper motor simulates fan oscillation

  6. Manual mode: Button adjusts fan speed

  7. Automatic mode: Determines presence and controls fan switch and speed based on temperature

  8. Voice mode: Controls fan speed, mode, and oscillation via voice

  9. Timing function: Timed shutdown

  10. 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

STM32 Project Share: Smart Voice Fan

03

PCB Hardware Design

PCB Diagram

STM32 Project Share: Smart Voice FanSTM32 Project Share: Smart Voice Fan

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

STM32 Project Share: Smart Voice FanSTM32 Project Share: Smart Voice Fan

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)STM32 Project Share: Smart Voice FanFor physical purchase, scan the QR code belowSTM32 Project Share: Smart Voice Fan

Leave a Comment