1.功能设计
本项目设计一种基于LORA无线通信的屋内火灾报警控制系统,采用DH11温湿度传感器跟MQ-2烟雾传感器作为火警安防系统,对房内温度,湿度,还有环境可燃气体,烟雾等实时检测,LORA通信为通信报警方法,LED跟蜂鸣器为声光报警,SG90舵机做为应急措施的开关。当温度湿度传感部分采集到超过限定温度值和低于限定湿度值时,会将信号发给相对应的传感检测部分,然后传感检测部分将信号发给核心处理器单片机,STM32F103RCT6核心处理单片机引脚电平转换,驱动声光报警系统和应急措施,同时启动LORA通信发送给业主和物业,让救援人员来时了解到大致情况做好充足准备。
(1) 检测项目:当环境温度≥45℃,湿度≤10%就可被判定为火灾,或者可燃气体甲烷浓度≥5%也可被判定为火灾可能发生,实施报警,并且采取应急措施。烟雾浓度≥5%被认定为有毒害,实施报警。
(2) 报警方式:声光报警,小区物业报警,户主报警。
(3) 应急措施:通过舵机控制阻燃气体的发射。
2.硬件设计
本系统由电源电路、主控制器、温度,湿度检测电路、烟雾可燃气体检测电路,LORA模块收发电路,应急措施电路,信息显示电路,声光报警电路等8个部分组成。
3.软件设计
其余介绍看,评论区报告链接,硬件设备工作流程图,以及步骤介绍等等。。。
程序分享
1.main.c
#include "led.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "dh11.h"
#include "adc.h"
#include "sys.h"
#include "delay.h"
#include "pwm.h"
#include "beep.h"
#include "lora.h"
#include "usart1.h"
#include "usart3.h"
char lora_tx_data[100];
char lora_rx_data[100];
int main(void)
{
u16 ADC_SHIDU ;
u8 t=0;
u8 temp;
u8 humi;
delay_init(); //延时函数初始化
Adc_Init(); //延时函数初始化
uart_init(115200); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init();
Beep_Init();//蜂鸣器初始化
SystemInit(); //配置系统时钟为 72M
USART1_Config();
TIM3_PWM_Init(899,0);//不分频。PWM频率=72000/(899+1)=80Khz
USART3_Config(115200); //USART3 配置
LORA_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(60,50,200,16,16,"GHB");
LCD_ShowString(60,70,200,16,16,"GXW");
LCD_ShowString(60,90,200,16,16,"LX");
LCD_ShowString(60,110,200,16,16,"2021/12/6");
LCD_ShowString(60,130,200,16,16,"DHT11 OK");
LCD_ShowString(60,150,200,16,16,"QCM2 OK");
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(60,170,200,16,16,"Temp: . C");
LCD_ShowString(60,190,200,16,16,"Humi: %");
LCD_ShowString(60,210,200,16,16,"Qtzl: %");
while(DHT11_Init()) //DHT11初始化
{
LCD_ShowString(60,130,200,16,16,"DHT11 Error");
delay_ms(200);
LCD_Fill(60,130,239,130+16,WHITE);
delay_ms(200);
}
while(1)
{
ADC_SHIDU=ADC_Get_aveg(ADC_Channel_1,10);
ADC_SHIDU = (float)(ADC_SHIDU/4096.0)*100;
{
DHT11_Read_Data(&temp,&humi); //读取温湿度值
LCD_ShowNum(60+40+4,170,temp,2,16); //显示温度
LCD_ShowNum(60+40+28,170,temp%10,1,16); //显示温度
LCD_ShowNum(60+40+6,190,humi,2,16); //显示湿度
LCD_ShowNum(60+40+6,210,ADC_SHIDU,2,16);
delay_ms(100);
}
delay_ms(10);
t++;
if(t==20)
{
t=0;
LED1=!LED1;
}
if(temp>30)
{
delay_ms(100);
LED0=!LED0;
delay_ms(100);
LED1=!LED1;
GPIO_ResetBits(GPIOA, GPIO_Pin_8);//输出低电平
delay_ms(100);
GPIO_SetBits(GPIOA, GPIO_Pin_8);//输出高电平
delay_ms(100); //延时100毫秒
TIM_SetCompare1(TIM3, 195);//0度
delay_ms(100);
TIM_SetCompare1(TIM3, 190);//45度
delay_ms(100);
sprintf((char*)lora_tx_data,"温度:%d‰\r\n烟雾:%d %$", temp, ADC_SHIDU);
lora_transmit(lora_tx_data);
delay_ms(100);
}
if(ADC_SHIDU>40)
{
delay_ms(100);
LED0=!LED0;
delay_ms(100);
LED1=!LED1;
GPIO_ResetBits(GPIOA, GPIO_Pin_8);//输出低电平
delay_ms(100);
GPIO_SetBits(GPIOA, GPIO_Pin_8);//输出高电平
delay_ms(100); //延时100毫秒
TIM_SetCompare1(TIM3, 195);//0度
delay_ms(100);
TIM_SetCompare1(TIM3, 190);//45度
delay_ms(100);
sprintf((char*)lora_tx_data,"温度:%d‰\r\n烟雾:%d %$", temp, ADC_SHIDU);
lora_transmit(lora_tx_data);
delay_ms(100);
}
}
}
2. LORA.C
#include "lora.h"
//#include "usart.h"
#include "usart1.h"
#include "usart3.h"
#include "delay.h"
#include "led.h"
void MD0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); // 使能PC端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //选择对应的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PC端口
GPIO_ResetBits(GPIOA, GPIO_Pin_11 ); //拉低
}
void AUX_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //开启按键端口PA的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //端口配置为下拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化端口
GPIO_ResetBits(GPIOA, GPIO_Pin_4 ); // 关闭所有LED
}
int AUX(void)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4) == 1)
return 1;
else
return 0;
}
void lora_check(void)
{
int OK_state = 0;
while(AUX())//检测模块是否在忙
{
UART1TX("Lora正忙1");
delay_ms(500);
}
MD0(1);
delay_ms(40);
UART1TX("LORA检测中......");
while(!OK_state)//模块检测成功
{
UART3TX("AT");
UART3Test(&OK_state);
}
UART1TX("LORA检测成功");
UART1TX(" ");
}
void lora_set(void)
{
/*#####设置地址#########*/
int OK_state = 0;
char addr[20] = {0};int t1 = 0,t2 = 0;
unsigned char i = 0;
while(AUX())//等待模块空闲
{
UART1TX("Lora正忙2");
}
MD0(1);
delay_ms(40);
while(!OK_state)//模块检测成功
{
UART3TX("AT+ADDR=00,00");
UART3Test(&OK_state);
UART1TX("地址设置中......");
}
UART1TX("地址为:");
UART3TX("AT+ADDR?");
while(1)
{
UART3GetByte(&i);
if(i==':')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='O')
{
addr[t1++]=i;
}
else if(i=='O')
break;
}
while(1)
{
UART1SendByte(addr[t2++]);
if(t1 == t2)
break;
}
break;
}
}
UART1TX(" ");
/*#################设置信道和速率###############*/
OK_state = 0;
t1 = 0;
t2 = 0;
while(!OK_state)//模块检测成功
{
UART3TX("AT+WLRATE=23,5");
UART3Test(&OK_state);
UART1TX("信道速率设置中......");
}
UART1TX("信道,速率为:");
UART3TX("AT+WLRATE?");
while(1)
{
UART3GetByte(&i);
if(i==':')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='O')
{
addr[t1++]=i;
}
else if(i=='O')
break;
}
while(1)
{
UART1SendByte(addr[t2++]);
if(t1 == t2)
break;
}
break;
}
}
UART1TX(" ");
/*#############发射功率##############*/
OK_state = 0;
t1 = 0;
t2 = 0;
while(!OK_state)//模块检测成功
{
UART3TX("AT+TPOWER=3");
UART3Test(&OK_state);
UART1TX("发射功率设置中......");
}
UART1TX("发射功率为:");
UART3TX("AT+TPOWER?");
while(1)
{
UART3GetByte(&i);
if(i==':')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='O')
{
addr[t1++]=i;
}
else if(i=='O')
break;
}
while(1)
{
UART1SendByte(addr[t2++]);
if(t1 == t2)
break;
}
break;
}
}
UART1TX(" ");
/*#################工作模式###################*/
OK_state = 0;
t1 = 0;
t2 = 0;
while(!OK_state)//模块检测成功
{
UART3TX("AT+CWMODE=0");
UART3Test(&OK_state);
UART1TX("工作模式设置中......");
}
UART1TX("工作模式为:");
UART3TX("AT+CWMODE?");
while(1)
{
UART3GetByte(&i);
if(i==':')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='O')
{
addr[t1++]=i;
}
else if(i=='O')
break;
}
while(1)
{
UART1SendByte(addr[t2++]);
if(t1 == t2)
break;
}
break;
}
}
UART1TX(" ");
/*#################发送状态#################*/
OK_state = 0;
t1 = 0;
t2 = 0;
while(!OK_state)//模块检测成功
{
UART3TX("AT+TMODE=0");//0:透明传输,1:定向传输
UART3Test(&OK_state);
UART1TX("发送状态设置中......");
}
UART1TX("发送状态为:");
UART3TX("AT+TMODE?");
while(1)
{
UART3GetByte(&i);
if(i==':')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='O')
{
addr[t1++]=i;
}
else if(i=='O')
break;
}
while(1)
{
UART1SendByte(addr[t2++]);
if(t1 == t2)
break;
}
break;
}
}
UART1TX(" ");
/*#############睡眠时间#################*/
OK_state = 0;
t1 = 0;
t2 = 0;
while(!OK_state)//模块检测成功
{
UART3TX("AT+WLTIME=0");
UART3Test(&OK_state);
UART1TX("睡眠时间设置中......");
}
UART1TX("睡眠时间为:");
UART3TX("AT+WLTIME?");
while(1)
{
UART3GetByte(&i);
if(i==':')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='O')
{
addr[t1++]=i;
}
else if(i=='O')
break;
}
while(1)
{
UART1SendByte(addr[t2++]);
if(t1 == t2)
break;
}
break;
}
}
UART1TX(" ");
/*################波特率,数据校验位####################*/
OK_state = 0;
t1 = 0;
t2 = 0;
while(!OK_state)//模块检测成功
{
UART3TX("AT+UART=3,0");
UART3Test(&OK_state);
UART1TX("波特率,数据校验位设置中......");
}
UART1TX("波特率,数据校验位为:");
UART3TX("AT+UART?");
while(1)
{
UART3GetByte(&i);
if(i==':')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='O')
{
addr[t1++]=i;
}
else if(i=='O')
break;
}
while(1)
{
UART1SendByte(addr[t2++]);
if(t1 == t2)
break;
}
break;
}
}
UART1TX(" ");
MD0(0);
delay_ms(40);
while(AUX());
USART3_Config(9600); //USART3 配置
UART1TX("LORA配置完成######################");
}
void lora_transmit(char* a)
{
int i = 0;
UART3SendByte('#');//起始符
while(a[i] != '$')
{
UART3SendByte(a[i]);
i++;
}
UART3SendByte('$');//结束符
UART3SendByte('\r');
UART3SendByte('\n');
}
void lora_receive(void)
{
unsigned char i = 0;
extern unsigned char lora_rx_data[100];
// extern char lora_rx_data[9];
int t1 = 0,t2 = 0;
while(1)
{
UART3GetByte(&i);
if(i=='#')
{
while(1)
{
if(UART3GetByte(&i) == 1 && i!='$')
{
lora_rx_data[t1++]=i;
}
else if(i=='$')
break;
}
while(1)
{
UART1SendByte(lora_rx_data[t2++]);
if(t1 == t2)
{
UART1SendByte('\r');
UART1SendByte('\n');
break;
}
}
break;
}
}
}
void LORA_Init(void)
{
MD0_Config(); //初始化MD0
AUX_Config();
// LED_Config();
lora_check();
lora_set(); //配置LORA
}
3. DHC11.C
#include "dh11.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(50); //主机拉高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)
{
DHT11_Rst(); //复位DHT11
return DHT11_Check();//等待DHT11的回应
}
4. PWM.C
#include "pwm.h"
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 199; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =7199; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM3,ENABLE); //MOE 主输出使能
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //CH1预装载使能
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM1
}
5.ADC.C
#include "adc.h"
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12MHZ
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
u16 Get_val(u8 ch)
{
u16 DataValue;
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
DataValue = ADC_GetConversionValue(ADC1);
return DataValue;
}
u16 ADC_Get_aveg(u8 ch,u8 n)
{
u32 ad_sum = 0;
u8 i;
for(i=0;i