毕业设计——STM32+DHT11传感器+蓝牙+手机APP(温湿度采集系统)

 哔哩哔哩视频效果链接:http://【毕业设计——STM32温湿度采集系统——手机APP】 https://www.bilibili.com/video/BV12i421Z7is/?share_source=copy_web&vd_source=d711fa8bef565aeec8a4d279df8b85aa ~~大家可以给我点波关注~~记得一键三连喔哦~~
 

一、项目描述:

本设计是基于stm32的无线温湿度检测系统设计实现对机房温湿度的自动化管理。首先STM32单片机通过温湿度传感器采集环境温湿度数据,并将数据显示在OLED显示屏上。其次STM32单片机对采集到的数据进行处理,包括计算、数据校验、异常检测等操作;接着将处理后的数据通过蓝牙通信模块发送到手机APP,手机APP接收STM32单片机发送的数据,并将数据显示在手机屏幕上,方便用户进行实时监控;之后通过手机APP对接收到的数据进行分析处理,以判断环境温湿度是否异常;最后当环境温湿度异常时,stm32报警模块就发出报警及LED灯就会亮,提醒用户和方便用户及时采取相应的措施以进一步改善温湿度环境,达到温湿度环境的安全和舒适性。大家感兴趣的可以私聊微信:18808978454

二、项目功能模块:

主要元器件:温湿度传感器DHT11,蜂鸣器,LED灯,面包板,stem32最小系统板,蓝牙HC05,电机等。

蜂鸣器:

原理图:

TB6612NG电机:

HC05蓝牙模块:

原理图:

三、功能代码:

Main.c配置:

GPIO_InitTypeDef GPIO_InitStructure;	
    uart1_init(115200);
    DHT11_Init();
    OLED_Init();
    OLED_Clear();
		Buzzer_Init();	
		Serial_Init();
		Motor_Init();
    OLED_ShowString(30,  0, "DHT11 ", 16);   //输出字符
    OLED_ShowCHinese(0,  4, 0);	//
    OLED_ShowCHinese(16, 4, 1);	//
		OLED_ShowString (32, 4, ":", 16);							//显示温度
		OLED_ShowString(48, 4, "--", 16);
		OLED_ShowString (64, 4, ".", 16);
    OLED_ShowCHinese(87, 4, 4);	//
		OLED_ShowString (93, 4, "C", 16);
		
    OLED_ShowCHinese(0,  6, 2);	//
    OLED_ShowCHinese(16, 6, 3);	//
		OLED_ShowString (32, 6, ":", 16);							//显示湿度
		OLED_ShowString(48, 6, "--", 16);
		OLED_ShowString(64, 6, ".", 16);
    OLED_ShowString(87, 6, "%", 16);
    SysTick_Init();
   
    
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
														
	
	
	
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_Out_PP;		//
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		
	
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//
														
    while(1)
    {
        //先去中断判断read_dht11_finish
        if( read_dht11_finish ) // read_dht11_finish == 1 or read_dht11_finish == -1 
        {
					sprintf ( cStr, "\r\n\r\n读取DHT11成功!\r\n 温度:%d.%d℃,湿度:%d.%d%%\r\n",
                     DHT11_Data.temp_int, DHT11_Data.temp_deci ,DHT11_Data.humi_int, DHT11_Data.humi_deci );
            printf ( "%s", cStr );   
                                          //打印读取 DHT11 温湿度信息
						humi_int=DHT11_Data.humi_int;
						humi_deci=DHT11_Data.humi_deci;
						temp_int=DHT11_Data.temp_int;
						temp_deci=DHT11_Data.temp_deci;
				
						OLED_ShowNum   (48, 4, temp_int, 2, 16);							//显示温度
            OLED_ShowNum   (66, 4, temp_deci, 2, 16);
					
            OLED_ShowNum   (48, 6, humi_int, 2, 16);
						OLED_ShowNum   (66, 6, humi_deci, 2, 16);							//显示湿度
					  
           read_dht11_finish = 0;       // 清零标志位
        }
    }    		

OLED.c配置:

#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"
#include "delay.h"

//起始信号
void I2C_Start(void)
{
    OLED_SDIN_Set();
    OLED_SCLK_Set();
    OLED_SDIN_Clr();
    OLED_SCLK_Clr();
}

//结束信号
void I2C_Stop(void)
{
    OLED_SCLK_Set();
    OLED_SDIN_Clr();
    OLED_SDIN_Set();
}

//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
    OLED_SCLK_Set();
    OLED_SCLK_Clr();
}

//写入一个字节
void Send_Byte(u8 dat)
{
    u8 i;
    for(i = 0; i < 8; i++)
    {
        OLED_SCLK_Clr();//将时钟信号设置为低电平
        if(dat & 0x80) //将dat的8位从最高位依次写入
        {
            OLED_SDIN_Set();
        }
        else
        {
            OLED_SDIN_Clr();
        }
        OLED_SCLK_Set();//将时钟信号设置为高电平
        OLED_SCLK_Clr();//将时钟信号设置为低电平
        dat <<= 1;
    }
}

//发送一个字节
//向SSD1306写入一个字节。
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat, u8 mode)
{
    I2C_Start();
    Send_Byte(0x78);
    I2C_WaitAck();
    if(mode)
    {
        Send_Byte(0x40);
    }
    else
    {
        Send_Byte(0x00);
    }
    I2C_WaitAck();
    Send_Byte(dat);
    I2C_WaitAck();
    I2C_Stop();
}


uint8_t CMD_Data[] =
{
    0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F,

    0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1, 0xDA, 0x12,

    0xD8, 0x30, 0x8D, 0x14, 0xAF
};      //初始化命令

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:15
* @brief: 写入初始化命令
* @return: None
********************************************************************************/
void Write_Cmd(void)
{
    int i;
    for (i = 0; i < 27; i++)
    {
        OLED_WR_Byte(CMD_Data[i], OLED_CMD); //SET DCDC命令
    }
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:19
* @brief: 向设备写控制命令
* @param: cmd
* @return: None
********************************************************************************/
void OLED_WR_CMD(uint8_t cmd)
{
    OLED_WR_Byte(cmd, OLED_CMD); //SET DCDC命令
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:35
* @brief: 写数据
* @param: data
* @return: None
********************************************************************************/
void OLED_WR_DATA(uint8_t data)
{
    OLED_WR_Byte(data, OLED_DATA); //SET DCDC命令
}


/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:32
* @brief: 清屏
* @param: None
* @return: None
********************************************************************************/
void OLED_Clear(void)
{
    uint8_t i, n;
    for (i = 0; i < 27; i++)
    {
        OLED_WR_CMD(0xb0 + i);
        OLED_WR_CMD(0x00);
        OLED_WR_CMD(0x10);
        for (n = 0; n < 128; n++)
        {
            OLED_WR_DATA(0);
        }
    }
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:59
* @brief: 开启OLed显示
* @param: None
* @return: None
********************************************************************************/
void OLED_Display_On(void)
{
    OLED_WR_CMD(0x8D);   //SET   DCDC设置电荷泵
    OLED_WR_CMD(0x14);   //DCDC  ON 开启电荷泵
    OLED_WR_CMD(0xAF);   //DISPlay ON开启显示
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 16:36
* @brief: 关闭OLED显示
* @param:
* @return:
********************************************************************************/
void OLED_Display_Off(void)
{
    OLED_WR_CMD(0x8D);   //SET   DCDC设置电荷泵
    OLED_WR_CMD(0x10);   //DCDC  OFF 开启电荷泵
    OLED_WR_CMD(0xAE);   //DISPlay OFF开启显示
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 16:33
* @brief: 设置起始点坐标
* @param: x,y
* @return: None
********************************************************************************/
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
    OLED_WR_CMD(0xb0 + y);
    OLED_WR_CMD(((x & 0xf0) >> 4) | 0x10);
    OLED_WR_CMD(x & 0x0f);
}

void OLED_On(void)
{
    uint8_t i, n;
    for (i = 0; i < 8; i++)
    {
        OLED_WR_CMD(0xb0 + i);      //设置页地址
        OLED_WR_CMD(0x00);        //设置显示位置-列低地址
        OLED_WR_CMD(0x10);        //设置显示位置-列高地址
        for (n = 0; n < 128; n++)
        {
            OLED_WR_DATA(1);
        }//刷新显示
    }
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-03 15:35
* @brief: n为循环次数,得到值为m的n+1次方
* @param m
* @param n
* @return result
********************************************************************************/
unsigned int oled_pow(uint8_t m, uint8_t n)
{
    unsigned int result = 1;
    while (n--)result *= m;
    return result;
}

//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式	0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(uint8_t x, uint8_t y, unsigned int num, uint8_t len, uint8_t size2)
{
    uint8_t t, temp;
    uint8_t enshow = 0;
    for (t = 0; t < len; t++)
    {
        temp = (num / oled_pow(10, len - t - 1)) % 10;
        if (enshow == 0 && t < (len - 1))
        {
            if (temp == 0)
            {
                OLED_ShowChar(x + (size2 / 2) * t, y, ' ', size2);
                continue;
            }
            else enshow = 1;
        }
        OLED_ShowChar(x + (size2 / 2) * t, y, temp + '0', size2);
    }
}

//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size)
{
    unsigned char c = 0, i = 0;
    c = chr - ' ';//得到偏移后的值
    if (x > 128 - 1)
    {
        x = 0;
        y = y + 2;
    }
    if (Char_Size == 16)
    {
        OLED_Set_Pos(x, y);
        for (i = 0; i < 8; i++)
            OLED_WR_DATA(F8X16[c * 16 + i]);
        OLED_Set_Pos(x, y + 1);
        for (i = 0; i < 8; i++)
            OLED_WR_DATA(F8X16[c * 16 + i + 8]);
    }
    else
    {
        OLED_Set_Pos(x, y);
        for (i = 0; i < 6; i++)
            OLED_WR_DATA(F6x8[c][i]);

    }
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-03 15:02
* @brief: 显示一个字符号串
* @param x
* @param y
* @param chr
* @param Char_Size
* @return: None
********************************************************************************/
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t Char_Size)
{
    unsigned char j = 0;
    while (chr[j] != '\0')
    {
        OLED_ShowChar(x, y, chr[j], Char_Size);
        x += 8;
        //如果--行显示的数字大于16就将行清零,y+2变成下一行
        if (x > 120)
        {
            x = 0;
            y += 2;
        }
        j++;
    }
}

/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-03 14:46
* @brief: 显示汉字     hzk 用取模软件得出的数组
* @param x,设置起始点x坐标
* @param y,设置起始点y坐标
* @param no,显示数组的第n个字
* @return:
********************************************************************************/
void OLED_ShowCHinese(uint8_t x, uint8_t y, uint8_t no)
{
    uint8_t t, adder = 0;
    OLED_Set_Pos(x, y);
    //显示上面一部分
    for (t = 0; t < 16; t++)
    {
        //Hzk[第几个数组][第几个值]
        OLED_WR_DATA(Hzk[2 * no][t]);
        adder += 1;
    }
    //显示下面一部分
    OLED_Set_Pos(x, y + 1);
    for (t = 0; t < 16; t++)
    {
        OLED_WR_DATA(Hzk[2 * no + 1][t]);
        adder += 1;
    }
}


/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:38
* @brief: 初始化oled屏幕
* @param: None
* @return: None
********************************************************************************/
void OLED_Init(void)
{

    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(	OLED_CLK, ENABLE );

    GPIO_InitStructure.GPIO_Pin = OLED_SCLK_Pin | OLED_SDIN_Pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(OLED_PORT, &GPIO_InitStructure);

    Delay_ms(100);
    Write_Cmd();
    OLED_Clear();
}

DHT11.c配置:

#include "dht11.h"
#include "delay.h"

static void                           DHT11_GPIO_Config                       ( void );
static void                           DHT11_Mode_IPU                          ( void );
static void                           DHT11_Mode_Out_PP                       ( void );
static uint8_t                        DHT11_ReadByte                          ( void );

/**
 * @brief  DHT11 初始化函数
 * @param  无
 * @retval 无
 */
void DHT11_Init ( void )
{
    DHT11_GPIO_Config ();

    DHT11_Dout_1;               // 拉高GPIOB10
}


/*
 * 函数名:DHT11_GPIO_Config
 * 描述  :配置DHT11用到的I/O口
 * 输入  :无
 * 输出  :无
 */
static void DHT11_GPIO_Config ( void )
{
    /*定义一个GPIO_InitTypeDef类型的结构体*/
    GPIO_InitTypeDef GPIO_InitStructure;


    /*开启DHT11_Dout_GPIO_PORT的外设时钟*/
    DHT11_Dout_SCK_APBxClock_FUN (  DHT11_Dout_GPIO_CLK, ENABLE );

    /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
    GPIO_InitStructure.GPIO_Pin =  DHT11_Dout_GPIO_PIN;

    /*设置引脚模式为通用推挽输出*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    /*设置引脚速率为50MHz */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
    GPIO_Init ( DHT11_Dout_GPIO_PORT, &GPIO_InitStructure );

}


/*
 * 函数名:DHT11_Mode_IPU
 * 描述  :使DHT11-DATA引脚变为上拉输入模式
 * 输入  :无
 * 输出  :无
 */
static void DHT11_Mode_IPU(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
    GPIO_InitStructure.GPIO_Pin =  DHT11_Dout_GPIO_PIN;

    /*设置引脚模式为浮空输入模式*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;

    /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
    GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);

}


/*
 * 函数名:DHT11_Mode_Out_PP
 * 描述  :使DHT11-DATA引脚变为推挽输出模式
 * 输入  :无
 * 输出  :无
 */
static void DHT11_Mode_Out_PP(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
    GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;

    /*设置引脚模式为通用推挽输出*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    /*设置引脚速率为50MHz */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
    GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);

}



static uint8_t DHT11_ReadByte(void)
{

    uint8_t i, temp = 0;
    for(i = 0; i < 8; i++)
    {
        /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/
        while(DHT11_Dout_IN() == Bit_RESET);

        /*DHT11 会持续26~28us的高电平表示“0”,持续70us高电平表示“1”,
         *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时
         */
        Delay_us(40);
        //如果40us后持续高电平
        if(DHT11_Dout_IN() == Bit_SET)
        {
            //将高电平持续结束
            while(DHT11_Dout_IN() == Bit_SET);
            temp |= 0x01 << (7 - i); //把第7-i位置1,MSB先行
        }
        else     // x us后为低电平表示数据“0”,将第7-i位置0
        {
            temp &= ~0x01 << (7 - i); //把第7-i位置0,MSB先行
        }
    }
    return temp;
}

/*

* 函数名:DHT11_Read_TempAndHumidity
* 描述  :DTH11读取温湿度
* 一次完整的数据传输为40bit,高位先出
* 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和
* 输入  :DHT11_Data
* 返回  :状态值
* 调用  :调用
*/
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{
    /********************************************************************************
    *   先主机为输出模式,拉低至少18ms的信号
    *   然后主机拉高20~40us的信号
    *   再主机(DHT11)转为输入模式,等待响应
    *   先将响应低电平通过while过滤
    *   再将响应高电平通过while过滤
    *   最后通过DHT11_ReadByte函数读取数据
    ********************************************************************************/
    //输出模式
    DHT11_Mode_Out_PP();

    //主机拉低
    DHT11_Dout_0;

    //延时18MS
    Delay_ms(20);

    //拉高
    DHT11_Dout_1;

    //延时30us
    Delay_us(30);

    /*主机设为输入 判断从机响应信号*/
    DHT11_Mode_IPU();
    /*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
    if(DHT11_Dout_IN() == Bit_RESET)
    {

        /*轮询直到从机发出 的80us 低电平 响应信号结束*/
        while(DHT11_Dout_IN() == Bit_RESET);
        /*轮询直到从机发出的 80us 高电平 标置信号结束*/
        while(DHT11_Dout_IN() == Bit_SET);
        //8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和
        DHT11_Data->humi_int = DHT11_ReadByte();
        DHT11_Data->humi_deci = DHT11_ReadByte();
        DHT11_Data->temp_int = DHT11_ReadByte();
        DHT11_Data->temp_deci = DHT11_ReadByte();

        DHT11_Data->check_sum = DHT11_ReadByte();

        //读取结束将引脚改为输出模式
        DHT11_Mode_Out_PP();

        DHT11_Dout_1;

        if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int + DHT11_Data->temp_deci)
            return SUCCESS;
        else
            return ERROR;
    }
    else
        return ERROR;
}






电机驱动+蜂鸣器+LED灯闪烁

if(temp_int >threshold){
					 
				   Buzzer_ON();	
		       GPIO_ResetBits(GPIOA, GPIO_Pin_0);					
           Delay_ms(500);										
           GPIO_SetBits(GPIOA, GPIO_Pin_0);					
           Delay_ms(500);
				   Motor_SetSpeed(30);
				}
				else
				{
					Buzzer_OFF();	
          GPIO_SetBits(GPIOA, GPIO_Pin_0);
					Motor_SetSpeed(0);

四、 成果展示:

 手机app:

整体效果:

  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值