基于stm32的环境监控控制系统,温湿度采集

今天来讲一下本次的项目,一个采集环境数据,并控制的简单项目。

实物获取和资料代码获取我会放在最后面。

其主要功能如下:

1、采集温湿度 2、采集烟雾浓度 3、蜂鸣器报警同时向手机终端发送报警信息 4、继电器控制风扇和加热片 5、OLED显示 6、蓝牙控制

先看实物,在来说说具体的功能

62d74a735a3147d8be0a07d52171080e.jpeg

OLED显示屏上会显示出当前的温度,湿度和烟雾浓度,还有极限值,当温度超过温度极限值时,继电器会吸合,打开风扇降温,低于极限值时,另一个继电器吸合打开加热片。湿度同理,也会有一样的控制效果。除了自动控制以外,还可以通过手机app来控制。其中按键是用来调节温湿度极限值的。

接下来,我们一个一个模块来看,先看最简单的mq-2烟雾传感器。

MQ-2 气体传感器使用的气敏材料是在清洁空气中电导率较低的二氧化锡(SnO2)。当传感器所处环境中存在 可燃气体时,传感器的电导率随空气中可燃气体浓度的增加而增大。使用简单的电路即可将电导率的变化转换为 与该气体浓度相对应的输出信号。MQ-2 气体传感器对液化气、丙烷、氢气的灵敏度高,对天然气和其它可燃蒸 汽的检测也很理想。这种传感器可检测多种可燃气体,是一款适合多种应用的低成本传感器。

 接下来,我们看接线

d43acf4d97bf48afb5c2e596bcc33814.png

 mq-2有四个引脚,D0引脚悬空,A0引脚接单片机的adc引脚,这里不是pc2引脚,程序上是哪个,就接哪个。

接下来,我们看程序上如何配置adc

void  Adc_Init(void)
{ 	
	GPIO_InitTypeDef  GPIO_InitStructure;
	ADC_InitTypeDef  ADC_InitStructure; 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO, ENABLE );   
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //72M/6=12,ADC最大时间不能超过14M    
                       
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;	 //PB0/1 作为模拟通道输入引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;                
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;	 //PA6 作为模拟通道输入引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;                
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//------------------------------------ADC设置--------------------------------------------------------

	ADC_DeInit(ADC1);  //将外设 ADC1 的全部寄存器重设为缺省值
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode =ENABLE;        //多信道扫描模式
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  //模数转换工作在连续转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   //外部触发转换关闭
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;        //ADC数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 3;        //此处开3个信道(可开的为1~16)
	ADC_Init(ADC1, &ADC_InitStructure);        //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器

	//ADC常规信道配置
	//ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_55Cycles5 );  //PA6              
	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 2, ADC_SampleTime_55Cycles5 );  //PB0
	ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 3, ADC_SampleTime_55Cycles5 );  //PB1              


	// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
	ADC_DMACmd(ADC1, ENABLE);       //使能ADC1的DMA传输         
	ADC_Cmd(ADC1, ENABLE);           //使能指定的ADC1
	ADC_ResetCalibration(ADC1);        //复位指定的ADC1的校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));        //获取ADC1复位校准寄存器的状态,设置状态则等待
	ADC_StartCalibration(ADC1);                //开始指定ADC1的校准状态
	while(ADC_GetCalibrationStatus(ADC1));                //获取指定ADC1的校准程序,设置状态则等待
}				  

配置完成后,在主程序中直接获取adc的值即可。

m = Get_Adc_Average_mq2();

至于dht11温湿度模块,用起来也很简单,至于其中的原理我们日后再细说,今天要讲的是整个项目如何构建。dht11的话,网上随便移植一个代码,在主函数一句话调用就完事了。下面给出一个dht11的代码供大家使用,我也移植来的。

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


      
//复位DHT11
void DHT11_Rst(void)	   
{                 
	DHT11_IO_OUT(); 	//SET OUTPUT
    DHT11_DQ_OUT=0; 	//拉低DQ
    delay_ms(20);    	//拉低至少18ms
    DHT11_DQ_OUT=1; 	//DQ=1 
	delay_us(30);     	//主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();//SET INPUT	 
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
	{
		retry++;
		delay_us(1);
	};
	if(retry>=100)return 1;	    
	return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);//等待40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}
//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();
	if(DHT11_Check()==0)
	{
		for(i=0;i<5;i++)//读取40位数据
		{
			buf[i]=DHT11_Read_Byte();
		}
		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
		{
			*humi=buf[0];
			*temp=buf[2];
		}
	}else return 1;
	return 0;	    
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在    	 
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB端口时钟
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;				 //PB11端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);				 //初始化IO口
 	GPIO_SetBits(GPIOB,GPIO_Pin_11);						 //PB11 输出高
			    
	DHT11_Rst();  //复位DHT11
	return DHT11_Check();//等待DHT11的回应
} 
#ifndef __DHT11_H
#define __DHT11_H 
#include "sys.h"   

//接口连接:DATA->PB11
//IO方向设置
#define DHT11_IO_IN()  {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=8<<12;}
#define DHT11_IO_OUT() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=3<<12;}
IO操作函数											   
#define	DHT11_DQ_OUT PBout(11) //数据端口	PB11
#define	DHT11_DQ_IN  PBin(11)  //数据端口	PB11

u8 DHT11_Init(void);//初始化DHT11
u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度
u8 DHT11_Read_Byte(void);//读出一个字节
u8 DHT11_Read_Bit(void);//读出一个位
u8 DHT11_Check(void);//检测是否存在DHT11
void DHT11_Rst(void);//复位DHT11    
#endif

dht11的接线也十分的简单,只有一个数据引脚,剩下的vcc接vcc,gnd接gnd即可。

蜂鸣器和继电器就没有什么好说的了,给一个高低电平就能够完成对他的操作,蜂鸣器低电平时,就会发出声响,高电平就会停止鸣叫,继电器也是一样的,高低电平完成他的开和关。最简单的写法,用if判断温湿度,随后决定给高电平还是低电平。

if(temp>=temp_max||temp<temp_min)				//温度过高,打开风扇和蜂鸣器
   {
		 //relay_on();
		 GPIO_ResetBits(GPIOB,GPIO_Pin_7);
		 GPIO_ResetBits(GPIOB,GPIO_Pin_8);
		 GPIO_ResetBits(GPIOB,GPIO_Pin_5);                                                                                                                
		 delay_ms(1000);
		 GPIO_SetBits(GPIOB,GPIO_Pin_5);
		 Serial_SendString("温度过高,请及时处理\r\n");		 
	 }

其中最后一句是发送报警信息给手机的,利用的是蓝牙jdy-31,和大家所熟悉的hc-05差不多,在使用上没有什么区别。

蓝牙主要的是利用串口通信的原理,tx和rx发送接收信息。

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>
#include <hc_05.h>
 
char Serial_RxPacket[100];				//USART1_IRQHandler那接收数据帧中的数据部分存储位置
uint8_t Serial_RxFlag;
 
void Serial_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 这两行代码用来使能USART1和GPIOA的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 配置GPIO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //输入上拉
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置USART1的通信参数
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1, &USART_InitStructure);

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能USART1的接收中断

    // 配置中断优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // 配置USART1的中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);

    USART_Cmd(USART1, ENABLE); //用来使能USART1
}
//这个函数接受一个8位无符号整数(Byte)作为参数,将该字节数据发送到USART1串口
void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//RESET=0
}
 
 
//通过串口发送一个字节数组的数据。做法是函数接受一个指向字节数组的指针(Array)和数组的长度(Length)作为参数,然后通过循环将数组中的每个字节逐个发送
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}
 
//函数接受一个指向字符数组的指针(String),表示要发送的字符串数据。
//用法是它使用循环遍历字符串中的每个字符,并调用Serial_SendByte()函数将字符逐个发送出去,直到遇到字符串结束符’\0’
void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}
 
//一个算法,即底数X的Y次幂的值。接受两个正整数参数:X(底数)和Y(指数)。它使用一个while循环,将底数X连续乘以自身Y次,得到结果Result。初始时,结果Result被初始化为1
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}
 
//Number表示要发送的数字,Length表示要发送的数字的位数。它使用一个循环,从最高位开始逐位地发送数值
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');//Serial_Pow(10, Length - i - 1)是用来获取当前位的除数,实现数字的右移操作
	}
}
 
//该函数的目的是将ch的值通过串口发送出去,并返回相同的值。即Serial_SendByte()被调用以将ch的值通过串口发送出去。最后,函数返回ch的值。
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}
 
//通过串口打印格式化的字符串,实现类似printf()的功能
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}
 
/**************************************以上跟江科大完全一样************************************/
 
 
void USART1_IRQHandler(void)//USART1串口中断服务函数
{
	static uint8_t flag = 0;
	static uint8_t pRxPacket = 0;
	
	
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//先通过USART_GetITStatus()函数检查USART1接收中断标志位是否置位,如果RXNE置一了,就进if判断
	{
		uint8_t RxData = USART_ReceiveData(USART1);//调用USART_ReceiveData()函数读取接收寄存器中的数据到变量RxData中
		
		
		if (flag == 0)
		{
			if (RxData == '0' )//RxData为0,表示接收到了数据帧的起始符号
			{
				flag = 1;
				pRxPacket = 0;
			}
		}
		
		else if (flag == 1)
		{
			if (RxData == '1')//如果接收到字符’1',表示接收到了数据帧的结束符号
			{
				flag = 0;
				Serial_RxPacket[pRxPacket] = '\0';//表示接收到了数据帧的终止符号,将状态切换为0,并在数据包末尾添加字符串结束符号’\0’
				Serial_RxFlag = 1;//表示接收到了完整的数据包
			}
			else//否则,将接收到的数据存储到Serial_RxPacket数组中
			{
				Serial_RxPacket[pRxPacket] = RxData;
				pRxPacket ++;
			}
		}
					USART_ClearITPendingBit(USART1, USART_IT_RXNE);//手动清除USART1接收中断标志位,以结束中断处理过程
	}
}
	//在蓝牙上写"0on1"即可实现打开灯,"0off1"关闭灯

我们来看一看在主函数中,是如何使用的。

if (Serial_RxFlag == 1)
		{			
			if (strcmp(Serial_RxPacket, "on") == 0) 
			{
				//LED1_ON();
				GPIO_SetBits(GPIOC, GPIO_Pin_13);
				Serial_SendString("LED_ON\r\n");
			
			}
			else if (strcmp(Serial_RxPacket, "off") == 0)
			{
				//LED1_OFF();
				GPIO_ResetBits(GPIOC, GPIO_Pin_13);
				Serial_SendString("LED_OFF\r\n");
			

我这里写的是,判断接收的信息,是不是“on”,是的话,给pc13引脚高电平,在判断是不是“off”,是的话给低电平。这里引脚改成继电器或者蜂鸣器的引脚都可以,通过改引脚来实现自己想要的结果。

这个项目还是比较简单的,想要实物的话可以联系

成品实物获取链接:https://m.tb.cn/h.5OOYu2j?tk=qDqLWf2di9U CZ3457

资料获取链接:【免费】oled,dht11,mq-2,stm32f103c8t6等相关参考资料资源-CSDN文库

 

 

  • 0
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
基于STM32温湿度检测与报警系统的设计。温湿度检测与报警系统是一种用于监测环境温湿度并及时报警的设备。基于STM32的设计能够提高系统的可靠性和稳定性。 首先,系统需要通过传感器获取环境温度和湿度数据。可以选择市场上常用的温湿度传感器,如DHT11或DHT22。通过STM32的GPIO接口连接传感器,使用相应的引脚进行数据采集。 其次,通过STM32的模数转换器(ADC)将传感器采集到的模拟信号转换为数字信号。可以使用STM32的内置ADC模块,通过编程配置ADC通道和采样频率,实现将模拟信号转换为数字值。 然后,使用STM32的串行通信接口(例如UART)将温湿度数据发送给外部设备,如计算机或显示屏。可以通过编程配置UART串口的波特率和数据格式,实现与外部设备的通信。 最后,设计温湿度报警功能。通过对比获取的温湿度数据与预设的报警阈值,判断是否触发报警。当温度或湿度超过预设的阈值时,STM32将触发报警信号。可以使用STM32的GPIO接口连接蜂鸣器或LED灯,实现报警的声音或灯光提示。 此外,可以进一步完善系统的功能。例如,可以添加数据存储功能,将获取的温湿度数据保存在闪存中,以备后续分析和查询。还可以添加通信模块(如以太网或WiFi模块),实现对温湿度数据的远程监测和控制。 综上所述,基于STM32温湿度检测与报警系统设计包括传感器的选择和连接、ADC的使用、UART的通信配置以及报警功能的实现。该系统能够实时监测环境温湿度,并根据预设的阈值及时报警,具有较高的可靠性和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

惹清风

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

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

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

打赏作者

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

抵扣说明:

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

余额充值