目录
一、前言
项目成品图片:
哔哩哔哩视频链接:
STM32智能衣柜
(资料分享见文末)
二、项目简介
1.功能详解
基于STM32智能衣柜
功能如下:
- DHT11检测温湿度、设置阈值进行加热和通风
- RTC获取实时时间,并定时进行紫外线UV消毒
- 通过光电传感器检测柜门状态,关门时消毒,开门时判断光照开灯
- 光敏传感器采集光强,显示环境数据并根据此判断是否需要开灯
- MQ2烟雾传感器检测烟雾浓度是否超标,超标触发蜂鸣器声光报警
- 按键可调节时间和全部的阈值信息与定时信息
- 蓝牙APP可远程控制衣柜系统进行执行加热、通风、照明和消毒的功能
- 蓝牙APP可接收环境的实时信息数据
2.主要器件
- STM32F103C8T6单片机
- OLED 屏幕
- DHT11温湿度传感器
- BT04A蓝牙模块
- 光敏传感器
- 光电红外传感器
- MQ-2烟雾传感器
- 继电器
- 风扇模块
- 大功率LED灯模块
- 有源蜂鸣器
- 步进电机
- UV消毒灯模块
- 加热片模块
三、原理图设计
四、PCB硬件设计
PCB图
五、程序设计
#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); //配置中断优先分组
Delay_Init(); //延时初始化
Gpio_Init(); //IO初始化
Key_Init(); //按键初始化
Oled_Init(); //OLED初始化
Oled_Clear_All(); //清屏
Usart1_Init(9600); //串口1初始化
Adc_Init(); //ADC初始化
Step_Motor_Init(); //步进电机初始化
while(RTC_Init()); //RTC初始化
while(DHT11_Init()); //DHT11初始化
Delay_ms(1000);
Delay_ms(1000);
// DHT11_Init();
while(1)
{
key_num = Chiclet_Keyboard_Scan(0); //按键扫描
if(key_num != 0) //有按键按下
{
switch(key_num)
{
case 1: //按键1,切换设置界面
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(); //清屏
break;
case 2: //按键2
switch(flag_display)
{
case 0:
if(flag_bujin_state == 0)
flag_bujin_foreward = 1;
break;
case 1: //界面1:修改年+1
calendar.w_year ++;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 2: //界面2:修改月+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: //界面3:修改日+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: //界面4:修改时+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: //界面5:修改分+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: //界面6:修改秒+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: //界面7:区间定时开始时+1
time_shi_begin++;
if(time_shi_begin >= 24)
time_shi_begin = 0;
break;
case 8: //界面8:区间定时开始分+1
time_fen_begin++;
if(time_fen_begin >= 60)
time_fen_begin = 0;
break;
case 9: //界面9:区间定时结束时+1
time_shi_end++;
if(time_shi_end >= 24)
time_shi_end = 0;
break;
case 10: //界面10:区间定时结束分+1
time_fen_end++;
if(time_fen_end >= 60)
time_fen_end = 0;
break;
case 11: //界面11:温度最小值+1
if(temp_min < 99)
temp_min++;
break;
case 12: //界面12:湿度最大值+1
if(humi_max < 99)
humi_max++;
break;
case 13: //界面13:光照最小值+1
if(light_min < 99)
light_min++;
break;
case 14: //界面14:烟雾最大值+1
if(smog_max < 200)
smog_max++;
break;
default:
break;
}
break;
case 3: //按键3
switch(flag_display)
{
case 0:
if(flag_bujin_state == 1)
flag_bujin_reversal = 1;
break;
case 1: //界面1:修改年-1
calendar.w_year --;
RTC_Set(calendar.w_year,calendar.w_month ,calendar.w_date ,calendar.hour ,calendar.min ,calendar.sec);
break;
case 2: //界面2:修改月-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: //界面3:修改日-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: //界面4:修改时-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: //界面5:修改分-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: //界面6:修改秒-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: //界面7:区间定时开始时-1
time_shi_begin--;
if(time_shi_begin < 0)
time_shi_begin = 23;
break;
case 8: //界面8:区间定时开始分-1
time_fen_begin--;
if(time_fen_begin < 0)
time_fen_begin = 59;
break;
case 9: //界面9:区间定时结束时-1
time_shi_end--;
if(time_shi_end < 0)
time_shi_end = 23;
break;
case 10: //界面10:区间定时结束分-1
time_fen_end--;
if(time_fen_end < 0)
time_fen_end = 59;
break;
case 11: //界面11:温度最小值-1
if(temp_min > 0)
temp_min--;
break;
case 12: //界面12:湿度最大值-1
if(humi_max > 0)
humi_max--;
break;
case 13: //界面13:光照最小值-1
if(light_min > 0)
light_min--;
break;
case 14: //界面14:烟雾最大值-1
if(smog_max > 0)
smog_max--;
break;
default:
break;
}
break;
default:
break;
}
}
if(flag_display == 0) //测量界面
{
if(time_num % 10 == 0) //获取数据
{
DHT11_Read_Data(&temp_value,&humi_value); //获取温湿度值
light_value = 99-30*(Get_Adc_Average(1,3)*3.3/4096.0); //获取光照强度
if(SMOG == 0)
smog_value = 60*(Get_Adc_Average(0,3)*3.3/4096.0); //获取烟雾值
else
smog_value = 0;
}
if(time_num % 30 == 0) //发送数据
{
UsartPrintf(USART1,"温度:%d.%dC\r\n",temp_value/10,temp_value%10);
UsartPrintf(USART1,"湿度:%d.%dC\r\n",humi_value/10,humi_value%10);
UsartPrintf(USART1,"光照:%dlux\r\n",light_value);
UsartPrintf(USART1,"烟雾:%dPPM\r\n",smog_value);
}
if(USART1_WaitRecive() == 0) //如果接收到蓝牙数据
{
switch(usart1_buf[0])
{
case('A'): //A:切换自动模式
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) //根据不同的显示模式标志位,显示不同的界面
{
case 0: //界面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: //界面1:显示设置时间年
Oled_ShowCHinese(1,2,"设置时间");
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: //界面2:显示设置时间月
Oled_ShowCHinese(1,2,"设置时间");
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: //界面3:显示设置时间日
Oled_ShowCHinese(1,2,"设置时间");
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: //界面4:显示设置时间时
Oled_ShowCHinese(1,2,"设置时间");
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: //界面5:显示设置时间分
Oled_ShowCHinese(1,2,"设置时间");
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: //界面6:显示设置时间秒
Oled_ShowCHinese(1,2,"设置时间");
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: //界面7:显示设置区间定时开始时
Oled_ShowCHinese(1,0,"设置定时开始时");
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: //界面8:显示设置区间定时开始分
Oled_ShowCHinese(1,0,"设置定时开始分");
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: //界面9:显示设置区间定时结束时
Oled_ShowCHinese(1,0,"设置定时结束时");
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: //界面10:显示设置区间定时结束分
Oled_ShowCHinese(1,0,"设置定时结束分");
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: //界面11:显示设置温度最小值
Oled_ShowCHinese(1,0,"设置温度最小值");
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: //界面12:显示设置湿度最大值
Oled_ShowCHinese(1,0,"设置湿度最大值");
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: //界面13:显示设置光照最小值
Oled_ShowCHinese(1,0,"设置光照最小值");
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: //界面14:显示设置烟雾最大值
Oled_ShowCHinese(1,0,"设置烟雾最大值");
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; //当前时间
if(flag_display == 0) //测量界面
{
if(flag_mode == 0)
{
time_shi = calendar.hour; //获取当前时间
time_fen = calendar.min;
if(time_shi_begin*60+time_fen_begin > time_shi_end*60+time_fen_end) //开始时间>结束时间
{
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)) //当前时间不在设置的时间内
flag_time_on = 0;
else //否则
flag_time_on = 1;
}
else if(time_shi_begin*60+time_fen_begin < time_shi_end*60+time_fen_end) //开始时间<结束时间
{
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)) //当前时间在设置的时间内
flag_time_on = 1;
else //否则
flag_time_on = 0;
}
if(DOOR == 1) //如果衣柜门是关闭的,关照明灯
{
RELAY_ZM = 0;
if(flag_time_on == 1) //如果在定时时间之内,开消毒
{
RELAY_XD = 1;
}
else //如果在定时时间之外,关消毒
{
RELAY_XD = 0;
}
}
else //如果衣柜门是打开的,关消毒,如果光照低于最小值开照明灯
{
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)
{ //如果温度低于最小值或湿度大于最大值,那么打开加热,如果是湿度大于最小值还需开通风
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) //烟雾值大于设置的最大值,声光报警
{
if(time_num % 3 == 0)
{
LED=~LED;
BEEP=~BEEP;
}
}
else
{
LED = 1;
BEEP = 0;
}
}
}
else //设置界面,关闭所有继电器
{
LED = 1;
BEEP = 0;
RELAY_ZM = 0;
RELAY_TF = 0;
RELAY_JR = 0;
RELAY_XD = 0;
}
time_num++; //计时变量+1
Delay_ms(10);
if(time_num %10 == 0)
LED_SYS = ~LED_SYS;
if(time_num >= 5000)
{
time_num = 0;
}
}
}
六、实验效果
七、资料内容