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

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

2025-06-01 10:29

<p> 哔哩哔哩室频成效链接&#Vff1a; ~~各人可以给我点波关注~~记得一键三连喔哦~~<br />  </p> <strong>一、名目形容&#Vff1a;</strong> <p>原设想是基于stm32的无线温湿度检测系统设想真现对机房温湿度的主动化打点。首先STM32单片机通过温湿度传感器支罗环境温湿度数据&#Vff0c;并将数据显示正在OLED显示屏上。其次STM32单片机对支罗到的数据停行办理&#Vff0c;蕴含计较、数据校验、异样检测等收配&#Vff1b;接着将办理后的数据通过蓝牙通信模块发送得手机APP&#Vff0c;手机APP接管STM32单片机发送的数据&#Vff0c;并将数据显示正在手机屏幕上&#Vff0c;便操做户停行真时监控&#Vff1b;之后通过手机APP对接管到的数据停行阐明办理&#Vff0c;以判断环境温湿度能否异样&#Vff1b;最后当环境温湿度异样时&#Vff0c;stm32报警模块就发出报警及LED灯就会亮&#Vff0c;揭示用户和便操做户实时回收相应的门径以进一步改进温湿度环境&#Vff0c;抵达温湿度环境的安宁和舒服性。各人感趣味的可以私聊微信&#Vff1a;18808978454</p> 二、名目罪能模块&#Vff1a; <p>次要元器件&#Vff1a;温湿度传感器DHT11&#Vff0c;蜂鸣器&#Vff0c;LED灯&#Vff0c;面包板&#Vff0c;stem32最小系统板&#Vff0c;蓝牙HC05&#Vff0c;电机等。</p> 蜂鸣器&#Vff1a; <p></p> <p>本理图&#Vff1a;</p> <p></p> TB6612NG电机&#Vff1a; <p></p> <p></p> HC05蓝牙模块&#Vff1a; <p></p> <p>本理图&#Vff1a;</p> <p></p> 三、罪能代码&#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, &#34;DHT11 &#34;, 16); //输出字符 OLED_ShowCHinese(0, 4, 0); // OLED_ShowCHinese(16, 4, 1); // OLED_ShowString (32, 4, &#34;:&#34;, 16); //显示温度 OLED_ShowString(48, 4, &#34;--&#34;, 16); OLED_ShowString (64, 4, &#34;.&#34;, 16); OLED_ShowCHinese(87, 4, 4); // OLED_ShowString (93, 4, &#34;C&#34;, 16); OLED_ShowCHinese(0, 6, 2); // OLED_ShowCHinese(16, 6, 3); // OLED_ShowString (32, 6, &#34;:&#34;, 16); //显示湿度 OLED_ShowString(48, 6, &#34;--&#34;, 16); OLED_ShowString(64, 6, &#34;.&#34;, 16); OLED_ShowString(87, 6, &#34;%&#34;, 16); SysTick_Init(); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Mode &#61; GPIO_Mode_Out_PP; // GPIO_InitStructure.GPIO_Pin &#61; GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed &#61; GPIO_Speed_50MHz; GPIO_Init(GPIOA, &amp;GPIO_InitStructure); // while(1) &#123; //先去中断判断read_dht11_finish if( read_dht11_finish ) // read_dht11_finish &#61;&#61; 1 or read_dht11_finish &#61;&#61; -1 &#123; sprintf ( cStr, &#34;\r\n\r\n读与DHT11乐成!\r\n 温度:%d.%d℃,湿度:%d.%d%%\r\n&#34;, DHT11_Data.temp_int, DHT11_Data.temp_deci ,DHT11_Data.humi_int, DHT11_Data.humi_deci ); printf ( &#34;%s&#34;, cStr ); //打印读与 DHT11 温湿度信息 humi_int&#61;DHT11_Data.humi_int; humi_deci&#61;DHT11_Data.humi_deci; temp_int&#61;DHT11_Data.temp_int; temp_deci&#61;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 &#61; 0; // 清零标识表记标帜位 &#125; &#125; OLED.c配置&#Vff1a; #include &#34;oled.h&#34; #include &#34;stdlib.h&#34; #include &#34;oledfont.h&#34; #include &#34;delay.h&#34; //起始信号 ZZZoid I2C_Start(ZZZoid) &#123; OLED_SDIN_Set(); OLED_SCLK_Set(); OLED_SDIN_Clr(); OLED_SCLK_Clr(); &#125; //完毕信号 ZZZoid I2C_Stop(ZZZoid) &#123; OLED_SCLK_Set(); OLED_SDIN_Clr(); OLED_SDIN_Set(); &#125; //等候信号响应 ZZZoid I2C_WaitAck(ZZZoid) //测数据信号的电平 &#123; OLED_SCLK_Set(); OLED_SCLK_Clr(); &#125; //写入一个字节 ZZZoid Send_Byte(u8 dat) &#123; u8 i; for(i &#61; 0; i &lt; 8; i&#43;&#43;) &#123; OLED_SCLK_Clr();//将时钟信号设置为低电平 if(dat &amp; 0V80) //将dat的8位从最高位挨次写入 &#123; OLED_SDIN_Set(); &#125; else &#123; OLED_SDIN_Clr(); &#125; OLED_SCLK_Set();//将时钟信号设置为高电平 OLED_SCLK_Clr();//将时钟信号设置为低电平 dat &lt;&lt;&#61; 1; &#125; &#125; //发送一个字节 //向SSD1306写入一个字节。 //mode:数据/号令标识表记标帜 0,默示号令;1,默示数据; ZZZoid OLED_WR_Byte(u8 dat, u8 mode) &#123; I2C_Start(); Send_Byte(0V78); I2C_WaitAck(); if(mode) &#123; Send_Byte(0V40); &#125; else &#123; Send_Byte(0V00); &#125; I2C_WaitAck(); Send_Byte(dat); I2C_WaitAck(); I2C_Stop(); &#125; uint8_t CMD_Data[] &#61; &#123; 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 &#125;; //初始化号令 /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 15:15 * &#64;brief: 写入初始化号令 * &#64;return: None ********************************************************************************/ ZZZoid Write_Cmd(ZZZoid) &#123; int i; for (i &#61; 0; i &lt; 27; i&#43;&#43;) &#123; OLED_WR_Byte(CMD_Data[i], OLED_CMD); //SET DCDC号令 &#125; &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 15:19 * &#64;brief: 向方法写控制号令 * &#64;param: cmd * &#64;return: None ********************************************************************************/ ZZZoid OLED_WR_CMD(uint8_t cmd) &#123; OLED_WR_Byte(cmd, OLED_CMD); //SET DCDC号令 &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 15:35 * &#64;brief: 写数据 * &#64;param: data * &#64;return: None ********************************************************************************/ ZZZoid OLED_WR_DATA(uint8_t data) &#123; OLED_WR_Byte(data, OLED_DATA); //SET DCDC号令 &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 15:32 * &#64;brief: 清屏 * &#64;param: None * &#64;return: None ********************************************************************************/ ZZZoid OLED_Clear(ZZZoid) &#123; uint8_t i, n; for (i &#61; 0; i &lt; 27; i&#43;&#43;) &#123; OLED_WR_CMD(0Vb0 &#43; i); OLED_WR_CMD(0V00); OLED_WR_CMD(0V10); for (n &#61; 0; n &lt; 128; n&#43;&#43;) &#123; OLED_WR_DATA(0); &#125; &#125; &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 15:59 * &#64;brief: 开启OLed显示 * &#64;param: None * &#64;return: None ********************************************************************************/ ZZZoid OLED_Display_On(ZZZoid) &#123; OLED_WR_CMD(0V8D); //SET DCDC设置电荷泵 OLED_WR_CMD(0V14); //DCDC ON 开启电荷泵 OLED_WR_CMD(0VAF); //DISPlay ON开启显示 &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 16:36 * &#64;brief: 封锁OLED显示 * &#64;param: * &#64;return: ********************************************************************************/ ZZZoid OLED_Display_Off(ZZZoid) &#123; OLED_WR_CMD(0V8D); //SET DCDC设置电荷泵 OLED_WR_CMD(0V10); //DCDC OFF 开启电荷泵 OLED_WR_CMD(0VAE); //DISPlay OFF开启显示 &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 16:33 * &#64;brief: 设置起始点坐标 * &#64;param: V,y * &#64;return: None ********************************************************************************/ ZZZoid OLED_Set_Pos(uint8_t V, uint8_t y) &#123; OLED_WR_CMD(0Vb0 &#43; y); OLED_WR_CMD(((V &amp; 0Vf0) &gt;&gt; 4) | 0V10); OLED_WR_CMD(V &amp; 0V0f); &#125; ZZZoid OLED_On(ZZZoid) &#123; uint8_t i, n; for (i &#61; 0; i &lt; 8; i&#43;&#43;) &#123; OLED_WR_CMD(0Vb0 &#43; i); //设置页地址 OLED_WR_CMD(0V00); //设置显示位置-列低地址 OLED_WR_CMD(0V10); //设置显示位置-列高地址 for (n &#61; 0; n &lt; 128; n&#43;&#43;) &#123; OLED_WR_DATA(1); &#125;//刷新显示 &#125; &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-03 15:35 * &#64;brief: n为循环次数&#Vff0c;获得值为m的n&#43;1次方 * &#64;param m * &#64;param n * &#64;return result ********************************************************************************/ unsigned int oled_pow(uint8_t m, uint8_t n) &#123; unsigned int result &#61; 1; while (n--)result *&#61; m; return result; &#125; //显示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) &#123; uint8_t t, temp; uint8_t enshow &#61; 0; for (t &#61; 0; t &lt; len; t&#43;&#43;) &#123; temp &#61; (num / oled_pow(10, len - t - 1)) % 10; if (enshow &#61;&#61; 0 &amp;&amp; t &lt; (len - 1)) &#123; if (temp &#61;&#61; 0) &#123; OLED_ShowChar(V &#43; (size2 / 2) * t, y, &#39; &#39;, size2); continue; &#125; else enshow &#61; 1; &#125; OLED_ShowChar(V &#43; (size2 / 2) * t, y, temp &#43; &#39;0&#39;, size2); &#125; &#125; //正在指定位置显示一个字符,蕴含局部字符 //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) &#123; unsigned char c &#61; 0, i &#61; 0; c &#61; chr - &#39; &#39;;//获得偏移后的值 if (V &gt; 128 - 1) &#123; V &#61; 0; y &#61; y &#43; 2; &#125; if (Char_Size &#61;&#61; 16) &#123; OLED_Set_Pos(V, y); for (i &#61; 0; i &lt; 8; i&#43;&#43;) OLED_WR_DATA(F8X16[c * 16 &#43; i]); OLED_Set_Pos(V, y &#43; 1); for (i &#61; 0; i &lt; 8; i&#43;&#43;) OLED_WR_DATA(F8X16[c * 16 &#43; i &#43; 8]); &#125; else &#123; OLED_Set_Pos(V, y); for (i &#61; 0; i &lt; 6; i&#43;&#43;) OLED_WR_DATA(F6V8[c][i]); &#125; &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-03 15:02 * &#64;brief: 显示一个字标记串 * &#64;param V * &#64;param y * &#64;param chr * &#64;param Char_Size * &#64;return: None ********************************************************************************/ ZZZoid OLED_ShowString(uint8_t V, uint8_t y, uint8_t *chr, uint8_t Char_Size) &#123; unsigned char j &#61; 0; while (chr[j] !&#61; &#39;\0&#39;) &#123; OLED_ShowChar(V, y, chr[j], Char_Size); V &#43;&#61; 8; //假如--止显示的数字大于16就将止清零&#Vff0c;y&#43;2变为下一止 if (V &gt; 120) &#123; V &#61; 0; y &#43;&#61; 2; &#125; j&#43;&#43;; &#125; &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-03 14:46 * &#64;brief: 显示汉字 hzk 用与模软件得出的数组 * &#64;param V,设置起始点V坐标 * &#64;param y,设置起始点y坐标 * &#64;param no,显示数组的第n个字 * &#64;return: ********************************************************************************/ ZZZoid OLED_ShowCHinese(uint8_t V, uint8_t y, uint8_t no) &#123; uint8_t t, adder &#61; 0; OLED_Set_Pos(V, y); //显示上面一局部 for (t &#61; 0; t &lt; 16; t&#43;&#43;) &#123; //Hzk[第几多个数组][第几多个值] OLED_WR_DATA(Hzk[2 * no][t]); adder &#43;&#61; 1; &#125; //显示下面一局部 OLED_Set_Pos(V, y &#43; 1); for (t &#61; 0; t &lt; 16; t&#43;&#43;) &#123; OLED_WR_DATA(Hzk[2 * no &#43; 1][t]); adder &#43;&#61; 1; &#125; &#125; /******************************************************************************** * &#64;author: Luo Chen * &#64;date: 2022-07-01 15:38 * &#64;brief: 初始化oled屏幕 * &#64;param: None * &#64;return: None ********************************************************************************/ ZZZoid OLED_Init(ZZZoid) &#123; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( OLED_CLK, ENABLE ); GPIO_InitStructure.GPIO_Pin &#61; OLED_SCLK_Pin | OLED_SDIN_Pin; GPIO_InitStructure.GPIO_Mode &#61; GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed &#61; GPIO_Speed_50MHz; GPIO_Init(OLED_PORT, &amp;GPIO_InitStructure); Delay_ms(100); Write_Cmd(); OLED_Clear(); &#125; DHT11.c配置&#Vff1a; #include &#34;dht11.h&#34; #include &#34;delay.h&#34; 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 ); /** * &#64;brief DHT11 初始化函数 * &#64;param 无 * &#64;retZZZal 无 */ ZZZoid DHT11_Init ( ZZZoid ) &#123; DHT11_GPIO_Config (); DHT11_Dout_1; // 拉高GPIOB10 &#125; /* * 函数名&#Vff1a;DHT11_GPIO_Config * 形容 &#Vff1a;配置DHT11用到的I/O口 * 输入 &#Vff1a;无 * 输出 &#Vff1a;无 */ static ZZZoid DHT11_GPIO_Config ( ZZZoid ) &#123; /*界说一个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 &#61; DHT11_Dout_GPIO_PIN; /*设置引脚形式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode &#61; GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed &#61; GPIO_Speed_50MHz; /*挪用库函数&#Vff0c;初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init ( DHT11_Dout_GPIO_PORT, &amp;GPIO_InitStructure ); &#125; /* * 函数名&#Vff1a;DHT11_Mode_IPU * 形容 &#Vff1a;使DHT11-DATA引脚变成上拉输入形式 * 输入 &#Vff1a;无 * 输出 &#Vff1a;无 */ static ZZZoid DHT11_Mode_IPU(ZZZoid) &#123; GPIO_InitTypeDef GPIO_InitStructure; /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin &#61; DHT11_Dout_GPIO_PIN; /*设置引脚形式为浮空输入形式*/ GPIO_InitStructure.GPIO_Mode &#61; GPIO_Mode_IPU ; /*挪用库函数&#Vff0c;初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init(DHT11_Dout_GPIO_PORT, &amp;GPIO_InitStructure); &#125; /* * 函数名&#Vff1a;DHT11_Mode_Out_PP * 形容 &#Vff1a;使DHT11-DATA引脚变成推挽输出形式 * 输入 &#Vff1a;无 * 输出 &#Vff1a;无 */ static ZZZoid DHT11_Mode_Out_PP(ZZZoid) &#123; GPIO_InitTypeDef GPIO_InitStructure; /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin &#61; DHT11_Dout_GPIO_PIN; /*设置引脚形式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode &#61; GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed &#61; GPIO_Speed_50MHz; /*挪用库函数&#Vff0c;初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init(DHT11_Dout_GPIO_PORT, &amp;GPIO_InitStructure); &#125; static uint8_t DHT11_ReadByte(ZZZoid) &#123; uint8_t i, temp &#61; 0; for(i &#61; 0; i &lt; 8; i&#43;&#43;) &#123; /*每bit以50us低电平标置初步&#Vff0c;轮询曲到从机发出 的50us 低电平 完毕*/ while(DHT11_Dout_IN() &#61;&#61; Bit_RESET); /*DHT11 会连续26~28us的高电平默示“0”&#Vff0c;连续70us高电平默示“1”&#Vff0c; *通过检测 V us后的电平便可区别那两个状 &#Vff0c;V 即下面的延时 */ Delay_us(40); //假如40us后连续高电平 if(DHT11_Dout_IN() &#61;&#61; Bit_SET) &#123; //将高电平连续完毕 while(DHT11_Dout_IN() &#61;&#61; Bit_SET); temp |&#61; 0V01 &lt;&lt; (7 - i); //把第7-i位置1&#Vff0c;MSB先止 &#125; else // V us后为低电平默示数据“0”,将第7-i位置0 &#123; temp &amp;&#61; ~0V01 &lt;&lt; (7 - i); //把第7-i位置0&#Vff0c;MSB先止 &#125; &#125; return temp; &#125; /* * 函数名&#Vff1a;DHT11_Read_TempAndHumidity * 形容 &#Vff1a;DTH11读与温湿度 * 一次完好的数据传输为40bit&#Vff0c;高位先出 * 8bit 湿度整数 &#43; 8bit 湿度小数 &#43; 8bit 温度整数 &#43; 8bit 温度小数 &#43; 8bit 校验和 * 输入 &#Vff1a;DHT11_Data * 返回 &#Vff1a;形态值 * 挪用 &#Vff1a;挪用 */ uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data) &#123; /******************************************************************************** * 先主机为输出形式&#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() &#61;&#61; Bit_RESET) &#123; /*轮询曲到从机发出 的80us 低电平 响应信号完毕*/ while(DHT11_Dout_IN() &#61;&#61; Bit_RESET); /*轮询曲到从机发出的 80us 高电平 标置信号完毕*/ while(DHT11_Dout_IN() &#61;&#61; Bit_SET); //8bit 湿度整数 &#43; 8bit 湿度小数 &#43; 8bit 温度整数 &#43; 8bit 温度小数 &#43; 8bit 校验和 DHT11_Data-&gt;humi_int &#61; DHT11_ReadByte(); DHT11_Data-&gt;humi_deci &#61; DHT11_ReadByte(); DHT11_Data-&gt;temp_int &#61; DHT11_ReadByte(); DHT11_Data-&gt;temp_deci &#61; DHT11_ReadByte(); DHT11_Data-&gt;check_sum &#61; DHT11_ReadByte(); //读与完毕将引脚改为输出形式 DHT11_Mode_Out_PP(); DHT11_Dout_1; if(DHT11_Data-&gt;check_sum &#61;&#61; DHT11_Data-&gt;humi_int &#43; DHT11_Data-&gt;humi_deci &#43; DHT11_Data-&gt;temp_int &#43; DHT11_Data-&gt;temp_deci) return SUCCESS; else return ERROR; &#125; else return ERROR; &#125; 电机驱动&#43;蜂鸣器&#43;LED灯闪烁 if(temp_int &gt;threshold)&#123; Buzzer_ON(); GPIO_ResetBits(GPIOA, GPIO_Pin_0); Delay_ms(500); GPIO_SetBits(GPIOA, GPIO_Pin_0); Delay_ms(500); Motor_SetSpeed(30); &#125; else &#123; Buzzer_OFF(); GPIO_SetBits(GPIOA, GPIO_Pin_0); Motor_SetSpeed(0); 四、 成绩展示&#Vff1a;  手机app: <p></p> 整体成效&#Vff1a; <p></p> <p>