主頁 > 移動端開發 > 基于STM32F103的智能門鎖系統

基于STM32F103的智能門鎖系統

2021-10-18 14:45:59 移動端開發

基于STM32F103的智能門鎖系統

直接說明實作了什么效果
1 指紋解鎖(基于AS608)
2 RFID解鎖(基于RC522)
3 密碼解鎖 (基于LCD電容屏觸摸控制)
4 藍牙解鎖 (基于HC-06)
5 后臺服務器管理開鎖資訊(基于ESP8266)
6 APP集成藍牙功能、門鎖開鎖資訊
7 管理員密碼修改開鎖密碼
8 管理員添加指紋洗掉指紋
9 實時同步網路時間(基于SIM800C)
10 監控開鎖時間并手機發送短信或打電話預警開鎖例外(基于SIM800C)


其他用到的模塊或硬體:28BYJ-48電機、IIC通信OLED螢屏

實作的效果如視頻展示
效果視頻

注:這是大一利用暑假寫的小demo,剪視頻的時候比較亂,可以將就食用 哈哈哈

文章目錄

  • 基于STM32F103的智能門鎖系統
  • 前言
  • 一、AS608
  • 二、RFID-RC522模塊
  • 三、LCD模塊以及觸摸功能講解
  • 四、基于HC-06的藍牙解鎖
  • 五、后臺服務器管理開鎖資訊(基于ESP8266)
  • 六、APP集成藍牙功能、門鎖開鎖資訊
  • 七、其他綜合功能(基于SIM800C)
  • 總結


前言

此demo運用的多種模塊,文中會對各個模塊進行較為詳細的講解,覺得有不足不對之后文末還將整理各個模塊的官方或其他資料分享出來,此demo的主要難度就是各個模塊之間的融合和資訊上傳,前后端整合


下面依次按照每個模塊進行分析

接下來,本文會很長很長很長,挑自己需要的看即可,主要是要看這個系統是怎么實作的,

一、AS608

看一眼長啥樣子~
AS608模塊

AS608 指紋識別模塊主要是指采用了杭州晟元芯片技術有限公司(Synochip)的 AS608 指紋識別芯片 而做成的指紋模塊,模塊廠商只是基于該芯片設計外圍電路,集成一個可供2次開發的指紋模塊;所以,只要是基于AS608芯片的指紋模塊,其控制電路及控制協議幾乎是一樣的,只是不同廠家的性能不同而已,(其實玩嵌入式的都知道,同種型別的模塊代碼都大差不差啦
技術指標
玩一個模塊最先的必須先看各種技術指標,如上電電壓,默認通訊設定,如波特率等,這一步必須要做,不然你就很有可能會了它(騷了它~)
呃…想表達的意思就是,看完指標我們才能更快的入手這個模塊啊,對后續莫名其妙 的bug也能更快的排查,(其實根本沒有那么多所謂莫名其妙的bug啦,肯定是你哪里的指標沒看,或接錯或代碼邏輯錯,科學都是很嚴謹的!!)
技術指標看完是不是就想上電啦?別急,先看看接線方式
AS608接線方式

  1. 采用8pin方式,其中有兩個腳需要上電3.3V,分別是Vi和Vt(記得別上5V!!!騷了它騷了它~ )
    Vi上電是給整個模塊供電,而Vt上電是給采集指紋的那塊觸摸感應螢屏上電,缺一不可,
  2. Tx和Rx看你用的哪個串口對應接,Rx、Tx反接記得檢查,我用的是STM32F103ZET6,開發板為正點原子精英板,接的串口3,即Rx接PA3,Tx接PA2,波特率可以自己設定,默認波特率57600
  3. WAK腳接上一個可用于輸入輸出的ADC腳,此處接的PA6,接上此腳的原因是為了能讓AS608模塊收到相關的指紋命令后,做對應的輸出,(所以要用輸入輸出捕獲呀
  4. 正常接GND即可
    接線圖
    對于WAK腳,接不接看你要什么效果,一般脫機(PC)做專案要接上,

關于用PC端串口助手直接連接AS608進行測驗的問題,文章末尾有資料包可自行查看,此處只講實際運用

附上代碼和個人理解

//與AS608握手 PS_HandShake
//引數: PS_Addr地址指標
//說明: 模塊返新地址(正確地址)	
u8 PS_HandShake(u32 *PS_Addr)
{
	SendHead();    //就是一些包頭
	SendAddr();		//發送模塊地址
	MYUSART_SendData(0X01);  //內部呼叫了串口,發送一個位元組,此處用于校驗和
	MYUSART_SendData(0X00);
	MYUSART_SendData(0X00);	
	delay_ms(200);
	if(USART2_RX_STA&0X8000)//接收到資料
	{		
		if(//判斷是不是模塊回傳的應答包				
					USART2_RX_BUF[0]==0XEF
				&&USART2_RX_BUF[1]==0X01
				&&USART2_RX_BUF[6]==0X07
			)
			{
				*PS_Addr=(USART2_RX_BUF[2]<<24) + (USART2_RX_BUF[3]<<16)
								+(USART2_RX_BUF[4]<<8) + (USART2_RX_BUF[5]);
				USART2_RX_STA=0;
				return 0;
			}
		USART2_RX_STA=0;					
	}
	return 1;		
}
/*
代碼塊主要就是看回傳值,對于地址、校驗這些知道它在干什么即可,學會看檔案查資料很重要
*/

模塊初始化代碼

u32 AS608Addr = 0XFFFFFFFF; //默認

//初始化PA6為下拉輸入		    
//讀摸出感應狀態(觸摸感應時輸出高電平信號)
void PS_StaGPIO_Init(void)
{   
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA時鐘
  //初始化讀狀態引腳GPIOA
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//輸入下拉模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO	
}
// 對于官方的代碼,建議不是很熟悉開發的不要改動配置

對于一個復雜模塊的代碼,其實不需要懂太多,看一下初始化,必要的功能函式,其他的函式只要知道它是干什么的,運用它即可,

貼上一兩個重要的功能函式

//判斷中斷接收的陣列有沒有應答包
//waittime為等待中斷接收資料的時間(單位1ms)
//回傳值:資料包首地址
static u8 *JudgeStr(u16 waittime)
{
	char *data;
	u8 str[8];
	str[0]=0xef;str[1]=0x01;str[2]=AS608Addr>>24;
	str[3]=AS608Addr>>16;str[4]=AS608Addr>>8;
	str[5]=AS608Addr;str[6]=0x07;str[7]='\0';
	USART2_RX_STA=0;
	while(--waittime)
	{
		delay_ms(1);
		if(USART2_RX_STA&0X8000)//接收到一次資料
		{
			USART2_RX_STA=0;
			data=strstr((const char*)USART2_RX_BUF,(const char*)str);
			if(data)
				return (u8*)data;	
		}
	}
	return 0;
}
/*
這里就是設定一些地址,接收資料包之后檢查有無應答包
strstr函式常常用于一些需要指令的模塊,函式的功能是判斷引數str2是否包含在str1內,如果包涵就回傳包涵資料的首地址,否則回傳NULL,
用大白話說就是查包
查包是查應答包,包括指令的包頭、模塊地址、指令碼,
*/
//錄入影像 PS_GetImage
//功能:探測手指,探測到后錄入指紋影像存于ImageBuffer, 
//模塊回傳確認字
u8 PS_GetImage(void)
{
  u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包標識
	SendLength(0x03);
	Sendcmd(0x01);
  temp =  0x01+0x03+0x01;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;
}

整個開發流程大概遵循以下步驟:

初始化硬體
→檢查字庫
→是否觸摸校準(電阻屏)
→與 AS608模塊通訊
→通訊成功讀取模塊引數
→顯示模塊引數
→加載虛擬鍵盤
→while while 回圈獲取觸摸鍵值
→判斷鍵值進入錄指紋或刪流程
→判斷觸摸感應狀態
→進入刷指紋流程,

串口除錯AS608模塊,注意波特率,57600和115200看實際情況
串口除錯
驗證結果
在這里插入圖片描述

二、RFID-RC522模塊

看一眼長啥樣子~
RC522
該圖片是讀取卡
鑰匙扣
該圖片是卡鑰匙扣

RFID-RC522模塊是眾多射頻模塊中最常用的一種,由于模塊涉及的東西還是較為復雜的,本文就簡明扼要的挑選一些重要的特性進行介紹,
首先,卡本身是分扇區的,所謂分扇區,就是一片片的儲存資訊區域,且每個區域負責的資訊不同,若需要查看請去找模塊手冊,我們本文的目標是教會大家怎么使用,
其次,要想對資料塊進行操作,首先要看該資料塊的控制位是否允許對資料塊的操作,如果允許操作,再看需要驗證什么密碼,只有驗證密碼正確后才可以對該資料塊執行相應操作,看到這里就會明白為什么代碼里會有0XFF…的校驗了,
卡的作業原理就是卡本身內置天線且繞線為線圈的形式封裝在卡片內,作業的時候讀卡器會向M1卡發送電磁波,卡內的電路進行處理(此處有復雜的模電數電知識)然后由電子泵將產生的電荷進行儲存,隨后卡片就可以向讀卡器發送或接收資料,實作通信,
pcb描述

流程為:放卡鑰匙扣 ->  讀卡器天線接收到信號 -> 讀卡器內置電路處理信號并存盤好(此處看代碼命令是怎么操作的,我們的主要目標也在這里,你也可以讀取到了信號但不計入卡內部) -> 驗證或其他外圍操作(指的是你讀到了卡鑰匙扣的資訊后你想干嘛當然由你制定) 

貼一些功能函式并適當講解,完整工程文末會給出

/* 初始化函式 模塊是spi的初始化 */
//RFID_RC522 GPIO SPI初始化
//RFID_NSS(CS)   PB12
//RFID_RST       PC0
//RFID_CLK       PB13
//RFID_MOSI      PB15
//RFID_MISO      PB14
extern void open_close(void);
void RFIDGPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽輸出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;      //RFID_CS
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//拉高RFID_CS

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽輸出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//RFID_RST
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_0);//拉高
	
	spi2_init();    //SPI2初始化
	PcdReset();     //RC522初始化
	PcdAntennaOff();//關天線
	delay_ms(1);    
	PcdAntennaOn(); //開天線
	M500PcdConfigISOType('A');//針對IOS14443A型卡進行初始化
}
//功    能:復位RC522
//返    回: 成功回傳MI_OK
char PcdReset(void)
{   	
		RST_1;
		delay_us(10);
	  WriteRawRC(CommandReg,PCD_RESETPHASE);//啟動命令的執行-執行復位指令
		delay_us(1);

		WriteRawRC(ModeReg,0x3d);//0x29?//0x3d//和Mifare卡通訊,CRC初始值0x6363
		WriteRawRC(TReloadRegL,30);//timer=15ms//30
		WriteRawRC(TReloadRegH,0);
		WriteRawRC(TModeReg,0x8D);//Prescaler=3390
		WriteRawRC(TPrescalerReg,0x3E);
		WriteRawRC(TxAskReg,0x40);		//forced to 100%ASK	 天線驅動
	  
		PcdAntennaOn(); 
		return MI_OK;
}
//初始化值塊 查找的意思
void CZ(void)
{
  Block=4;//將1扇區的資料塊0初始化為值塊 初始值為0
	//尋卡、防沖突、選卡
	if(Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID)==MI_OK)
	{
		//用修改后的密碼A驗證卡片
		if(PcdAuthState(PICC_AUTHENT1A,Block,Modify_Key,Card_ID)==MI_OK)
		{
			//將當前操作的塊列印出來
			USART_SendData(USART1,table[Block/10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,table[Block%10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,' ');
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			usart1_sendstring("PcdAuthState_OK\r\n");
			//按照值塊格式寫入
			if(PcdWrite(Block,DefaultValue)==MI_OK)
			{
				if(PcdRead(Block,Data_Buffer)==MI_OK)
				{
					Printing(Data_Buffer,16);//將值塊列印到串口
					usart1_sendstring("Init_OK\r\n");//修改密碼成功
					usart1_sendstring("\r\n");
					BEEP_SET;
					delay_ms(500);
					BEEP_CLR;	
				}					
			}
		}
	}		
}
//這里的代碼大概就是開發RC522功能的步驟了,其他的可以寫比如錢包的加錢,減錢功能函式等
//減值(扣款)
void Decrement(void)
{
	Block=4;
	//尋卡、防沖突、選卡
	if(Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID)==MI_OK)
	{
		//用修改后的密碼A驗證卡片
		if(PcdAuthState(PICC_AUTHENT1A,Block,Modify_Key,Card_ID)==MI_OK)
		{
			//將當前操作的塊列印出來
			USART_SendData(USART1,table[Block/10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,table[Block%10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,' ');
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			usart1_sendstring("PcdAuthState_OK\r\n");
			
			//減前讀一遍錢包
			if(PcdRead(Block,Data_Buffer)==MI_OK)
			{
				Printing(Data_Buffer,16);
				
				if(Data_Buffer[0]<=0)//判斷是否還能再減 
				{
					BEEP_SET;delay_ms(50);BEEP_CLR;delay_ms(50);	
					BEEP_SET;delay_ms(50);BEEP_CLR;delay_ms(50);
					BEEP_SET;delay_ms(50);BEEP_CLR;//超值報警
					return;
				}
				//減值  
				if(PcdValue(PICC_DECREMENT,Block,value_Buf)==MI_OK)
				{
					if(PcdRead(Block,Data_Buffer)==MI_OK)
					{
						Printing(Data_Buffer,16);
						usart1_sendstring("-_OK\r\n");
						usart1_sendstring("\r\n");
						BEEP_SET;
						delay_ms(50);
						BEEP_CLR;	
					}			
				}
			}
		}
	}
}

整個工程下來,最重要的是怎么讀取到你的當前卡號問題,代碼中有個陣列尾Card_ID,里面存盤的就是讀取到的卡號,讀卡的操作也可以用電腦串口助手除錯,如圖所示(這不是賣廣告,測驗自帶的)
查卡id

三、LCD模塊以及觸摸功能講解

因為LCD模塊的代碼多且復雜,故這里也就將它分開講解并以實用為主,
首先因為LCD的型號也是多且雜的,先放上本文講解的版本
LCD螢屏
它的功能用大白話來說就是,顯示+輸入,解析度為240*320,此解析度也就是能加載一些較為模糊的圖片和弄好的UI布局,我了解過的一個是emwin圖形界面,可以設計很多的“美麗”布局,見仁見智的美麗,
LCD螢屏另一個重要的用途就是用作觸摸輸入了,市面上又會分電阻屏和電容屏的區別,TFT只是一種液晶螢屏的材料,電容屏是靠電流熱感應作業,只能用手指操作, 電阻屏靠壓力作業,任何東西壓在上面都有反應·所以用指甲或手寫筆都可以,圖中的是電阻屏
上代碼先看看是怎么驅動的

#include "lcd.h"				//匯入頭檔案
LCD_Init();					//LCD初始化
tp_dev.init();			//初始化觸摸屏 

以上三個就是其他花里胡哨的功能實作的前提下必須要寫的,看起來低級,但往往有人功能寫得花里胡哨卻忘記初始化一個模塊或者把它注釋了之類的,那么就導致了不能使用LCD的功能,
其他形如一些指定的標準庫函式就直接拿來用就好了

POINT_COLOR=BLUE; //字體顏色為藍色
LCD_Clear(WHITE); //清屏
Show_Str_Mid(0,40,“LCD模塊正常!”,16,240); //形如這樣子的顯示,前提是檢測好字庫

抽取一段實作觸摸的代碼進行說明

//得到觸摸屏的輸入
//x,y:鍵盤坐標
//回傳值:(1~15,對應按鍵表)
u8 AS608_get_keynum(u16 x,u16 y)
{
	u16 i,j;
	static u8 key_x=0;//0,沒有任何按鍵按下
	u8 key=0;
	tp_dev.scan(0);		//設定掃描模式 		 
	if(tp_dev.sta&TP_PRES_DOWN)//觸摸屏被按下
	{	
		for(i=0;i<5;i++)		//以下的一大段就是計算被點擊的物理地址得到鍵值
		{
			for(j=0;j<3;j++)
			{
			 	if(tp_dev.x[0]<(x+j*80+80)&&tp_dev.x[0]>(x+j*80)&&tp_dev.y[0]<(y+i*30+30)&&tp_dev.y[0]>(y+i*30)) //就是根據LCD螢屏的大小計算  
				{	
					key=i*3+j+1;	 
					break;	 		   
				}
			}
			if(key)
			{	   
				if(key_x==key)key=0;
				else 
				{
					AS608_key_staset(x,y,key_x-1,0);
					key_x=key;
					AS608_key_staset(x,y,key_x-1,1);
				}
				break;
			}
	  }  
	}else if(key_x) 
	{
		AS608_key_staset(x,y,key_x-1,0);
		key_x=0;
	} 
	return key; //回傳的key是對應LCD顯示鍵盤上的數字
}
//觸摸按鍵掃描
//tp:0,螢屏坐標;1,物理坐標(校準等特殊場合用)
//回傳值:當前觸屏狀態.
//0,觸屏無觸摸;1,觸屏有觸摸
u8 TP_Scan(u8 tp)
{			   
	if(PEN==0)//有按鍵按下
	{
		if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//讀取物理坐標
		else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//讀取螢屏坐標
		{
	 		tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//將結果轉換為螢屏坐標
			tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff;  
	 	} 
		if((tp_dev.sta&TP_PRES_DOWN)==0)//之前沒有被按下
		{		 
			tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按鍵按下  
			tp_dev.x[4]=tp_dev.x[0];//記錄第一次按下時的坐標
			tp_dev.y[4]=tp_dev.y[0];  	   			 
		}			   
	}else
	{
		if(tp_dev.sta&TP_PRES_DOWN)//之前是被按下的
		{
			tp_dev.sta&=~(1<<7);//標記按鍵松開	
		}else//之前就沒有被按下
		{
			tp_dev.x[4]=0;
			tp_dev.y[4]=0;
			tp_dev.x[0]=0xffff;
			tp_dev.y[0]=0xffff;
		}	    
	}
	return tp_dev.sta&TP_PRES_DOWN;//回傳當前的觸屏狀態
}	  

小總結:對于LCD的使用,普通顯示直接呼叫庫函式即可,注意根據螢屏大小實作顯示;對于觸摸使用,觸摸不準確的時候可以進行觸摸屏校準
TP_Adjust(); //螢屏校準
TP_Save_Adjdata();//保存校準引數
觸摸的時候一是設定UI,二就是根據點擊的物理地址回傳UI的對應值
提供一個思路:輸入密碼的時候,可以根據點擊事件的發生,更換UI界面,實作如同中國農業銀行app輸入密碼的效果

思路代碼如下:

if(key_num>=4&&key_num<=13)//觸摸屏的1-9被按下,加載隨機虛擬鍵盤
		AS608_load_keyboard(0,170,(u8**)kbd_menu_random[temp_num]);//此處傳入的menu可以更換

附上一個設定UI的代碼

//加載按鍵界面(尺寸x,y為240*150)
//x,y:界面起始坐標(240*320解析度的時候,x必須為0)
void AS608_load_keyboard(u16 x,u16 y,u8 **kbtbl)
{
	u16 i;
	POINT_COLOR=RED;
	kbd_tb2=kbtbl;
	LCD_Fill(x,y,x+240,y+150,WHITE);
	/* 此處就是畫一些條條框框來放置相應的UI值 */
	LCD_DrawRectangle(x,y,x+240,y+150);	//畫豎線					   
	LCD_DrawRectangle(x+80,y,x+160,y+150);	 
	LCD_DrawRectangle(x,y+30,x+240,y+60);
	LCD_DrawRectangle(x,y+90,x+240,y+120);
	POINT_COLOR=BLUE;
	for(i=0;i<15;i++)//數字鍵盤
	{
		if(i==1)//按鍵表第2個‘:’不需要中間顯示
			Show_Str(x+(i%3)*80+2,y+7+30*(i/3),80,30,(u8*)kbd_tb2[i],16,0);	
		else
			Show_Str_Mid(x+(i%3)*80,y+7+30*(i/3),(u8*)kbd_tb2[i],16,80);
	} 
}

效果如下:
在這里插入圖片描述

四、基于HC-06的藍牙解鎖

此處放上HC-05和HC-06的圖片,第一張帶有小按鍵的是05,第二張不帶按鍵的是06
HC-05
HC-06
關于HC-05和HC-06的區別,在這里簡單描述,HC-05和HC-06都是主從一體的藍牙模塊,意思就是都能配置主從模式,不同的地方是HC-05的指令集比HC-06的指令集豐富,HC-05可以查詢模塊的主從狀態、PIN碼等等,HC-06的優勢就是更加的簡潔便用,其實兩個模塊用哪個都無所謂,因為這個專案只是使用的從機模式,簡單的收取一個位元組的命令即可,此專案用的是HC-06
指令集給出如下:
在這里插入圖片描述
關于驅動藍牙模塊的問題,其實就是串口通信,初始化一個串口,與藍牙模塊的波特率保持一致即可,此處用的是9600,簡單的理解,波特率越高,傳輸的速率就越快,但是丟包率不一定,丟包率有時候并不關波特率大小的事,是看具體兩個通信體之間的穩定性問題,此處不多作贅述,
STM32驅動藍牙模塊一般用串口1、2、3,但因為此專案工程量較大,用了過多的串口,故此處用的串口四
初始化代碼如下

void UART4_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;        

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE );
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE );

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //UART4 TX;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure); 
    
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //UART4 RX;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
GPIO_Init(GPIOC, &GPIO_InitStructure); 

USART_InitStructure.USART_BaudRate = 9600; //波特率為9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8 1 0初始化
USART_InitStructure.USART_StopBits = USART_StopBits_1; 
USART_InitStructure.USART_Parity = USART_Parity_No ; 
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_Init(UART4, &USART_InitStructure);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 

NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn; 
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);
USART_Cmd(UART4, ENABLE); 
}

藍牙處理部分,運用最簡單的處理一個buff位的思想

if(USART_GetITStatus(USART4, USART_IT_RXNE) != RESET)  //接收中斷(接收到的資料必須是0x0d 0x0a結尾)
		{
		Res =USART_ReceiveData(USART4);	//讀取接收到的資料
		//此處運用控制思想
		/*如
		if(Res==‘a’) LED0 = 0;
		else if(Res=='b') LED0 = 1;
		*/
		if((USART4_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART4_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART4_RX_STA=0;//接收錯誤,重新開始
				else USART4_RX_STA|=0x8000;	//接收完成了 
				}
			else //還沒收到0X0D
				{	
				if(Res==0x0d)USART4_RX_STA|=0x4000;
				else
					{
					USART4_RX_BUF[USART4_RX_STA&0X3FFF]=Res ;
					USART4_RX_STA++;
					if(USART4_RX_STA>(USART4_REC_LEN-1))USART4_RX_STA=0;//接收資料錯誤,重新開始接收	  
					}		 
				}
			}

五、后臺服務器管理開鎖資訊(基于ESP8266)

嵌入式最常用的WIFI通信模塊莫過于ESP8266了,ESP8266是上海樂鑫資訊科技設計的低功耗WiFi芯片,集成完整的TCP/IP協議堆疊和MCU,而ESP8266模塊是深圳安信可公司基于ESP8266芯片研發(增加必要外圍電路、串口flash、板載天線等)的串口WiFi模塊,成本低、使用簡便、功能強大,

玩單片機的應該都聽說過單片機就是一臺微型計算機?微型計算機?那不就是電腦嘛?電腦?那電腦豈能沒網?

其實單片機聯網也有以太網的形式連接,但是那也太笨重了吧,而小巧的ESP8266WiFi模塊通過串口AT指令與單片機通訊,實作串口透傳,非常好上手,

說了這么多,其實市面上有非常多種類的8266,本專案使用的是ATK-esp8266,可以直接插在開發板上使用,使用的是哪個類的8266無所謂,因為整個esp8266的協議都是一模一樣的,只要會了一個種類的,其他的自然就會了,

在這里插入圖片描述
在這里插入圖片描述
其實就是插上去了就直接能跑例程了,我就是因為方便 才選用的這個,
首先介紹ESP8266的三種模式,分別為

  1. WIFI STA
    STA站點,每一個連接到無線網路中的終端(如筆記本電腦、PDA及其它可以聯網的用戶設備)都可稱為一個站點,簡單的說就是手機熱點,想要連接一個路由器而不消耗流量
  2. WIFI AP
    AP,也就是無線接入點,是一個無線網路的創建者,是網路的中心節點,一般家庭或辦公室使用的無線路由器就一個AP,簡單的說就是路由器,等你的手機開啟WiFi并連接上去,
  3. WIFI STA+AP
    兩種模式的共存模式,(STA 模式)即可以通過路由器連接到
    互聯網,并通過互聯網控制設備; (AP模式)也可作為wifi熱點,其他wifi
    設備連接到模塊,這樣實作局域網和廣域網的無縫切換,方便操作,

因為ESP8266的模式也是AT指令的方式,也就是通過串口的方式進行設定,接下來簡單的介紹一些常用的AT指令和除錯的經驗,
基礎AT指令:

執行指令回應功能
ATOK測驗通信
AT+RSTOK重啟模塊
AT+RESTOREOK恢復出廠設定

串口設定
WIFI功能AT指令

執行指令回應功能
AT+CWMODE?+CWMODE:<mode> OK回傳當前模塊的模式
AT+CWJAP?+CWJAP:<ssid> OK回傳當前選擇的AP
AT+CIPSTA?+CIPSTA:<ip> OK設定模塊STA的IP地址
AT+CIPAP?+CIPAP:<ip> OK設定模塊AP的IP地址

TCP/IP相關AT指令
相關AT指令集

建立連接TCP
由于篇幅原因,只介紹大致的指令集,詳細的指令集文章末尾會給出用戶手冊

接下來梳理本專案實際應用ESP8266的程序,并分析怎么傳資料到后端

sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",ipbuf,(u8*)portnum);    //配置目標TCP服務器

挑出最重要的一句來講,ipbuf就是你后端服務器的ip地址,portnum就是后端服務器的埠號,只要這兩個寫對了,按照例程中的流程,走TCP三次握手連接之后,開啟透傳模式,就可以發送資料到服務器,

sprintf((char*)p,"ATK-8266%s測驗%02d\r\n",ATK_ESP8266_WORKMODE_TBL[netpro],t/10);//測驗資料

連接完畢之后就可以發送資料進行測驗了,如果沒有服務器的話,測驗的時候是有一個叫網路助手的軟體的,軟體會有ip地址和埠號,這個軟體是專門供測驗硬體網路模塊使用的,

由于ESP8266使用的是串口除錯,所以難免會用到sprintf的函式,關于這個函式,就是將后面的引數p2和p3發送格式化輸出到str所指向的字串,

//向ATK-ESP8266發送命令
//cmd:發送的命令字串
//ack:期待的應答結果,如果為空,則表示不需要等待應答
//waittime:等待時間(單位:10ms)
//回傳值:0,發送成功(得到了期待的應答結果)
//       1,發送失敗
u8 atk_8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
	u8 res=0; 
	USART3_RX_STA=0;
	u3_printf("%s\r\n",cmd);	//發送命令
	if(ack&&waittime)		//需要等待應答
	{
		while(--waittime)	//等待倒計時
		{
			delay_ms(10);
			if(USART3_RX_STA&0X8000)//接收到期待的應答結果
			{
				if(atk_8266_check_cmd(ack))
				{
					printf("ack:%s\r\n",(u8*)ack);
					break;//得到有效資料 
				}
					USART3_RX_STA=0;
			} 
		}
		if(waittime==0)res=1; 
	}
	return res;
} 
/*
* 此函式專門用來測驗AT命令,等待模塊回傳確認資訊
* 如發送AT,模塊回應OK,說明模塊作業正常
*/

對于往后端發送資料格式的問題,可以是json格式,也可以是其他格式,后端自己確定格式之后對資料包進行解包就行了,可以加一些幀頭幀尾作為校驗,包的格式可以參考網上的,如幀頭為0x55 0x55,作為檢測資料正確的標準,

六、APP集成藍牙功能、門鎖開鎖資訊

由于部分原因,導致APP連接后端部分的代碼遺失,但之前專案是能夠實作此功能的,此代碼部分是由另一個專案的安卓方向的同學幫助開發,如今只剩下搜索藍牙并連接藍牙的部分,
給出一部分的代碼

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_bluetooth_control_panel);
    SetSystemBar.setStatusBarColor(this, R.color.blue);
    SetSystemBar.setAndroidNativeLightStatusBar(this, false);

    Toolbar toolbar = findViewById(R.id.toolbar_BluetoothControlPanelActivity);
    viewPager = findViewById(R.id.viewPager_BluetoothControlPanelActivity);
    tabLayout = findViewById(R.id.tabLayout_BluetoothControlPanelActivity);

    toolbar.setTitle("控制面板");
    setSupportActionBar(toolbar);
    ActionBar actionBar = getSupportActionBar();
    assert actionBar != null;
    actionBar.setDisplayHomeAsUpEnabled(true);

    loadAnimationDialog = LoadAnimationDialog.showDialog(this,"連接設備中,請稍后...");
    // 獲取要連接的設備
    bluetoothDevice = getIntent().getParcelableExtra("bluetoothDevice");
    loadAnimationDialog.show();
    new Thread(new Runnable() {
        @Override
        public void run() {
            //停止發現設備,否則會減慢連接速度
            bluetoothAdapter.cancelDiscovery();
            try {
                //通過socket連接到藍牙設備,此呼叫將阻塞,直到成功或引發例外
                socket = bluetoothDevice.createRfcommSocketToServiceRecord(bluetoothDevice.getUuids()[0].getUuid());
                socket.connect();
            } catch (Exception e) {
                try {
                    socket.close();
                    loadAnimationDialog.cancel();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(BluetoothControlPanelActivity.this,"連接失敗,請重試!",Toast.LENGTH_SHORT).show();
                            finish();
                        }
                    });
                } catch (Exception ee) {
                    ee.printStackTrace();
                }
            }
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    loadAnimationDialog.cancel();
                    Toast.makeText(BluetoothControlPanelActivity.this,"連接成功!",Toast.LENGTH_SHORT).show();
                    List<Fragment> fragments = new ArrayList<>();
                    fragments.add(new ChatFragment());
                    fragments.add(new KeyboardFragment());
                    tabLayout.setTabTextColors(getResources().getColor(R.color.dark_grey),getResources().getColor(R.color.blue));
                    tabLayout.setupWithViewPager(viewPager);
                    BluetoothControlPanelActivity_ViewPagerAdapter viewPagerAdapter = new BluetoothControlPanelActivity_ViewPagerAdapter(getSupportFragmentManager(), fragments);
                    viewPager.setAdapter(viewPagerAdapter);
                }
            });
        }
    }).start();
}

完整代碼文章末尾給出

七、其他綜合功能(基于SIM800C)

SIM800C
SIM800C模塊是一個GSM/GPRS模塊,簡單來說就是電話卡模塊,可以實作的功能:低功耗語言通話、短信發送與接收、獲取網路時間、其他如http、tcp協議的網路通信等,
不要以為這是一個網路模塊,準確的來說這是一個基于通信基站(就是平時我們手機沒有信號都會怪的那個基站),是基于GSM全球移動通訊系統(Global System for Mobile Communications)升級過來的,GPRS是通用分組無線業務(General Packet Radio Service)的簡稱,
本專案使用此專案實作的功能包括:

  1. 短信通信
  2. 電話通知
  3. 獲取網路時間并顯示
    因為本專案用了ESP8266,所以就沒用上網路的功能,不要問為什么不整合到一起,問就是之前并不知道SIM模塊有網路功能!!!(理不直氣也壯)

接下來先介紹SIM800C大致是怎么使用的:
SIM800C其實也是一種使用串口命令的模塊,設定它可以直接用一些AT指令的形式,但由于SIM800C的AT指令集實在是太多太雜了,所以文中只能羅列一些我自以為常用的AT指令,全部的指令集文章末尾給出,

  1. AT+CPIN?
    該指令用于查詢SIM卡的狀態,主要是PIN碼,如果該指令回傳: +CPIN:READY,則
    表明SIM卡狀態正常,回傳其他值,則有可能是沒有SIM卡,在模塊出現問題的時候,-一
    定要先發送: AT+CPIN?, 查詢一下,看看是不是SIM卡和SIM卡座沒有接觸好?如果返
    回ERROR,則說明可能是SIM卡沒接觸好,用紗布擦一下SIM卡座和SIM卡的接觸焊
    盤,然后重裝SIM卡,重啟,一般就可以解決,
  2. AT+COPS?
    該指令用于查詢當前運營商,該指令只有在連上網路后,才回傳運營商,否則回傳空,
    如回傳: +COPS:0,0,“CHINA MOBILE”,表示當前選擇的運營商是中國移動,
  3. ATD+<號碼>
    我們將利用ATK-SIM800C模塊來撥打10086,并進行話費查詢,
    首先發送: ATE1,設定回顯,再發送: AT+COLP=1,設定被叫號碼顯示,
    電話測驗
    然后,我們發送: ATD10086;(注意完整命令是有;的,且要加回車換行)
    撥打10086, 在接通后,SIM800C模塊回傳:
    +COLP:1006129."”,此時,我們就可以聽到中國移動那熟悉的聲音了…
  4. 短信命令
    SIM800C是可以直接發送中英文短信的,作為China man,直接說明怎么發送中文的短信,
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述
    后續我會用代碼來更直觀的說明是如何使用的此功能,
  5. AT+CCLK?
    指令功能是獲取網路時間,回傳的是一串字符,包括狀態和實時的網路時間
    想要的時間資料需要自己寫解包的代碼,
    同樣的,后續我會用代碼來更直觀的說明是如何使用的此功能,

在此工程中,我是這樣來使用這個模塊的:
首先,初始化是

usart3_init(115200);		//此模塊用AT通信,并在此選用串口三

在專案的一開始,我的想法是,一個智能鎖放在門口,那么出門或者回家的時候是不是要看一眼時間呢?而單機的時間難免會有誤差,不久又得手動調整時間以保證時間的準確,那這還叫智能鎖???智能從何而來呢?所以我的第一步就是先利用SIM800C拿到實時網路時間,

update_time();   //更新時間
void update_time(void)			//更新網路時間
{
	u8 *p,*p1,*p2;
	u16 z1=0,z2=0,z3=0,z4=0,z5=0;
	u16 t,i;
	u16 *p6,*p7,*p3,*p4,*p5;
	u16 s=0;
	p=mymalloc(SRAMIN,50); //申請50個位元組的記憶體
	p6=mymalloc(SRAMIN,20); //申請50個位元組的記憶體
	p7=mymalloc(SRAMIN,20); //申請50個位元組的記憶體
	p3=mymalloc(SRAMIN,20); //申請50個位元組的記憶體
	p4=mymalloc(SRAMIN,20); //申請50個位元組的記憶體
	p5=mymalloc(SRAMIN,20); //申請50個位元組的記憶體
	POINT_COLOR=BLUE; 	
	USART3_RX_STA=0;
	if(sim800c_send_cmd("AT+CCLK?","+CCLK:",200)==0)    //查詢時間指令
	{ 
		p1=(u8*)strstr((const char*)(USART3_RX_BUF),"\"");//拿到時間
		p2=(u8*)strstr((const char*)(p1+1),":");
		p2[3]=0;//加入結束符
		sprintf((char*)p,"日期時間:%s",p1+1);
		//Show_Str(0,0,200,16,p,16,0);
		USART3_RX_STA=0;		
	}
	//其實這里的解包程序看不懂是正常的,你用串口助手看到SIM800C回傳的時間包是什么樣子的,即可看懂
	  //年
	for(i=9;i<11;i++)
    {
    if(p[i]>='0'&&p[i]<='9')
    {
    *(p6+s)=p[i]-48;
    s++;
    }
    }
    //月
    s=0;
    for(i=12;i<14;i++)
    {
    if(p[i]>='0'&&p[i]<='9')
    {
    *(p7+s)=p[i]-48;
    s++;
    }
    }
    //日
    s=0;
    for(i=15;i<17;i++)
    {
    if(p[i]>='0'&&p[i]<='9')
    {
    *(p3+s)=p[i]-48;
    s++;
    }
    }
    //小時
    s=0;
    for(i=18;i<20;i++)
    {
    if(p[i]>='0'&&p[i]<='9')
    {
    *(p4+s)=p[i]-48;
    s++;
    }
    }
    //分鐘
    s=0;
    for(i=21;i<23;i++)
    {
    if(p[i]>='0'&&p[i]<='9')
    {
    *(p5+s)=p[i]-48;
    s++;
    }
    }
		//賦值
    for(t=0;t<2;t++)
    {
    z1+=(*(p6+t))*pow(10,2-(t+1));//計算公式
		z2+=(*(p7+t))*pow(10,2-(t+1));//計算公式
		z3+=(*(p3+t))*pow(10,2-(t+1));//計算公式
		z4+=(*(p4+t))*pow(10,2-(t+1));//計算公式
		z5+=(*(p5+t))*pow(10,2-(t+1));//計算公式
    }
    //以上的計算公式,實際上是解包的程序,把模塊發回來的資料進行解包,拿到想要的時間資料,并回饋到RTC中
	RTC_Set(2020,z2,z3,z4,z5,30);  //RTC設定時間
	myfree(SRAMIN,p);
	myfree(SRAMIN,p6);
	myfree(SRAMIN,p7);
	myfree(SRAMIN,p3);
	myfree(SRAMIN,p4);
	myfree(SRAMIN,p5);
}

拿到時間之后,就是顯示到OLED上面了

//clendar就是RTC里面的結構體變數,因為之前已經用update_time();更新過了,所以直接顯示即可
		if(t!=calendar.sec)
		{
			t=calendar.sec;
			OLED_ShowNum(0,16,calendar.w_year,4,16);
			OLED_ShowString(36,16,"-",16);		//列印字串
			OLED_ShowNum(44,16,calendar.w_month,2,16);
			OLED_ShowString(62,16,"-",16);		//列印字串
			OLED_ShowNum(70,16,calendar.w_date,2,16);
			OLED_ShowString(0,0,"Time:",16);		//列印字串			
			OLED_Refresh_Gram();		//更新顯示到OLED 	
			switch(calendar.week)
			{
				case 0:					//星期天
				  Show_MyChinese(48,0,0,1);
					Show_MyChinese(64,0,1,1);
					Show_MyChinese(80,0,8,1);
					break;
				case 1://星期一
					 Show_MyChinese(48,0,0,1);
					Show_MyChinese(64,0,1,1);
					Show_MyChinese(80,0,2,1);
					break;
				case 2:	
					 Show_MyChinese(48,0,0,1);
					Show_MyChinese(64,0,1,1);
					Show_MyChinese(80,0,3,1);
					break;
				case 3:
					 Show_MyChinese(48,0,0,1);
					Show_MyChinese(64,0,1,1);
					Show_MyChinese(80,0,4,1);
					break;
				case 4:
					 Show_MyChinese(48,0,0,1);
					Show_MyChinese(64,0,1,1);
					Show_MyChinese(80,0,5,1);
					break;
				case 5:
					 Show_MyChinese(48,0,0,1);
					Show_MyChinese(64,0,1,1);
					Show_MyChinese(80,0,6,1);
					break;
				case 6:		//星期六
					 Show_MyChinese(48,0,0,1);
					Show_MyChinese(64,0,1,1);
					Show_MyChinese(80,0,7,1);
					break;  
			}
			OLED_ShowNum(32,32,calendar.hour,2,16);			
			OLED_ShowNum(56,32,calendar.min,2,16);			
			OLED_ShowNum(80,32,calendar.sec,2,16);
			OLED_Refresh_Gram();		//更新顯示到OLED 			
		}	

拿到時間之后,我想著這個模塊不可能只有這一個功能吧?隨后肯定就是電話和短信功能了,從實用性的想法出發,當你的智能鎖忘記了密碼之后,那不是很抓狂??破門而入?所以忘記密碼的功能就交給它吧!首先要解決的如何發送中文短信的問題

//unicode gbk 轉換函式
//src:輸入字串
//dst:輸出(uni2gbk時為gbk內碼,gbk2uni時,為unicode字串)
//mode:0,unicode到gbk轉換;
//     1,gbk到unicode轉換;
void sim800c_unigbk_exchange(u8 *src,u8 *dst,u8 mode)
{
	u16 temp; 
	u8 buf[2];
	if(mode)//gbk 2 unicode
	{
		while(*src!=0)
		{
			if(*src<0X81)	//非漢字
			{
				temp=(u16)ff_convert((WCHAR)*src,1);
				src++;
			}else 			//漢字,占2個位元組
			{
				buf[1]=*src++;
				buf[0]=*src++; 
				temp=(u16)ff_convert((WCHAR)*(u16*)buf,1); 
			}
			*dst++=sim800c_hex2chr((temp>>12)&0X0F);
			*dst++=sim800c_hex2chr((temp>>8)&0X0F);
			*dst++=sim800c_hex2chr((temp>>4)&0X0F);
			*dst++=sim800c_hex2chr(temp&0X0F);
		}
	}else	//unicode 2 gbk
	{ 
		while(*src!=0)
		{
			buf[1]=sim800c_chr2hex(*src++)*16;
			buf[1]+=sim800c_chr2hex(*src++);
			buf[0]=sim800c_chr2hex(*src++)*16;
			buf[0]+=sim800c_chr2hex(*src++);
 			temp=(u16)ff_convert((WCHAR)*(u16*)buf,0);
			if(temp<0X80){*dst=temp;dst++;}
			else {*(u16*)dst=swap16(temp);dst+=2;}
		} 
	}
	*dst=0;//添加結束符
} 

完整發送當前鎖體密碼的代碼如下:

void send_messege(void)
{
	u8 *p,*p1,*p2,*p3;
	u16 temp;			//用于列印當前密碼
	u8 pohnenumlen=11;	//號碼長度,最大15個數
	u8 temp_code[4];
	p=mymalloc(SRAMIN,100);	//申請100個位元組的記憶體,用于存放電話號碼的unicode字串
	p1=mymalloc(SRAMIN,300);//申請300個位元組的記憶體,用于存放短信的unicode字串
	p2=mymalloc(SRAMIN,100);//申請100個位元組的記憶體 存放:AT+CMGS=p1
	p3=mymalloc(SRAMIN,50);//申請50個位元組的記憶體,用于存放密碼
	temp=mycode[0]*1000+mycode[1]*100+mycode[2]*10+mycode[3];				//整型密碼轉換為整型數字
	sprintf((char *)temp_code,"%d",temp);																	//整型數字轉換為字串數字并保存進temp_code陣列中
	sim800c_unigbk_exchange(mycallbuf,p,1);				//將電話號碼轉換為unicode字串
	sim800c_unigbk_exchange((u8*)sim900a_test_msg,p1,1);//將短信內容轉換為unicode字串
	//temp_code就是我們當前鎖體的密碼
	sim800c_unigbk_exchange(temp_code,p3,1);				//將字串密碼轉換為unicode字串
	sprintf((char*)p2,"AT+CMGS=\"%s\"",p);
		if(sim800c_send_cmd(p2,">",200)==0)					//發送短信命令+電話號碼
		{ 	
			
			u3_printf("%s%s",p1,p3);		 						//發送短信內容到GSM模塊
 				if(sim800c_send_cmd((u8*)0X1A,"+CMGS:",1000)==0)
					{
					LED1=!LED1;//發送結束符,等待發送完成(最長等待10秒鐘,因為短信長了的話,等待時間會長一些)
					Show_Str_Mid(0,120,"發送成功!",16,240);
					delay_ms(1000);
					LCD_Fill(0,120,lcddev.width,160,WHITE);
					}
		} 
					USART3_RX_STA=0;
					mycallbuf[pohnenumlen]=0;			//最后加入結束符 
					if(USART3_RX_STA&0X8000)sim_at_response(1);//檢查從GSM模塊接收到的資料 
					myfree(SRAMIN,p);
					myfree(SRAMIN,p1);
					myfree(SRAMIN,p2);
					myfree(SRAMIN,p3);
}

完成了短信功能之后,我想著,那這個智能鎖,沒有防盜或者防范的體現嗎?于是乎,我想到了,盜賊都是深夜無人或者觀察居住者不在家的時候進行偷盜,那么,我是不是可以設定在某個我需要的特定時間點,有開鎖的行為進行電話預警呢?當然了,如果恰好是我開的,直接掛了就行,益大大的大于弊!好,那就開始!

if(calendar.hour>=19&&calendar.hour<=22) 
	{
		Show_Str_Mid(0,140,"開鎖時間例外",16,240);
		u3_printf("ATD%s;\r\n",mycallbuf);			//打電話提醒開鎖時間例外
		delay_ms(600);	//延遲等待AT指令發送完成
		LCD_Fill(0,140,lcddev.width,160,WHITE);
	}

哈哈,其實核心的代碼就這幾句,但你要知道,其實有時候好的效果是關乎你的想法而不是你復雜的代碼

對于這句話,將此想法和惡意嘗試解鎖的防范寫出完整代碼如下:

u16 Check_code(void)
{
	u16 i;
	u16 lock_num;
	u16 temp_num=0;
	if(error_num>=3)  //error_num是全域變數
	{
	do
		{
			Show_Str_Mid(0,100,"密碼輸入錯誤5次,請30秒后重試",16,240);
		  delay_ms(1000);
			temp_num++;
		}while(temp_num<error_num*10); //超過三次輸入密碼錯誤之后,每次輸入密碼等待時間以十倍疊加		
	LCD_Fill(0,100,lcddev.width,160,WHITE);
	}
	update_time();
	Show_Str_Mid(0,100,"請輸入解鎖密碼...",16,240);
  lock_num=LOCK_GET_NUM();		
				for( i = 4 ; i > 0 ;	i-- )
					{
						testcode[i-1]=lock_num%10;
						lock_num/=10;			
					}
					delay_ms(500);
					LCD_Fill(0,100,lcddev.width,160,WHITE);
					Show_Str_Mid(0,100,"正在檢索密碼...",16,240);
				
						if(testcode[0]==mycode[0]&&testcode[1]==mycode[1]&&testcode[2]==mycode[2]&&testcode[3]==mycode[3])
						{
							LCD_Fill(0,100,lcddev.width,160,WHITE);
							Show_Str_Mid(0,120,"密碼正確!正在解鎖...",16,240);
								if(calendar.hour>=19&&calendar.hour<=22) 
								{
									Show_Str_Mid(0,140,"開鎖時間例外",16,240);
									u3_printf("ATD%s;\r\n",mycallbuf);			//打電話提醒開鎖時間例外
								  delay_ms(600);	
									LCD_Fill(0,140,lcddev.width,160,WHITE);
								}
							AS608_load_keyboard(0,170,(u8**)kbd_menu);							
							open_close();				//自動開關門
						  LCD_Fill(0,120,lcddev.width,160,WHITE);
							return 1;
						}
						else
						{
							error_num++;
							LCD_Fill(0,100,lcddev.width,160,WHITE);
							LCD_ShowxNum(10,120,error_num,1,16,0);
							Show_Str_Mid(0,120,"次密碼錯誤!請重新輸入...",16,240);
							AS608_load_keyboard(0,170,(u8**)kbd_menu);
							BEEP=1;	
							delay_ms(1000);
							BEEP=0;
							LCD_Fill(0,120,lcddev.width,160,WHITE);
							return 0;
						}
}

總結

整個專案的實作是大一暑假的時候自己學習STM32并完成的,有一些地方肯定是存在思考不周到之處,即所謂的BUG,當然這是思維bug,當然還存在大量的代碼bug(專案用到的東西太多,這是肯定的,只是整體系統測驗還并未完全到位)寫下這個博客也是為了記錄自己的專案和學習狀態,慢慢進步即可,

這里整個博客是為了大致的展示專案的架構與設計,每個模塊并未更加詳細的介紹,后續會將每個模塊拿出來并將實際應用該模塊的程序或方法給出,模塊涉及的協議或演算法,也將詳細給出,詳情可關注博客后續,

因為演示視頻是大一自學pr做的,所以很拉,不過功能效果還是能看出來的

演示視頻

ESP8266用戶手冊

藍牙串口APP原始碼

SIM800CAT指令集

TJDZ-RC522射頻卡用戶使用手冊資料Ver_1.0

RFRD可用工程

HC-05AT指令集

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/321298.html

標籤:其他

上一篇:【Flutter】Dart的方法與箭頭函式

下一篇:android_jetpack_代碼重構之fragment基類

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more