基于STM32设计的车库监控报警系统

一、前言

随着社会的发展,人们的生活水平不断提高对安防问题的关注度越来越高,因而各类防盗设备也层出不穷,本文采用红外测距传感器、摄像头、STM32单片机以及ESP8266无线通信模块设计一种车库防盗报警器,其可安装在车库的里,通过红外线测距检测是否有人入侵,产生报警信号,提醒相关人员采取应急防御措施。该防盗报警器成本低,功能优,防盗效果好,是家居防盗的优良选择。
image-20220327000641487

二、具体需求

(一)主要模块:密码锁开门模块+检测模块+报警模块+拍摄模块+主控制模块+传输存取模块

检测模块:红外测距传感器

报警模块:灯+蜂鸣器

拍摄模块:摄像头

传输存取模块:WIFI模块实现传输,用SD卡存取

主控制模块:TFT显示屏+主控制器(STM32单片机)

最后整体呈现方式:矩形的微型车库构造

主要用途:主要解决电瓶车在车库内被盗和电瓶车长时间充电引起火灾等不良安全隐患。

二、具体流程想法

一开始门(由能运转90°的舵机和一个小木板构建成的)和限位开关一直触碰着

(二)、输入正确密码进入车库:

门打开(即舵机运转90°,门和限位开关脱离)时,灯常亮,其他模块装置不运转;关闭车库门时,门没关紧(即门没有触碰到限位开关)时,通过WIFI模块发送信息至手机端,提醒门没关紧;门关紧后灯灭,报警系统全部正常运转。

(三)、不能输入正确密码反而也能进入车库:

不正常进入车库内时,当红外测距系统检测到人时,灯亮起和蜂鸣器响起,同时触发摄像装置,触发后的摄像装置会连续拍摄两次(注:红外测距系统没有检测到人,摄像装置是不运行的,以达到一个低功耗的目的,连续拍摄两次也可改为一次,两次是为了保证抓拍效果),拍摄的图片保存至存储模块,手机端也会收到报警信息和图片,提醒有非本人进入车库。

(四)拟所用器件

类型 数量 预期达到的效果 备注
STM32F103 1 主控制系统
TFT液晶屏 1 实现输入四位密码开门及调整红外测距的测距距离和查看图片、显示车库内的温度等
LED灯 10个或以上
ov7670摄像头 2 两个摄像头同时拍摄
温度传感器 1
一种转运90°的舵机 1 用于开关门装置
限位开关 1 门是否关紧感应装置
蜂鸣器 1
存储装置 1 存储图片
WIFI模块 1 ESP8266
红外测距装置 1 可实现自主调整测距感应 做设置10cm测距调试

(五)、想象的液晶屏效果图

二、功能拆解

(1) 通过红外线测距模块(超声波代替),检测是否有人经过,如果有人经过就调用摄像头拍摄一张照片存放到SD卡保存,并通过ESP8266通知手机APP,有人闯入,手机APP会产生警报声。手机上可以设置报警检测的距离。
(2) 照片拍摄时,保存的文件名称以当前时间为准,需要用到开发板上的RTC时钟,也需要手机APP上增加一个时间校准功能,方便校准开发板上的RTC实时时钟的时间。
(3) 实时检测环境温湿度,将温湿度信息传递给手机APP显示

技术点:

  1. 需要移植FATFS文件系统,构建BMP图片结果,才能将摄像头拍摄的照片保存在SD卡里。
  2. 需要启动RTC,实时记录当前时间,当做系统时钟使用,,手机APP需要增加一个时间校准功能。
  3. 摄像头采用OV7670,通过SCCB时序交互,需要将采集的RGB数据编码成BMP图片存放到SD卡。

三、上位机开发

上位机采用QT框架开发,分别开发了Android、windows版本。

3.1 QT 框架简介

Qt是一个1991年由QtCompany开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。

在发布 Qt 4.6 的同时,作为 Qt 开发跨平台 IDE 的Qt Creator也发布了更新版本。Qt Creator 1.3 和 Qt 4.6共同构成的 Qt SDK,包含了开发跨平台应用程序所需的全部功能。

Qt Creator是一个用于Qt开发的轻量级跨平台集成开发环境。Qt Creator可带来两大关键益处:提供首个专为支持跨平台开发而设计的集成开发环境 (IDE),并确保首次接触Qt框架的开发人员能迅速上手和操作。即使不开发Qt应用程序,Qt Creator也是一个简单易用且功能强大的IDE。

QT官网: https://resources.qt.io/cn

3.2 QT环境搭建

QT5.12.6安装包下载地址: https://download.qt.io/archive/qt/5.12/5.12.6/

QT学习专栏: https://blog.csdn.net/xiaolong1126626497/category_11400392.html
QT环境搭建文章:https://xiaolong.blog.csdn.net/article/details/120654599

3.3 软件运行效果

软件功能介绍:
(1)STM32端通过DHT11实时检测环境温湿度,通过红外测距传感器测量距离,通过ESP8266传递到手机APP上实时显示。

数据传输的格式:

$update,16,23,23.5
温度、湿度、测量的距离

(2)手机APP上可以设置红外报警检测距离,在手机页面上点击设置之后,会传递给STM32。

数据传输的格式:

#5   5的单位是厘米,表示设置5cm的检测距离。

当实际测量的距离小于了设置的报警距离,就会产生报警信息,手机APPA上会自动响起报警铃声。

(3)在STM32上有一个RTC时钟,需要实时记录当前时间,手机APP有一个时间校准按钮,点击之后,会校准STM32开发板上的时间。

数据通信格式:

*20220309114156     传递过去的就是年月日时分秒时间

下面是在win10系统下运行软件的效果。

下面是在Android系统下运行软件的效果。

3.4 模拟测试

现在先不连接设置设备,采用网络调试助手来模拟ESP8266+STM32,测试通信效果,通信过程是否正常。
左边是TCP调试助手,右边是上位机软件,可以使用TCP调试助手模拟ESP8266,与上位机之间通信。

(1)搭建模拟化环境

(2)温度、湿度、检测距离上传

数据格式协议:

上位机时间校准–向ESP8266发送:*20220309114156

上位机设置报警距离-向ESP8266发送:#5

ESP8266向上位机发送:

$update,16,23,23.5
温度、湿度、测量的距离

(3)APP设置报警距离

(4)APP报警提示演示
image-20220309132259022

四、设备端开发

如果需要完整工程,可以去这里下载:
https://download.csdn.net/download/xiaolong1126626497/85894457

4.1 硬件效果图

image-20220327000632150

4.2 OV7725摄像头核心代码

#include "sys.h"
#include "ov7725.h"
#include "ov7725cfg.h"
#include "timer.h"	  
#include "delay.h"
#include "usart.h"			 
#include "sccb.h"	


//JTAG模式设置,用于设置JTAG的模式
//mode:jtag,swd模式设置;00,全使能;01,使能SWD;10,全关闭;	   
//#define JTAG_SWD_DISABLE   0X02
//#define SWD_ENABLE         0X01
//#define JTAG_SWD_ENABLE    0X00		  
void JTAG_Set(u8 mode)
{
    
    
	u32 temp;
	temp=mode;
	temp<<=25;
	RCC->APB2ENR|=1<<0;     //开启辅助时钟	   
	AFIO->MAPR&=0XF8FFFFFF; //清除MAPR的[26:24]
	AFIO->MAPR|=temp;       //设置jtag模式
}


//初始化OV7725
//返回0:成功
//返回其他值:错误代码
u8 OV7725_Init(void)
{
    
    
	u16 i=0;
	u16 reg=0;	  
	//设置IO
	RCC->APB2ENR|=1<<2;		//先使能外设PORTA时钟
	RCC->APB2ENR|=1<<3;		//先使能外设PORTB时钟
 	RCC->APB2ENR|=1<<4;		//先使能外设PORTC时钟
  	RCC->APB2ENR|=1<<5;		//先使能外设PORTD时钟
	RCC->APB2ENR|=1<<8;		//先使能外设PORTG时钟	   
 
  	GPIOA->CRH&=0XFFFFFFF0; 	   
  	GPIOA->CRH|=0X00000008; 	//PA8 输入  
	GPIOA->ODR|=1<<8; 
   	GPIOB->CRL&=0XFFF00FFF; 	  
  	GPIOB->CRL|=0X00033000; 	//PB3/4 输出
	GPIOB->ODR|=3<<3; 	    
  	GPIOC->CRL=0X88888888; 		//PC0~7 输入    
	GPIOC->ODR|=0x00ff; 
   	GPIOD->CRL&=0XF0FFFFFF; 	//PD6 输出   
  	GPIOD->CRL|=0X03000000; 	 
 	GPIOD->ODR|=1<<6; 
   	GPIOG->CRH&=0X00FFFFFF; 	 
	GPIOG->CRH|=0X33000000;	    
	GPIOG->ODR=7<<14;      		//PG14/15  输出高 
 	JTAG_Set(1);
 	SCCB_Init();        		//初始化SCCB 的IO口	   	  
 	if(SCCB_WR_Reg(0x12,0x80))return 1;	//复位SCCB	  
	DelayMs(50);  
	reg=SCCB_RD_Reg(0X1c);		//读取厂家ID 高八位
	reg<<=8;
	reg|=SCCB_RD_Reg(0X1d);		//读取厂家ID 低八位
	if(reg!=OV7725_MID)
	{
    
    
		printf("MID:%d\r\n",reg);
		return 1;
	}
	reg=SCCB_RD_Reg(0X0a);		//读取厂家ID 高八位
	reg<<=8;
	reg|=SCCB_RD_Reg(0X0b);		//读取厂家ID 低八位
	if(reg!=OV7725_PID)
	{
    
    
		printf("HID:%d\r\n",reg);
		return 2;
	}   
 	//初始化 OV7725,采用QVGA分辨率(320*240)  
	for(i=0;i<sizeof(ov7725_init_reg_tb1)/sizeof(ov7725_init_reg_tb1[0]);i++)
	{
    
    								
	   	SCCB_WR_Reg(ov7725_init_reg_tb1[i][0],ov7725_init_reg_tb1[i][1]);
 	} 
  	return 0x00; 	//ok
} 

//OV7725功能设置
//白平衡设置
//0:自动模式
//1:晴天
//2,多云
//3,办公室
//4,家里
//5,夜晚
void OV7725_Light_Mode(u8 mode)
{
    
    
	switch(mode)
	{
    
    
		case 0:	//Auto,自动模式
			SCCB_WR_Reg(0x13, 0xff); //AWB on 
			SCCB_WR_Reg(0x0e, 0x65);
			SCCB_WR_Reg(0x2d, 0x00);
			SCCB_WR_Reg(0x2e, 0x00);
			break;
		case 1://sunny,晴天
			SCCB_WR_Reg(0x13, 0xfd); //AWB off
			SCCB_WR_Reg(0x01, 0x5a);
			SCCB_WR_Reg(0x02, 0x5c);
			SCCB_WR_Reg(0x0e, 0x65);
			SCCB_WR_Reg(0x2d, 0x00);
			SCCB_WR_Reg(0x2e, 0x00);
			break;	
		case 2://cloudy,多云
			SCCB_WR_Reg(0x13, 0xfd); //AWB off
			SCCB_WR_Reg(0x01, 0x58);
			SCCB_WR_Reg(0x02, 0x60);
			SCCB_WR_Reg(0x0e, 0x65);
			SCCB_WR_Reg(0x2d, 0x00);
			SCCB_WR_Reg(0x2e, 0x00);
			break;	
		case 3://office,办公室
			SCCB_WR_Reg(0x13, 0xfd); //AWB off
			SCCB_WR_Reg(0x01, 0x84);
			SCCB_WR_Reg(0x02, 0x4c);
			SCCB_WR_Reg(0x0e, 0x65);
			SCCB_WR_Reg(0x2d, 0x00);
			SCCB_WR_Reg(0x2e, 0x00);
			break;	
		case 4://home,家里
			SCCB_WR_Reg(0x13, 0xfd); //AWB off
			SCCB_WR_Reg(0x01, 0x96);
			SCCB_WR_Reg(0x02, 0x40);
			SCCB_WR_Reg(0x0e, 0x65);
			SCCB_WR_Reg(0x2d, 0x00);
			SCCB_WR_Reg(0x2e, 0x00);
			break;	

		case 5://night,夜晚
			SCCB_WR_Reg(0x13, 0xff); //AWB on
			SCCB_WR_Reg(0x0e, 0xe5);
			break;
	}
}		  
//色度设置
//sat:-4~+4
void OV7725_Color_Saturation(s8 sat)
{
    
    
 	if(sat>=-4 && sat<=4)
	{
    
    	
		SCCB_WR_Reg(USAT,(sat+4)<<4); 
		SCCB_WR_Reg(VSAT,(sat+4)<<4);
	}
}
//亮度设置
//bright:-4~+4
void OV7725_Brightness(s8 bright)
{
    
    
	u8 bright_value,sign;
  	switch(bright)
	{
    
    
		case 4:
			bright_value = 0x48;
			sign = 0x06;
			break;
		case 3:
			bright_value = 0x38;
			sign = 0x06;		
			break;	
		case 2:
			bright_value = 0x28;
			sign = 0x06;			
			break;	
		case 1:
			bright_value = 0x18;
			sign = 0x06;			
			break;
		case 0:
			bright_value = 0x08;
			sign = 0x06;			
			break;	
		case -1:
			bright_value = 0x08;
			sign = 0x0e;		
			break;		
		case -2:
			bright_value = 0x18;
			sign = 0x0e;		
			break;	
		case -3:
			bright_value = 0x28;
			sign = 0x0e;		
			break;	
		case -4:
			bright_value = 0x38;
			sign = 0x0e;		
			break;	
	}
	SCCB_WR_Reg(BRIGHT, bright_value);
	SCCB_WR_Reg(SIGN, sign);
}
//对比度设置
//contrast:-4~+4
void OV7725_Contrast(s8 contrast)
{
    
    
	if(contrast >= -4 && contrast <=4)
	{
    
    
		SCCB_WR_Reg(CNST,(0x30-(4-contrast)*4));
	}
}
//特效设置
//0:普通模式    
//1,负片
//2,黑白   
//3,偏红色
//4,偏绿色
//5,偏蓝色
//6,复古	    
void OV7725_Special_Effects(u8 eft)
{
    
    
	switch(eft)
	{
    
    
		case 0://正常
			SCCB_WR_Reg(0xa6, 0x06);//TSLB设置
			SCCB_WR_Reg(0x60, 0x80);//MANV,手动V值
			SCCB_WR_Reg(0x61, 0x80);//MANU,手动U值
			break;
		case 1://负片
			SCCB_WR_Reg(0xa6, 0x46);
			break;
		case 2://黑白
			SCCB_WR_Reg(0xa6, 0x26);
			SCCB_WR_Reg(0x60, 0x80);
			SCCB_WR_Reg(0x61, 0x80);
			break;		
		case 3://偏红
			SCCB_WR_Reg(0xa6, 0x1e);
			SCCB_WR_Reg(0x60, 0x80);
			SCCB_WR_Reg(0x61, 0xc0);		
			break;
		case 4://偏绿
			SCCB_WR_Reg(0xa6, 0x1e);
			SCCB_WR_Reg(0x60, 0x60);
			SCCB_WR_Reg(0x61, 0x60);		
			break;
		case 5://偏蓝
			SCCB_WR_Reg(0xa6, 0x1e);
			SCCB_WR_Reg(0x60, 0xa0);
			SCCB_WR_Reg(0x61, 0x40);	
			break;
		case 6://复古
			SCCB_WR_Reg(0xa6, 0x1e);
			SCCB_WR_Reg(0x60, 0x40);
			SCCB_WR_Reg(0x61, 0xa0);
			break;	
	}
}	
//设置图像输出窗口
//width:输出图像宽度,<=320
//height:输出图像高度,<=240
//mode:0,QVGA输出模式;1,VGA输出模式
//QVGA模式可视范围广但近物不是很清晰,VGA模式可视范围小近物清晰
void OV7725_Window_Set(u16 width,u16 height,u8 mode)
{
    
    
	u8 raw,temp;
	u16 sx,sy;	
	if(mode)
	{
    
    
		sx=(640-width)/2;
		sy=(480-height)/2;
		SCCB_WR_Reg(COM7,0x06);		//设置为VGA模式
		SCCB_WR_Reg(HSTART,0x23); 	//水平起始位置
		SCCB_WR_Reg(HSIZE,0xA0); 	//水平尺寸
		SCCB_WR_Reg(VSTRT,0x07); 	//垂直起始位置
		SCCB_WR_Reg(VSIZE,0xF0); 	//垂直尺寸
		SCCB_WR_Reg(HREF,0x00);
		SCCB_WR_Reg(HOutSize,0xA0); //输出尺寸
		SCCB_WR_Reg(VOutSize,0xF0); //输出尺寸
	}else
	{
    
    
		sx=(320-width)/2;
		sy=(240-height)/2;
		SCCB_WR_Reg(COM7,0x46);		//设置为QVGA模式
		SCCB_WR_Reg(HSTART,0x3f); 	//水平起始位置
		SCCB_WR_Reg(HSIZE, 0x50); 	//水平尺寸
		SCCB_WR_Reg(VSTRT, 0x03); 	//垂直起始位置
		SCCB_WR_Reg(VSIZE, 0x78); 	//垂直尺寸
		SCCB_WR_Reg(HREF,  0x00);
		SCCB_WR_Reg(HOutSize,0x50);	//输出尺寸
		SCCB_WR_Reg(VOutSize,0x78); //输出尺寸
	}
	raw=SCCB_RD_Reg(HSTART);
	temp=raw+(sx>>2);//sx高8位存在HSTART,低2位存在HREF[5:4]
	SCCB_WR_Reg(HSTART,temp);
	SCCB_WR_Reg(HSIZE,width>>2);//width高8位存在HSIZE,低2位存在HREF[1:0]
	
	raw=SCCB_RD_Reg(VSTRT);
	temp=raw+(sy>>1);//sy高8位存在VSTRT,低1位存在HREF[6]
	SCCB_WR_Reg(VSTRT,temp);
	SCCB_WR_Reg(VSIZE,height>>1);//height高8位存在VSIZE,低1位存在HREF[2]
	
	raw=SCCB_RD_Reg(HREF);
	temp=((sy&0x01)<<6)|((sx&0x03)<<4)|((height&0x01)<<2)|(width&0x03)|raw;
	SCCB_WR_Reg(HREF,temp);
	
	SCCB_WR_Reg(HOutSize,width>>2);
	SCCB_WR_Reg(VOutSize,height>>1);
	
	SCCB_RD_Reg(EXHCH);	
	temp = (raw|(width&0x03)|((height&0x01)<<2));	
	SCCB_WR_Reg(EXHCH,temp);	
}

4.3 ESP8266核心代码

#include "esp8266.h"
u8 ESP8266_IP_ADDR[16]; //255.255.255.255
u8 ESP8266_MAC_ADDR[18]; //硬件地址
/*
函数功能: ESP8266命令发送函数
函数返回值:0表示成功  1表示失败
*/
u8 ESP8266_SendCmd(char *cmd)
{
    
    
    u8 i,j;
    for(i=0;i<10;i++) //检测的次数--发送指令的次数
    {
    
    
        USARTx_StringSend(USART3,cmd);
        for(j=0;j<5;j++) //等待的时间
        {
    
    
            delay_ms(50);
            if(USART3_RX_FLAG)
            {
    
    
                USART3_RX_BUFFER[USART3_RX_CNT]='\0';
                USART3_RX_FLAG=0;
                USART3_RX_CNT=0;
                if(strstr((char*)USART3_RX_BUFFER,"OK"))
                {
    
    
                    return 0;
                }
            }
        }
    }
    return 1;
}

/*
函数功能: ESP8266硬件初始化检测函数
函数返回值:0表示成功  1表示失败
*/
u8 ESP8266_Init(void)
{
    
    
    USARTx_StringSend(USART3,"+++");
    delay_ms(50);
    return ESP8266_SendCmd("AT\r\n");
}

/*
函数功能: 一键配置WIFI为AP+TCP服务器模式
函数参数:
char *ssid  创建的热点名称
char *pass  创建的热点密码 (最少8位)
u16 port    创建的服务器端口号
函数返回值: 0表示成功 其他值表示对应错误值
*/
u8 ESP8266_AP_TCP_Server_Mode(char *ssid,char *pass,u16 port)
{
    
    
    char *p;
    u8 i;
    char ESP8266_SendCMD[100]; //组合发送过程中的命令
    /*1. 测试硬件*/
    if(ESP8266_SendCmd("AT\r\n"))return 1;
    /*2. 关闭回显*/
    if(ESP8266_SendCmd("ATE0\r\n"))return 2;
    /*3. 设置WIFI模式*/
    if(ESP8266_SendCmd("AT+CWMODE=2\r\n"))return 3;
    /*4. 复位*/
    ESP8266_SendCmd("AT+RST\r\n");
    delay_ms(1000);
    delay_ms(1000);
    delay_ms(1000);
    /*5. 关闭回显*/
    if(ESP8266_SendCmd("ATE0\r\n"))return 5;
    /*6. 设置WIFI的AP模式参数*/
    sprintf(ESP8266_SendCMD,"AT+CWSAP=\"%s\",\"%s\",1,4\r\n",ssid,pass);
    if(ESP8266_SendCmd(ESP8266_SendCMD))return 6;
    /*7. 开启多连接*/
    if(ESP8266_SendCmd("AT+CIPMUX=1\r\n"))return 7;
    /*8. 设置服务器端口号*/
    sprintf(ESP8266_SendCMD,"AT+CIPSERVER=1,%d\r\n",port);
    if(ESP8266_SendCmd(ESP8266_SendCMD))return 8;
    /*9. 查询本地IP地址*/
    if(ESP8266_SendCmd("AT+CIFSR\r\n"))return 9;
    //提取IP地址
    p=strstr((char*)USART3_RX_BUFFER,"APIP");
    if(p)
    {
    
    
        p+=6;
        for(i=0;*p!='"';i++)
        {
    
    
            ESP8266_IP_ADDR[i]=*p++;
        }
        ESP8266_IP_ADDR[i]='\0';
    }
    //提取MAC地址
    p=strstr((char*)USART3_RX_BUFFER,"APMAC");
    if(p)
    {
    
    
        p+=7;
        for(i=0;*p!='"';i++)
        {
    
    
            ESP8266_MAC_ADDR[i]=*p++;
        }
        ESP8266_MAC_ADDR[i]='\0';
    }
    
    //打印总体信息
    printf("当前WIFI模式:AP+TCP服务器\r\n");
    printf("当前WIFI热点名称:%s\r\n",ssid);
    printf("当前WIFI热点密码:%s\r\n",pass);
    printf("当前TCP服务器端口号:%d\r\n",port);
    printf("当前TCP服务器IP地址:%s\r\n",ESP8266_IP_ADDR);
    printf("当前TCP服务器MAC地址:%s\r\n",ESP8266_MAC_ADDR);
    return 0;
}

/*
函数功能: TCP服务器模式下的发送函数
发送指令: 
*/
u8 ESP8266_ServerSendData(u8 id,u8 *data,u16 len)
{
    
    
    u8 i,j,n;
    char ESP8266_SendCMD[100]; //组合发送过程中的命令
    for(i=0;i<10;i++)
    {
    
    
        sprintf(ESP8266_SendCMD,"AT+CIPSEND=%d,%d\r\n",id,len);
        USARTx_StringSend(USART3,ESP8266_SendCMD);
        for(j=0;j<10;j++)
        {
    
    
            delay_ms(50);
            if(USART3_RX_FLAG)
            {
    
    
                USART3_RX_BUFFER[USART3_RX_CNT]='\0';
                USART3_RX_FLAG=0;
                USART3_RX_CNT=0;
                if(strstr((char*)USART3_RX_BUFFER,">"))
                {
    
    
                    //继续发送数据
                    USARTx_DataSend(USART3,data,len);
                    //等待数据发送成功
                    for(n=0;n<200;n++)
                    {
    
    
                        delay_ms(50);
                        if(USART3_RX_FLAG)
                        {
    
    
                            USART3_RX_BUFFER[USART3_RX_CNT]='\0';
                            USART3_RX_FLAG=0;
                            USART3_RX_CNT=0;
                            if(strstr((char*)USART3_RX_BUFFER,"SEND OK"))
                            {
    
    
                                return 0;
                            }
                         }            
                    }   
                }
            }
        }
    }
    return 1;
}

4.4 main函数逻辑代码

#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "led.h"
#include "ff.h"
#include "timer.h"
#include "dht11.h"
#include "rtc.h"
#include "esp8266.h"
#include "ov7725.h"
#include "bmp.h"
#include "sdcard.h"	
#include <string.h>
#include <stdlib.h>

/*
硬件连接:

(1) ESP8266 WIFI
PB10--->ESP8266-RX
PB11--->ESP8266-TX
3.3v--->VCC
GND---->GND


(2) DHT11 温湿度传感器
PA5 ---->DHT11-OUT
3.3v---->VCC
GND----->GND


(3) SD卡接线
PC11  片选 SDCardCS
PC12  时钟 SDCardSCLK
PD2   输出 SPI_MOSI--主机输出从机输入
PC8   输入 SPI_MISO--主机输入从机输出


(4) OV7725摄像头接线

OV7725_VSYNC----PA8         //同步信号检测IO
OV7725_WRST-----PD6	        //写指针复位
OV7725_WREN-----PB3		    //写入FIFO使能
OV7725_RCK------PB4         //读数据时钟
OV7725_RRST-----PG14  		//读指针复位
OV7725_CS-------PG15 		//片选信号(OE)
SCCB_SCL--------PD3	 	    //SCL
SCCB_SDA--------PG13	    //SDA	 
PC0~7 ----------输入    


(5) 报警灯、车库灯接线



(6) 红外测距模块接线


(7) 板载LED灯
硬件连接: PB5 PE5
PF11--照明灯


(8) 板载按键
K1: PA0 
K2: PE3 
K3: PE4


*/

FATFS FatFs;
u8 DHT11_temp;  //温度
u8 DHT11_humi;  //湿度
u32 length_cm;  //距离长度
u32 warning_length_cm=20; //报警距离
u8 photograph_flag=0; //拍照标志  1需要拍照  0不需要拍照
u16 color_line_rgb[320]; //存放一行的数据

//数据发送缓冲区
char esp8266_send_buff[200];


//RTC时间更新
void rtc_time_update(char *buff)
{
    
    
    char *time;
    /*判断是否收到客户端发来的数据  */
    char *p=strstr((char*)buff,"+IPD");
    if(p!=NULL) //正常数据格式: +IPD,0,7:LED1_ON    +IPD,0表示第0个客户端   7:LED1_ON表示数据长度与数据
    {
    
    
            /*解析上位机发来的数据*/
            p=strstr((char*)buff,":");
            if(p!=NULL)
            {
    
    
                    p+=1; //向后偏移1个字节
                    if(*p=='*')  //设置RTC时间
                    {
    
    
                            p+=1; //向后偏移,指向正确的时间
                            time=p;
                            calendar.w_year=(time[0]-48)*1000+(time[1]-48)*100+(time[2]-48)*10+(time[3]-48)*1;
                            calendar.w_month=(time[4]-48)*10+(time[5]-48)*1;
                            calendar.w_date=(time[6]-48)*10+(time[7]-48)*1;
                            calendar.hour=(time[8]-48)*10+(time[9]-48)*1;
                            calendar.min=(time[10]-48)*10+(time[11]-48)*1;
                            calendar.sec=(time[12]-48)*10+(time[13]-48)*1;
                           
                        RTC_Set(calendar.w_year,
                                    calendar.w_month,
                        calendar.w_date,
                        calendar.hour,
                        calendar.min,
                        calendar.sec);
                        
                        printf("时间设置成功:%s\r\n",buff);
                    }
            }
    }
}

extern u8 ov_sta;

FIL file;

//更新LCD显示(OV7725)
void OV7725_camera_refresh(void)
{
    
    
	u32 i,j;
 	u16 color;	 
	if(ov_sta)//有帧中断更新
	{
    
    
        if(photograph_flag)
        {
    
    
             //获取照片名称
            sprintf(esp8266_send_buff,"0:%d-%d-%d-%d-%d-%d.bmp",
                calendar.w_year,
                calendar.w_month,
                calendar.w_date,
                calendar.hour,
                calendar.min,
                calendar.sec);
            printf("拍照的照片名称:%s\r\n",esp8266_send_buff);
            
            file=photograph_open((u8*)esp8266_send_buff,320,240);
        }
 
		OV7725_RRST=0;				//开始复位读指针 
		OV7725_RCK_L;
		OV7725_RCK_H;
		OV7725_RCK_L;
		OV7725_RRST=1;				//复位读指针结束 
		OV7725_RCK_H; 
		for(i=0;i<240;i++)
		{
    
    
			for(j=0;j<320;j++)
			{
    
    
				OV7725_RCK_L;
				color=GPIOC->IDR&0XFF;	//读数据
				OV7725_RCK_H; 
				color<<=8;  
				OV7725_RCK_L;
				color|=GPIOC->IDR&0XFF;	//读数据
				OV7725_RCK_H; 
                color_line_rgb[j]=color; //保存颜色数据
			}
            
            //如果需要拍照
            if(photograph_flag)
            {
    
    
               photograph_write(file,color_line_rgb);  
            }
		}
        
        //关闭文件
        if(photograph_flag)
        {
    
    
            photograph_close(file);     
        }
        
 		ov_sta=0;					//清零帧中断标记
        photograph_flag=0;  //清除标志
	} 
}


/*
功  能:外部中断初始化配置
参  数:
u8 u8 GPIOx:那一个GPIO口
u8 IRQLine :中断线
u8 RF_TSR  :边沿触发方式       
*/

//以下定义外部中断触发方式
#define EXTI_RTSRFTSR 2 //双
#define EXTI_RTSR 1     //上升
#define EXTI_FTSR 0     //下降

//定义外部中断的IO口编号
#define EXTI_GPIOA 0
#define EXTI_GPIOB 1
#define EXTI_GPIOC 2
#define EXTI_GPIOD 3
#define EXTI_GPIOE 4
#define EXTI_GPIOF 5
#define EXTI_GPIOG 6

void ExtiInitConfig(u8 GPIOx,u8 IRQLine,u8 RF_TSR)
{
    
    
	  u8 exticnt=IRQLine/4;  //得到数组的下标
	  u8 extiline=(IRQLine%4)*4;  //得到向左移位的位置
	
	  /*1. 开放来自IRQLine线上的中断请求;*/
	  EXTI->IMR|=1<<IRQLine;
	
	  /*2. 配置边沿触发方式*/
	  if(RF_TSR==EXTI_RTSR)EXTI->RTSR|=1<<IRQLine; //上升
	  if(RF_TSR==EXTI_FTSR)EXTI->FTSR|=1<<IRQLine; //下降
	  if(RF_TSR==EXTI_RTSRFTSR){
    
    EXTI->RTSR|=1<<IRQLine; EXTI->FTSR|=1<<IRQLine;}
	
	  /*3. 开启AFIO时钟*/
	  RCC->APB2ENR|=1<<0;
	  
	  /*4. 配置外部中断寄存器*/
	  AFIO->EXTICR[exticnt]&=~(0xf<<extiline); 	
	  AFIO->EXTICR[exticnt]|=GPIOx<<extiline;    
		
		if(IRQLine==0)STM32_SetPriority(EXTI0_IRQn,1,1);	
		else if(IRQLine==1)STM32_SetPriority(EXTI0_IRQn,1,1);	
		else if(IRQLine==2)STM32_SetPriority(EXTI0_IRQn,1,1);	
		else if(IRQLine==3)STM32_SetPriority(EXTI0_IRQn,1,1);	
		else if(IRQLine==4)STM32_SetPriority(EXTI0_IRQn,1,1);	
		else if(IRQLine>4 && IRQLine<10)STM32_SetPriority(EXTI9_5_IRQn,1,1);	
		else if(IRQLine>9 && IRQLine<16)STM32_SetPriority(EXTI15_10_IRQn,1,1);	
}


u8 ov_sta;	//帧中断标记
 //外部中断5~9服务程序
void EXTI9_5_IRQHandler(void)
{
    
    		 		
	if(EXTI->PR&(1<<8))		//是8线的中断
	{
    
          
		if(ov_sta==0)
		{
    
    
			OV7725_WRST=0;	//复位写指针		
			OV7725_RCK_L;
			OV7725_RCK_H;  		 
			OV7725_WRST=1;	
			OV7725_WREN=1;	//允许写入FIFO 	 
			ov_sta++;		//帧中断加1 
		}else OV7725_WREN=0;//禁止写入FIFO 	 
	}
	EXTI->PR=1<<8;			//清除LINE8上的中断标志位						  
} 


extern u8 XKC_KL200_BUFF[20];
extern u8 XKC_KL200_CNT;
extern u8 XKC_KL200_RX_FLAG;
extern u8 XKC_KL200_RX_OK_FLAG;

int main()
{
    
    
   u32 time_cnt=0;
   u8 key;
   u8 XKC_KL200_START_FLAG=0;
   LED_Init();
   KEY_Init();
   USART1_Init(115200);
   USART3_Init(115200);   //串口-WIFI
   USART2_Init(115200);   //红外线测距
   TIMER3_Init(72,20000); //超时时间20ms
    
   DHT11_Init(); //初始化DHT11

   if(SDCardDeviceInit())//检测不到SD卡
   {
    
    
      printf("SDCard_DeviceInit 错误.\r\n");
   }
   else
   {
    
    
      printf("SDCard_DeviceInit 成功.\r\n");
   }
   
   f_mount(&FatFs, "0:", 0);   //注册工作区并初始化SD卡

   printf("正在初始化WIFI请稍等.\r\n");
   
   //初始化WIFI硬件
   if(ESP8266_Init())
   {
    
    
       printf("WIFI硬件错误.\n");
   }
   else
   {
    
    
      //配置WIFI的模式
      printf("WIFI配置状态:%d\n",ESP8266_AP_TCP_Server_Mode("esp8266_666","12345678",8089));
   }
   
   
    //摄像头初始
    if(OV7725_Init()!=0)
    {
    
    
       printf("OV7725摄像头初始失败.\r\n");
    }
    else
    {
    
    
        printf("OV7725摄像头初始成功.\r\n");
    }
	OV7725_CS=0;
	ExtiInitConfig(EXTI_GPIOA,8,EXTI_RTSR);//外部中断8  上升沿触发

    RTC_Init();//RTC初始化,一定要初始化成功
   
//    while(1)
//    {
    
    
//          //扫描按键
//        key=KEY_Scan(0);
//        
//        //
//        if(key)
//        {
    
    
//            printf("按键%d按下.\r\n",key);
//            //
//           // XKC_KL200_START_FLAG=1;
//            //LED3=1;
//            //length_cm=666;
//        }
//       
//    }
//    

   while(1)
   {
    
       
        //ESP8266 WIFI 返回的数据
        if(USART3_RX_FLAG)
        {
    
    
            USART3_RX_BUFFER[USART3_RX_CNT]='\0';
            printf("%s",USART3_RX_BUFFER);

            //解析WIFI返回的数据
            //如果是设置报警距离. length_cm  
            if(strstr((char*)USART3_RX_BUFFER,":#"))
            {
    
    
                char *p=strstr((char*)USART3_RX_BUFFER,":#");
                
                //设置报警距离
                warning_length_cm=atoi(p+2);
                
                printf("报警距离设置成功:%s,%d\r\n",p+2,warning_length_cm);
            }
            
            //如果是校准RTC时间 +IPD,0,15:*20220304220552
            else if(strstr((char*)USART3_RX_BUFFER,":*"))
            {
    
    
                 printf("校准时间.\r\n");
                 rtc_time_update((char*)USART3_RX_BUFFER);
            }
   

            USART3_RX_CNT=0;
            USART3_RX_FLAG=0;
        }
        
        //扫描按键
        key=KEY_Scan(0);
        if(key==2)
        {
    
    
            printf("按键%d按下.启动系统\r\n",key);
           
            //启动系统,关闭照明灯
            XKC_KL200_START_FLAG=1;
            LED3=1;
            length_cm=666;
        }
        else  if(key==3)
        {
    
    
            //关闭系统,开启照明灯
            printf("按键%d按下.关闭系统\r\n",key);
            //
            XKC_KL200_START_FLAG=0;
            LED3=0;
        }
       
       //得到红外测距的距离
       if(XKC_KL200_RX_OK_FLAG && XKC_KL200_START_FLAG)
       {
    
    
            //开始计算距离
            length_cm=(XKC_KL200_BUFF[5]<<8|XKC_KL200_BUFF[6])/10.0;  
            //清除接收标志
            XKC_KL200_RX_FLAG=0;
            XKC_KL200_CNT=0;
            XKC_KL200_RX_OK_FLAG=0;
       }
       
        //判断距离,检测是否有人闯入
        if(length_cm<warning_length_cm)
        {
    
    
            //启动拍照
            photograph_flag=1;
        }

        //轮询时间
        time_cnt++;
        delay_ms(10);
        if(time_cnt>=50)
        {
    
    
            //读取温湿度数据
            DHT11_Read_Data(&DHT11_temp,&DHT11_humi);
            printf("温度:%d,湿度:%d\r\n",DHT11_temp,DHT11_humi);
            //拼接数据格式
            sprintf(esp8266_send_buff,"$update,%d,%d,%d",DHT11_temp,DHT11_humi,length_cm);
            //发送给上位机
            ESP8266_ServerSendData(0,(u8*)esp8266_send_buff,strlen((char*)esp8266_send_buff));
            printf("发送:%s\r\n",esp8266_send_buff);
            
            time_cnt=0;
            LED1=!LED1;
            LED2=!LED2;
        }
   }
}

猜你喜欢

转载自blog.csdn.net/xiaolong1126626497/article/details/125798033