基于STM32的智能摇头风扇设计(WIFI+语音控制)

✌️✌️✌️大家好,这里是5132单片机毕设设计项目分享,今天给大家分享的是基于《STM32的智能摇头风扇设计》。

目录

1、系统功能

2、演示视频和实物

3、系统设计框图

4、软件设计流程图

5、原理图

6、主程序

7、总结

1、系统功能

STM32+OLED+DHT11 温湿度传感器 + 蜂鸣器 + 语音识别模块 + ESP866 + 电机驱动模块 + 舵机 + 后备电池
(1)ESP866 WIFI 模块用来连接远程云平台(本次采用机智云),实现手机 APP 对设备的远程控制。
(2)DHT11 温湿度传感器用来检测环境的温度和湿度,并将采集的温湿度数据上传到手机 APP 。
(3)蜂鸣器模块用于声音提示等功能。
(4)语音识别模块用来识别用户的语音指令,如 “摇头开”“档位加” 等,控制风扇摇头和档位等操作。
(5)0.96 寸 OLED 显示屏,用来显示风扇的工作状态信息,以及采集的温湿度数据等。
(6)按键模块(4 个按键):
第一个按键:模式切换按键,
第二个按键:在手动模式下来控制风扇档位(一档、二档、三档、四档)。
第三个按键:在手动模式下来控制风扇摇头的开关;在自动模式下可用于停止操作。
第四个按键:未用到。
(7)电机驱动模块用于驱动风扇电机,实现风扇不同档位(一档、二档、三档、四档)的转速控制,且在自动模式下,风扇会根据温度高低调整转速,温度越高,转速越快(档位越高) 。
(8)舵机用于控制风扇摇头,可实现风扇转到 180 度再回来的摇头动作,且在远程模式和自动模式下都可控制摇头。
(9)后备电池用于保证系统在断电等情况下,保证实时时间的正确。
工作模式:
(1)远程模式:上电默认模式,通过手机 APP 来控制风扇的档位、摇头等操作,同时可实时查看采集的温湿度数据。可通过手机 APP 调节模式(远程、自动、手动、语音)。
(2)手动模式:通过第二个按键来控制风扇档位,第三个按键来控制风扇摇头。
(3)自动模式:风扇根据温度来控制转速,温度越高,转速越快(档位越快),并且在自动模式下也可以控制摇头。也可通过按下第三个按键停止风扇。
(4)语音模式:通过第一个按键进入,使用语音指令 “小智” 唤醒语音助手,然后可通过语音指令控制风扇摇头和档位等操作,如 “摇头开”“档位加” 等。

2、演示视频和实物

基于STM32的智能风扇设计(智能摇头风扇)(语音、WIFI、自动、手动4中模式控制)

 

3、系统设计框图

4、软件设计流程图

5、原理图

 

6、主程序

#include "main.h"
#define PERIOD    400
#define PRESCALER 36000
unsigned int num=0;  有定制和购买的,可以联系VX:lwfw123456789
uint8_t KeyNum;// 存储按键值
u8 t = 0;// 传感器读取时间间隔
uint16_t RTC_Time[] = {0, 0, 0};// RTC时间
uint16_t RTC_Time1[] = {7, 0, 0};// 定时时间---开
uint16_t RTC_Time2[] = {19, 0, 0};// 定时时间---关
u8 S_Mode; // 按键状态标志
u8 S_Shou_1 ;     //手动模式控制设备1
u8 S_Shou_2;     //手动模式控制设备2
u8 S_YuZhi;       //阈值设置里用的,用来切换阈值
u8 S_YUAN_1;
u8 S_YUYIN_1;
u8 DangWei;
// 其他状态标志
u8 qingping = 1;   //清屏标志
u8 S_ShiShi_Time_1;     //设置实时时间用,用来切换时间显示和设置时间
u8 S_ShiShi_Time_2;    //设置实时时间用,用来切换设置的时分秒
u8 S_DingShi_Switch;    //设置定时时间里用到,切换时分秒
u8 angle = 90;
u8 i, S_YaoTou;
bool isIncreasing = true;
// 定义传感器数据和阈值结构体变量
SensorDataAndThreshold sensorData;
/***********************************************************************************************
同学好!我们是5132单片机设计,定制单片机设计,实物和说明书均可定制,欢迎大家咨询。VX:lwfw123456789
其他项目演示视频和说明请大家移步哔哩哔哩、CSDN和公众号等。
哔哩哔哩:5132单片机设计
CSDN:    5132单片机设计
公众号:  5132单片机设计
抖音  :  5132单片机设计
小红书:  5132单片机设计
咸鱼:    5132单片机设计
************************************************************************************************/

// 初始化相关硬件和机智云
void System_Init()
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    uart_init(9600);       // 串口初始化为9600
    delay_init();      // 延时函数初始化
    DHT11_Init();
    Buzzer_Init();             // 外设初始化
    OLED_Init();
    Key_Init();
		Serial2_Init();   //串口2初始化(语音识别模块)
    MyRTC_Init();
    Motor_Init();
    Servo_Init();
    MY_Gizwits_Init();   // 机智云初始化
		PID_Init();
    // 初始化传感器阈值
    sensorData.TempYu = 35;
    sensorData.HumiYu = 30;
    sensorData.ZhongYu = 100;
    sensorData.YaLiYu = 70;
}

int main(void)
{
    System_Init();

    Servo_SetAngle(angle);
//    delay_ms(1000);
//    Servo_SetAngle(0);
    while (1)
    {
				PID_Calc();   //PID计算
				angle=(((pid.OUT*PERIOD)/pid.pwmcycle)-1);   //计算占空比

        if (S_YaoTou == 1)
        {
            if (isIncreasing)
            {
                angle++;  // 角度递增
                if (angle >= 180)
                {
                    isIncreasing = false;  // 达到180度后开始递减
                }
            }
            else
            {
                angle--;  // 角度递减
                if (angle <= 0)
                {
                    isIncreasing = true;  // 回到0度后开始递增
                }
            }
        }
        Servo_SetAngle(angle);
        userHandle();    // 数据上传
        gizwitsHandle((dataPoint_t *)&currentDataPoint);   // 后台处理,必须放在while里

        ReadSensorData();
        HandleModes();

       
    }
}

// 读取传感器数据
void ReadSensorData()
{
    DHT11_Read_Data(&sensorData.temp, &sensorData.humi);  // 读取温湿度并显示在OLED上
}

// 处理不同模式
void HandleModes()
{
    KeyNum = Key_GetNum();
    if (KeyNum == 1 && DebounceKey(1))
    {
        qingping = 0;
        S_Shou_2 = 2;            //每次初始化手动模式的标志,这样进入手动模式是一样的
        S_Mode = (S_Mode + 1) % 4;
    }
    switch (S_Mode)
    {
    case 0: // 远程模式
        if (qingping == 0)
        {
            OLED_Clear();
            Motor_SetSpeed(0);
            S_Shou_1 = 0;
            qingping = 1;
        }
        TimeRead();
        ChuangGan();
        OLED_ShowChinese(1, 7, 49);
        OLED_ShowChinese(1, 8, 50);
        if (S_YUAN_1 == 0)
        {
            Motor_SetSpeed(0);DangWei=0;//外设操作
        }
        if (S_YUAN_1 == 1)
        {
            Motor_SetSpeed(20);DangWei=1;// 外设操作
        }
        if (S_YUAN_1 == 2)
        {
            Motor_SetSpeed(30);DangWei=2; // 外设操作
        }
        if (S_YUAN_1 == 3)
        {
            Motor_SetSpeed(40);DangWei=3;// 外设操作
        }
        break;
    case 1: // 自动模式
        OLED_ShowChinese(1, 7, 51);
        OLED_ShowChinese(1, 8, 52);
        TimeRead();
        zhidong();
        ChuangGan();
        break;
    case 2: // 手动模式
        OLED_ShowChinese(1, 7, 18);
        OLED_ShowChinese(1, 8, 52);
        ChuangGan();
        shoudong();
        break;
    case 3: // 语音模式
        OLED_ShowChinese(1, 7, 57);
        OLED_ShowChinese(1, 8, 58);
        ChuangGan();
        YuYinMode();
        break;		
    }
}


// 手动模式函数
void shoudong()
{
    if (qingping == 0)
    {
        OLED_Clear();
        Motor_SetSpeed(0);
        S_Shou_1 = 0;
        qingping = 1;
    }
    TimeRead();
    if (KeyNum == 2 && DebounceKey(2))
    {
        S_Shou_1 = (S_Shou_1 + 1) % 4;
    }
    if (S_Shou_1 == 0)
    {
        Motor_SetSpeed(0);DangWei=0;//外设操作
    }
    if (S_Shou_1 == 1)
    {
        Motor_SetSpeed(20);DangWei=1;// 外设操作
    }
    if (S_Shou_1 == 2)
    {
        Motor_SetSpeed(30);DangWei=2; // 外设操作
    }
    if (S_Shou_1 == 3)
    {
        Motor_SetSpeed(40);DangWei=3;// 外设操作
    }
		 if (KeyNum == 3 && DebounceKey(3))
        {
            S_Shou_2 = (S_Shou_2 + 1) % 2;

        }
        if (S_Shou_2 == 1)
        {
            S_YaoTou = 1; // 外设操作
        }
        if (S_Shou_2 == 0)
        {
            S_YaoTou = 0; // 外设操作
        }
}

// 自动模式函数
void zhidong()
{
    if (qingping == 0)
    {
        OLED_Clear();
        Motor_SetSpeed(0);
        S_Shou_1 = 0;
        qingping = 1;
    }
    if (10 < sensorData.temp < 20)
    {
        Motor_SetSpeed(0);
        DangWei=0;
    }
    if (20 < sensorData.temp < 30)
    {
        Motor_SetSpeed(10);
        DangWei=1;
    }
    if (30 < sensorData.temp < 40)
    {
        Motor_SetSpeed(20);
        DangWei=2;
    }
    if (40 < sensorData.temp < 50)
    {
        Motor_SetSpeed(30);
        DangWei=3;
    }
		 if (KeyNum == 3 && DebounceKey(3))
        {
            S_Shou_2 = (S_Shou_2 + 1) % 2;

        }
        if (S_Shou_2 == 1)
        {
            S_YaoTou = 1; // 外设操作
        }
        if (S_Shou_2 == 0)
        {
            S_YaoTou = 0; // 外设操作
        }
}

// 设置阈值函数
void YuZhiSet()
{
    zhidong();
    OLED_ShowChinese(1, 3, 72);
    OLED_ShowChinese(1, 4, 73);
    OLED_ShowChinese(1, 5, 74);
    OLED_ShowChinese(1, 6, 75);
    OLED_ShowString(2, 1, "W:");
    OLED_ShowNum(2, 3, sensorData.TempYu, 2);
    OLED_ShowString(2, 7, "S:");
    OLED_ShowNum(2, 9, sensorData.HumiYu, 2);
    OLED_ShowString(3, 1, "ZhongLiang:");
    OLED_ShowNum(3, 12, sensorData.ZhongYu, 4);

    if (KeyNum == 2 && DebounceKey(2))
    {
        S_YuZhi = (S_YuZhi + 1) % 4;
    }
    switch (S_YuZhi)
    {
    case 0:
        if (KeyNum == 3) sensorData.TempYu++;
        if (KeyNum == 4) sensorData.TempYu--;
        break;
    case 1:
        if (KeyNum == 3) sensorData.HumiYu++;
        if (KeyNum == 4) sensorData.HumiYu--;
        break;
    case 2:
        if (KeyNum == 3) sensorData.ZhongYu += 100;
        if (KeyNum == 4) sensorData.ZhongYu -= 100;
        break;
    }
}


void YuYinMode()
{
    if (Serial2_RxFlag == 1)        //串口接收到数据包的标志位,若是收到数据包,会置1
    {
				if (strcmp(Serial2_RxPacket, "TOU_ON") == 0)
        {
                S_YaoTou=1;
        }
        else if (strcmp(Serial2_RxPacket, "TOU_OFF") == 0)
        {
                S_YaoTou=0;
        }
				if (strcmp(Serial2_RxPacket, "DANG_JIA") == 0)
        {
            S_YUYIN_1 = (S_YUYIN_1 + 1) % 4;              
        }
			     
        Serial2_RxFlag = 0; //将标志位清零,不清零就接收不到下一个数据包了
    }
		 if (S_YUYIN_1 == 0)
        {
            Motor_SetSpeed(0);DangWei=0;//外设操作
        }
        if (S_YUYIN_1 == 1)
        {
            Motor_SetSpeed(20);DangWei=1;// 外设操作
        }
        if (S_YUYIN_1 == 2)
        {
            Motor_SetSpeed(30);DangWei=2; // 外设操作
        }
        if (S_YUYIN_1 == 3)
        {
            Motor_SetSpeed(40);DangWei=3;// 外设操作
        }
}


// 定时模式函数
void DingShiMoShi()
{
    TimeRead();
    // 定时模式判断
    if ((MyRTC_Time[3] == RTC_Time1[0]) && (MyRTC_Time[4] == RTC_Time1[1]) && (MyRTC_Time[5] == RTC_Time1[2]))
    {
        // 外设操作
    }
    if ((MyRTC_Time[3] == RTC_Time2[0]) && (MyRTC_Time[4] == RTC_Time2[1]) && (MyRTC_Time[5] == RTC_Time2[2]))
    {
        // 外设操作
    }
    // 显示定时时间
    OLED_ShowChinese(3, 1, 31);
    OLED_ShowString(3, 3, ":");
    OLED_ShowNum(3, 5, RTC_Time1[0], 2);
    OLED_ShowString(3, 7, ":");
    OLED_ShowNum(3, 8, RTC_Time1[1], 2);
    OLED_ShowString(3, 10, ":");
    OLED_ShowNum(3, 11, RTC_Time1[2], 2);
    OLED_ShowChinese(4, 1, 32);
    OLED_ShowString(4, 3, ":");
    OLED_ShowNum(4, 5, RTC_Time2[0], 2);
    OLED_ShowString(4, 7, ":");
    OLED_ShowNum(4, 8, RTC_Time2[1], 2);
    OLED_ShowString(4, 10, ":");
    OLED_ShowNum(4, 11, RTC_Time2[2], 2);

    // 修改定时时间操作
    if (KeyNum == 2 && DebounceKey(2))
    {
        S_DingShi_Switch = (S_DingShi_Switch + 1) % 6;
    }
    switch (S_DingShi_Switch)
    {
    case 0: // 时
        if (KeyNum == 3) RTC_Time2[0]++;
        if (KeyNum == 4) RTC_Time2[0]--;
        break;
    case 1: // 分
        if (KeyNum == 3) RTC_Time2[1]++;
        if (KeyNum == 4) RTC_Time2[1]--;
        break;
    case 2: // 秒
        if (KeyNum == 3) RTC_Time2[2]++;
        if (KeyNum == 4) RTC_Time2[2]--;
        break;
    case 3: // 时
        if (KeyNum == 3) RTC_Time1[0]++;
        if (KeyNum == 4) RTC_Time1[0]--;
        break;
    case 4: // 分
        if (KeyNum == 3) RTC_Time1[1]++;
        if (KeyNum == 4) RTC_Time1[1]--;
        break;
    case 5: // 秒
        if (KeyNum == 3) RTC_Time1[2]++;
        if (KeyNum == 4) RTC_Time1[2]--;
        break;
    }
}


// 机智云初始化函数
void MY_Gizwits_Init(void)
{
    TIM3_Int_Init(9, 7199); // 1MS系统定时
    usart3_init(9600); // WIFI初始化
    memset((uint8_t *)&currentDataPoint, 0, sizeof(dataPoint_t)); // 设备状态结构体初始化
    gizwitsInit(); // 环形缓冲区初始化
    gizwitsSetMode(2);   // 设置模式
    userInit();
}

// 按键消抖函数
uint8_t DebounceKey(uint8_t key)
{
    delay_ms(20);
    return KeyNum == key;
}


// 设置时间函数
void TimeSet()
{
    if (KeyNum == 2 && DebounceKey(2))
    {
        S_ShiShi_Time_1 = (S_ShiShi_Time_1 + 1) % 3;
    }
    if (S_ShiShi_Time_1 == 0)   // 时间显示模式
    {
        MyRTC_ReadTime();
        OLED_ShowNum(1, 5, MyRTC_Time[3], 2);    // 时
        OLED_ShowString(1, 7, ":");
        OLED_ShowNum(1, 8, MyRTC_Time[4], 2);    // 分
        OLED_ShowString(1, 10, ":");
        OLED_ShowNum(1, 11, MyRTC_Time[5], 2);   // 秒
        RTC_Time[0] = MyRTC_Time[3];
        RTC_Time[1] = MyRTC_Time[4];
        RTC_Time[2] = MyRTC_Time[5];
    }
    else if (S_ShiShi_Time_1 == 1)     // 修改时间
    {
        if (KeyNum == 5 && DebounceKey(5))
        {
            S_ShiShi_Time_2 = (S_ShiShi_Time_2 + 1) % 3;
        }
        switch (S_ShiShi_Time_2)
        {
        case 0: // 修改时
            if (KeyNum == 4)
            {
                RTC_Time[0] = (RTC_Time[0] + 1) % 24; // 加 1 后取模 24,确保在 0 - 23 范围内
            }
            if (KeyNum == 3)
            {
                if (RTC_Time[0] == 0)
                {
                    RTC_Time[0] = 23; // 当为 0 时,减操作变为 23
                }
                else
                {
                    RTC_Time[0]--;
                }
            }
            OLED_ShowNum(1, 5, RTC_Time[0], 2);  // 时
            break;
        case 1: // 修改分
            if (KeyNum == 4)
            {
                RTC_Time[1] = (RTC_Time[1] + 1) % 60; // 加 1 后取模 60,确保在 0 - 59 范围内
            }
            if (KeyNum == 3)
            {
                if (RTC_Time[1] == 0)
                {
                    RTC_Time[1] = 59; // 当为 0 时,减操作变为 59
                }
                else
                {
                    RTC_Time[1]--;
                }
            }
            OLED_ShowNum(1, 8, RTC_Time[1], 2);  // 分
            break;
        case 2: // 修改秒
            if (KeyNum == 4)
            {
                RTC_Time[2] = (RTC_Time[2] + 1) % 60; // 加 1 后取模 60,确保在 0 - 59 范围内
            }
            if (KeyNum == 3)
            {
                if (RTC_Time[2] == 0)
                {
                    RTC_Time[2] = 59; // 当为 0 时,减操作变为 59
                }
                else
                {
                    RTC_Time[2]--;
                }
            }
            OLED_ShowNum(1, 11, RTC_Time[2], 2);  // 秒
            break;
        }
    }
    else if (S_ShiShi_Time_1 == 2)
    {
        MyRTC_Time[3] = RTC_Time[0];
        MyRTC_Time[4] = RTC_Time[1];
        MyRTC_Time[5] = RTC_Time[2];
        MyRTC_SetTime();
        S_ShiShi_Time_1 = 0;
    }
}

// 读取时间函数
void TimeRead()
{
    MyRTC_ReadTime();
    OLED_ShowNum(1, 5, MyRTC_Time[3], 2);    // 时
    OLED_ShowString(1, 7, ":");
    OLED_ShowNum(1, 8, MyRTC_Time[4], 2);    // 分
    OLED_ShowString(1, 10, ":");
    OLED_ShowNum(1, 11, MyRTC_Time[5], 2);   // 秒
}


// 显示传感器信息
void ChuangGan()
{
    OLED_ShowChinese(2, 1, 26);
    OLED_ShowChinese(2, 2, 28);
    OLED_ShowString(2, 5, ":");
    OLED_ShowChinese(2, 5, 27);
    OLED_ShowChinese(2, 6, 28);
    OLED_ShowString(2, 13, ":");
    OLED_ShowNum(2, 6, sensorData.temp, 2);
    OLED_ShowNum(2, 14, sensorData.humi, 2);


    OLED_ShowChinese(3, 1, 78);
    OLED_ShowChinese(3, 2, 79);
    OLED_ShowString(3, 5, ":");
    OLED_ShowNum(3, 6, DangWei, 2);


    OLED_ShowChinese(4, 1, 80);
    OLED_ShowChinese(4, 2, 81);
    OLED_ShowString(4, 5, ":");
    OLED_ShowNum(4, 6, S_YaoTou, 2);
}

7、总结

该系统以 STM32 为核心,集成多种模块。ESP866 连接机智云实现手机 APP 远程控制;DHT11 采集温湿度上传;蜂鸣器提示,语音模块识别指令。OLED 屏显示信息,4 按键切换模式、控制档位与摇头。电机驱动与舵机实现风扇调速、摇头,后备电池保障实时时间,有远程、手动等四种工作模式 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

5132单片机设计

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值