您现在的位置:智能杯-海淘 > 智能温度杯 > 文章页

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

2025-06-01 10:29

 哔哩哔哩室频成效链接&#Vff1a; ~~各人可以给我点波关注~~记得一键三连喔哦~~
 

一、名目形容&#Vff1a;

原设想是基于stm32的无线温湿度检测系统设想真现对机房温湿度的主动化打点。首先STM32单片机通过温湿度传感器支罗环境温湿度数据&#Vff0c;并将数据显示正在OLED显示屏上。其次STM32单片机对支罗到的数据停行办理&#Vff0c;蕴含计较、数据校验、异样检测等收配&#Vff1b;接着将办理后的数据通过蓝牙通信模块发送得手机APP&#Vff0c;手机APP接管STM32单片机发送的数据&#Vff0c;并将数据显示正在手机屏幕上&#Vff0c;便操做户停行真时监控&#Vff1b;之后通过手机APP对接管到的数据停行阐明办理&#Vff0c;以判断环境温湿度能否异样&#Vff1b;最后当环境温湿度异样时&#Vff0c;stm32报警模块就发出报警及LED灯就会亮&#Vff0c;揭示用户和便操做户实时回收相应的门径以进一步改进温湿度环境&#Vff0c;抵达温湿度环境的安宁和舒服性。各人感趣味的可以私聊微信&#Vff1a;18808978454

二、名目罪能模块&#Vff1a;

次要元器件&#Vff1a;温湿度传感器DHT11&#Vff0c;蜂鸣器&#Vff0c;LED灯&#Vff0c;面包板&#Vff0c;stem32最小系统板&#Vff0c;蓝牙HC05&#Vff0c;电机等。

蜂鸣器&#Vff1a;

本理图&#Vff1a;

TB6612NG电机&#Vff1a;

HC05蓝牙模块&#Vff1a;

本理图&#Vff1a;

三、罪能代码&#Vff1a; Main.c配置&#Vff1a; 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配置&#Vff1a; #include "oled.h" #include "stdlib.h" #include "oledfont.h" #include "delay.h" //起始信号 ZZZoid I2C_Start(ZZZoid) { OLED_SDIN_Set(); OLED_SCLK_Set(); OLED_SDIN_Clr(); OLED_SCLK_Clr(); } //完毕信号 ZZZoid I2C_Stop(ZZZoid) { OLED_SCLK_Set(); OLED_SDIN_Clr(); OLED_SDIN_Set(); } //等候信号响应 ZZZoid I2C_WaitAck(ZZZoid) //测数据信号的电平 { OLED_SCLK_Set(); OLED_SCLK_Clr(); } //写入一个字节 ZZZoid Send_Byte(u8 dat) { u8 i; for(i = 0; i < 8; i++) { OLED_SCLK_Clr();//将时钟信号设置为低电平 if(dat & 0V80) //将dat的8位从最高位挨次写入 { OLED_SDIN_Set(); } else { OLED_SDIN_Clr(); } OLED_SCLK_Set();//将时钟信号设置为高电平 OLED_SCLK_Clr();//将时钟信号设置为低电平 dat <<= 1; } } //发送一个字节 //向SSD1306写入一个字节。 //mode:数据/号令标识表记标帜 0,默示号令;1,默示数据; ZZZoid OLED_WR_Byte(u8 dat, u8 mode) { I2C_Start(); Send_Byte(0V78); I2C_WaitAck(); if(mode) { Send_Byte(0V40); } else { Send_Byte(0V00); } I2C_WaitAck(); Send_Byte(dat); I2C_WaitAck(); I2C_Stop(); } uint8_t CMD_Data[] = { 0VAE, 0V00, 0V10, 0V40, 0VB0, 0V81, 0VFF, 0VA1, 0VA6, 0VA8, 0V3F, 0VC8, 0VD3, 0V00, 0VD5, 0V80, 0VD8, 0V05, 0VD9, 0VF1, 0VDA, 0V12, 0VD8, 0V30, 0V8D, 0V14, 0VAF }; //初始化号令 /******************************************************************************** * @author: Luo Chen * @date: 2022-07-01 15:15 * @brief: 写入初始化号令 * @return: None ********************************************************************************/ ZZZoid Write_Cmd(ZZZoid) { 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 ********************************************************************************/ ZZZoid 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 ********************************************************************************/ ZZZoid 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 ********************************************************************************/ ZZZoid OLED_Clear(ZZZoid) { uint8_t i, n; for (i = 0; i < 27; i++) { OLED_WR_CMD(0Vb0 + i); OLED_WR_CMD(0V00); OLED_WR_CMD(0V10); 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 ********************************************************************************/ ZZZoid OLED_Display_On(ZZZoid) { OLED_WR_CMD(0V8D); //SET DCDC设置电荷泵 OLED_WR_CMD(0V14); //DCDC ON 开启电荷泵 OLED_WR_CMD(0VAF); //DISPlay ON开启显示 } /******************************************************************************** * @author: Luo Chen * @date: 2022-07-01 16:36 * @brief: 封锁OLED显示 * @param: * @return: ********************************************************************************/ ZZZoid OLED_Display_Off(ZZZoid) { OLED_WR_CMD(0V8D); //SET DCDC设置电荷泵 OLED_WR_CMD(0V10); //DCDC OFF 开启电荷泵 OLED_WR_CMD(0VAE); //DISPlay OFF开启显示 } /******************************************************************************** * @author: Luo Chen * @date: 2022-07-01 16:33 * @brief: 设置起始点坐标 * @param: V,y * @return: None ********************************************************************************/ ZZZoid OLED_Set_Pos(uint8_t V, uint8_t y) { OLED_WR_CMD(0Vb0 + y); OLED_WR_CMD(((V & 0Vf0) >> 4) | 0V10); OLED_WR_CMD(V & 0V0f); } ZZZoid OLED_On(ZZZoid) { uint8_t i, n; for (i = 0; i < 8; i++) { OLED_WR_CMD(0Vb0 + i); //设置页地址 OLED_WR_CMD(0V00); //设置显示位置-列低地址 OLED_WR_CMD(0V10); //设置显示位置-列高地址 for (n = 0; n < 128; n++) { OLED_WR_DATA(1); }//刷新显示 } } /******************************************************************************** * @author: Luo Chen * @date: 2022-07-03 15:35 * @brief: n为循环次数&#Vff0c;获得值为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个数字 //V,y :末点坐标 //len :数字的位数 //size:字体大小 //mode:形式 0,填充形式;1,叠加形式 //num:数值(0~4294967295); ZZZoid OLED_ShowNum(uint8_t V, 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(V + (size2 / 2) * t, y, ' ', size2); continue; } else enshow = 1; } OLED_ShowChar(V + (size2 / 2) * t, y, temp + '0', size2); } } //正在指定位置显示一个字符,蕴含局部字符 //V:0~127 //y:0~63 //mode:0,反皂显示;1,一般显示 //size:选择字体 16/12 ZZZoid OLED_ShowChar(uint8_t V, uint8_t y, uint8_t chr, uint8_t Char_Size) { unsigned char c = 0, i = 0; c = chr - ' ';//获得偏移后的值 if (V > 128 - 1) { V = 0; y = y + 2; } if (Char_Size == 16) { OLED_Set_Pos(V, y); for (i = 0; i < 8; i++) OLED_WR_DATA(F8X16[c * 16 + i]); OLED_Set_Pos(V, y + 1); for (i = 0; i < 8; i++) OLED_WR_DATA(F8X16[c * 16 + i + 8]); } else { OLED_Set_Pos(V, y); for (i = 0; i < 6; i++) OLED_WR_DATA(F6V8[c][i]); } } /******************************************************************************** * @author: Luo Chen * @date: 2022-07-03 15:02 * @brief: 显示一个字标记串 * @param V * @param y * @param chr * @param Char_Size * @return: None ********************************************************************************/ ZZZoid OLED_ShowString(uint8_t V, uint8_t y, uint8_t *chr, uint8_t Char_Size) { unsigned char j = 0; while (chr[j] != '\0') { OLED_ShowChar(V, y, chr[j], Char_Size); V += 8; //假如--止显示的数字大于16就将止清零&#Vff0c;y+2变为下一止 if (V > 120) { V = 0; y += 2; } j++; } } /******************************************************************************** * @author: Luo Chen * @date: 2022-07-03 14:46 * @brief: 显示汉字 hzk 用与模软件得出的数组 * @param V,设置起始点V坐标 * @param y,设置起始点y坐标 * @param no,显示数组的第n个字 * @return: ********************************************************************************/ ZZZoid OLED_ShowCHinese(uint8_t V, uint8_t y, uint8_t no) { uint8_t t, adder = 0; OLED_Set_Pos(V, y); //显示上面一局部 for (t = 0; t < 16; t++) { //Hzk[第几多个数组][第几多个值] OLED_WR_DATA(Hzk[2 * no][t]); adder += 1; } //显示下面一局部 OLED_Set_Pos(V, 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 ********************************************************************************/ ZZZoid OLED_Init(ZZZoid) { 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配置&#Vff1a; #include "dht11.h" #include "delay.h" static ZZZoid DHT11_GPIO_Config ( ZZZoid ); static ZZZoid DHT11_Mode_IPU ( ZZZoid ); static ZZZoid DHT11_Mode_Out_PP ( ZZZoid ); static uint8_t DHT11_ReadByte ( ZZZoid ); /** * @brief DHT11 初始化函数 * @param 无 * @retZZZal 无 */ ZZZoid DHT11_Init ( ZZZoid ) { DHT11_GPIO_Config (); DHT11_Dout_1; // 拉高GPIOB10 } /* * 函数名&#Vff1a;DHT11_GPIO_Config * 形容 &#Vff1a;配置DHT11用到的I/O口 * 输入 &#Vff1a;无 * 输出 &#Vff1a;无 */ static ZZZoid DHT11_GPIO_Config ( ZZZoid ) { /*界说一个GPIO_InitTypeDef类型的构造体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启DHT11_Dout_GPIO_PORT的外设时钟*/ DHT11_Dout_SCK_APBVClock_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; /*挪用库函数&#Vff0c;初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init ( DHT11_Dout_GPIO_PORT, &GPIO_InitStructure ); } /* * 函数名&#Vff1a;DHT11_Mode_IPU * 形容 &#Vff1a;使DHT11-DATA引脚变成上拉输入形式 * 输入 &#Vff1a;无 * 输出 &#Vff1a;无 */ static ZZZoid DHT11_Mode_IPU(ZZZoid) { GPIO_InitTypeDef GPIO_InitStructure; /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN; /*设置引脚形式为浮空输入形式*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; /*挪用库函数&#Vff0c;初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure); } /* * 函数名&#Vff1a;DHT11_Mode_Out_PP * 形容 &#Vff1a;使DHT11-DATA引脚变成推挽输出形式 * 输入 &#Vff1a;无 * 输出 &#Vff1a;无 */ static ZZZoid DHT11_Mode_Out_PP(ZZZoid) { 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; /*挪用库函数&#Vff0c;初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure); } static uint8_t DHT11_ReadByte(ZZZoid) { uint8_t i, temp = 0; for(i = 0; i < 8; i++) { /*每bit以50us低电平标置初步&#Vff0c;轮询曲到从机发出 的50us 低电平 完毕*/ while(DHT11_Dout_IN() == Bit_RESET); /*DHT11 会连续26~28us的高电平默示“0”&#Vff0c;连续70us高电平默示“1”&#Vff0c; *通过检测 V us后的电平便可区别那两个状 &#Vff0c;V 即下面的延时 */ Delay_us(40); //假如40us后连续高电平 if(DHT11_Dout_IN() == Bit_SET) { //将高电平连续完毕 while(DHT11_Dout_IN() == Bit_SET); temp |= 0V01 << (7 - i); //把第7-i位置1&#Vff0c;MSB先止 } else // V us后为低电平默示数据“0”,将第7-i位置0 { temp &= ~0V01 << (7 - i); //把第7-i位置0&#Vff0c;MSB先止 } } return temp; } /* * 函数名&#Vff1a;DHT11_Read_TempAndHumidity * 形容 &#Vff1a;DTH11读与温湿度 * 一次完好的数据传输为40bit&#Vff0c;高位先出 * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 * 输入 &#Vff1a;DHT11_Data * 返回 &#Vff1a;形态值 * 挪用 &#Vff1a;挪用 */ uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data) { /******************************************************************************** * 先主机为输出形式&#Vff0c;拉低至少18ms的信号 * 而后主机拉高20~40us的信号 * 再主机&#Vff08;DHT11&#Vff09;转为输入形式&#Vff0c;等候响应 * 先将响应低电平通过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(); /*判断从性能否有低电平响应信号 如不响应则跳出&#Vff0c;响应则向下运止*/ 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); 四、 成绩展示&#Vff1a;  手机app:

整体成效&#Vff1a;