基于stm32+小程序開發智能家居門禁系統-硬件-軟件實現

視頻演示:

基于stm32智能家居門禁系統小程序開發項目

視頻還有添加刪除卡號,添加刪除指紋,關閉繼電器電源等沒有演示。

代碼Git:

https://github.com/Abear6666/stm32lock

總體功能:

本門禁系統主要解鎖功能分別為卡片解鎖,指紋解鎖,小程序遠程開鎖,云端和本地端的結合,使我們解鎖更加便捷,快速。在硬件上主要采用的是:底層板子運用嘉立創打印的板子,焊接上stm32f103最小系統板,0.96寸的OLED,RC522的RIFD射頻感應模塊,舵機模塊,繼電器、蜂鳴器等模塊。

在軟件上,利用通信協議實現各個模塊和stm32f103之間的通信,實現數據的交互功能。最后通過代碼燒錄進入單片機,實現項目設計完整的功能。

系統程序流程圖:

在這里插入圖片描述

在這里插入圖片描述

本次的設計思路小程序是根據上一篇智能家居小程序來設計的。在原本進行優化設計更多的功能。

硬件部分:

在這里插入圖片描述
在這里插入圖片描述

繼電器和溫濕度是后邊接的,使用杜邦線引就可以了。

項目的模塊都在原理圖上邊了。

stm32部分:

代碼工程文件太多,代碼工程將會更新到GitHub

系統流程設計

本次系統流程設計首先從開機對時鐘初始化、GPIO初始化、ADC初始化、PWM初始化等。接著將各個模塊的引腳進行相應模式的初始化,如OLED顯示屏,蜂鳴器模塊,LED燈模塊,按鍵,RC522模塊,WIFI模塊,指紋識別模塊等。接著編寫主程序邏輯,設計好邏輯通過判斷那個模塊觸發執行相應的門禁解鎖功能,同時也需要對門禁實現安全防護,使用蜂鳴器報警提示。

在這里插入圖片描述

核心主程序設計

主函數:首先進行各模塊的引腳初始化

	u8 ensure;unsigned char *dataPtr = NULL;OLED_Init();Servo_Init();Key_Init();LED_Init();BEEP_Init();NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優先級,2位響應優先級USART1_Init(115200);RC522_IO_Init();PcdReset();  //復位RC522PcdAntennaOff();  //關閉天線delay_ms(100);PcdAntennaOn();  //開啟天線

接著在main函數中寫while循環,首先先判斷是否為解鎖狀態,如果是已解鎖狀態,程序標志lock將設置為1,通過判斷lock為1,再循環判斷按鍵的狀態,來實現菜單的切換,來實現開鎖后管理員的功能,例如添加卡號,刪除卡號,關閉門禁等等

	while (1){if(lock)//開鎖狀態{KeyNum = Key_GetNum();if (KeyNum == 1){LED1_Turn();OLED_Clear();if(Line <= 5){Line = 8;}else{Line --;}main_interface();	//解鎖后的主界面			}else if(KeyNum == 2){LED1_Turn();OLED_Clear();if(Line >= 8){Line = 5;}else{Line ++;}main_interface();	//解鎖后的主界面		}else if(KeyNum == 3) //確認按鍵{if(Line == 6) //添加指紋{Add_FR(++fingerprints_num);				}if(Line == 7) //刪除指紋{Del_FR(fingerprints_num);											}if(Line == 8){OLED_Clear();lock = 0;}}			}else{//關鎖狀態OLED_WriteCN(16,1,0,6);    //顯示->智能門禁系統OLED_WriteCN(56,4,1,1);		 //顯示鎖OLED_ShowString(8,7,"Back"); RC522_Get_card();//檢測到有刷卡 啟動識別驗證開鎖Check_FR(); //檢測到有指紋 啟動識別驗證開鎖			}dataPtr = ESP8266_GetIPD(3);if(dataPtr != NULL)OneNet_RevPro(dataPtr);delay_ms(10);}

本次系統主程序流程設計首先將各個模塊的引腳進行相應模式的初始化,如OLED顯示屏,蜂鳴器模塊,LED燈模塊,按鍵,RC522模塊,WIFI模塊,指紋識別模塊等。接著編寫主程序邏輯,在while循環中,首先判斷門禁lock標志位,如果是0即OLED顯示鎖屏狀態,指紋模塊,刷卡模塊進行識別工作,通過wifi模塊連接MQTT服務器,將門禁的狀態、LED狀態、蜂鳴器狀態、繼電器狀態、溫濕度等數據進行上報給服務器,識別過程出現失敗,失敗次數num+1,當達到5次即觸發報警功能,當解鎖成功時,將lock標志位置1,OLED顯示解鎖后的狀態,云端小程序收到報警提示。接著設計好系統整體邏輯細節,具體實現流程如下圖所示:

在這里插入圖片描述

圖4-14 系統主程序流程圖

在主函數main中進行整個系統代碼設計,大體可以分為以下的步驟:初始化MCU、系統時鐘、MQTT客戶端、RFID讀卡器和LCD顯示屏。已經封裝好了相應的模塊初始化函數,在主函數直接調用。

Init_ALL();//所有模塊設備初始化

(1) 連接WIFI并且訂閱接入MQTT服務器。

	ESP8266_Init();					//初始化ESP8266	while(OneNet_DevLink()){//接入OneNETdelay_ms(500);}	

(2)在循環中判斷鎖門標志lock標志,顯示對應的門禁狀態。如果門禁lock標志為0(鎖門狀態),在循環中等待RFID讀卡器讀卡并獲取卡號、指紋觸摸識別。識別成功將lock標志置1,并且解鎖門禁,OLED顯示解鎖后界面

(3)在循環中不斷發送數據給MQTT服務器,接收相應的指令操作,實現數據同步。

		//數據回發給小程序if(timeCount % 60 == 0)//1500ms / 25 = 60  1.5秒執行一次{if(upload_card_number_flag){upload_card_number(); //回發存儲的卡號數據給小程序upload_card_number_flag = 0;}else if(upload_fin_number_flag){upload_fin_number();//回發指紋個數數據給小程序upload_fin_number_flag = 0;}else{/********** 讀取LED0的狀態 **************/Led_Status = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_12);/********** 溫濕度傳感器獲取數據**************/DHT11_Read_Data(&humidityH,&humidityL,&temperatureH,&temperatureL);DEBUG_LOG(" | 濕度:%d.%d C | 溫度:%d.%d %%  ",humidityH,humidityL,temperatureH,temperatureL);DEBUG_LOG("==================================================================================");DEBUG_LOG("發布數據 ----- OneNet_Publish");sprintf(PUB_BUF,"{\"Hum\":%d.%d,\"Temp\":%d.%d,\"Door\":%d,\"Led\":%d,\"Beep\":%d,\"Power\":%d}",humidityH,humidityL,temperatureH,temperatureL,lock,Led_Status?0:1,Beep_Status,Power_Status);OneNet_Publish(devPubTopic, PUB_BUF);DEBUG_LOG("==================================================================================");ESP8266_Clear();			}}

(4)處理MQTT服務器推送的消息。通過接收平臺返回的數據,進JSON數據解析,判斷相應的指令,控制單片機的狀態(門禁、報警功能、客廳燈、門禁電源、查詢卡號、查詢指紋等狀態)。延時以降低CPU占用率。

		dataPtr = ESP8266_GetIPD(3); //獲取平臺返回的數據if(dataPtr != NULL)OneNet_RevPro(dataPtr);//平臺返回數據檢測delay_ms(10);timeCount++;

最后附一個主函數:

int main(void)
{unsigned char *dataPtr = NULL;//接收云平臺的數據unsigned short timeCount = 0;	//發送間隔變量Init_ALL();//所有模塊設備初始化while (1){//數據回發給小程序if(timeCount % 60 == 0)//1500ms / 25 = 60  1.5秒執行一次{if(upload_card_number_flag){upload_card_number(); //回發存儲的卡號數據給小程序upload_card_number_flag = 0;}else if(upload_fin_number_flag){upload_fin_number();//回發指紋個數數據給小程序upload_fin_number_flag = 0;}else{/********** 讀取LED0的狀態 **************/Led_Status = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_12);/********** 溫濕度傳感器獲取數據**************/DHT11_Read_Data(&humidityH,&humidityL,&temperatureH,&temperatureL);DEBUG_LOG(" | 濕度:%d.%d C | 溫度:%d.%d %%  ",humidityH,humidityL,temperatureH,temperatureL);DEBUG_LOG("==================================================================================");DEBUG_LOG("發布數據 ----- OneNet_Publish");sprintf(PUB_BUF,"{\"Hum\":%d.%d,\"Temp\":%d.%d,\"Door\":%d,\"Led\":%d,\"Beep\":%d,\"Power\":%d}",humidityH,humidityL,temperatureH,temperatureL,lock,Led_Status?0:1,Beep_Status,Power_Status);OneNet_Publish(devPubTopic, PUB_BUF);DEBUG_LOG("==================================================================================");ESP8266_Clear();			}}	if(lock)//開鎖狀態{KeyNum = Key_GetNum();//掃描按鍵if (KeyNum == 1){OLED_Clear();if(Line <= 5){Line = 8;interface_logo = 1;}else{Line --;}if(interface_logo == 1){main_interface();	//解鎖后的主界面	第一頁}else{main1_interface();	//解鎖后的主界面	第二頁}}else if(KeyNum == 2){OLED_Clear();if(Line >= 8){Line = 5;interface_logo = 2;}else{Line ++;}if(interface_logo == 1){main_interface();	//解鎖后的主界面	第一頁}else{main1_interface();	//解鎖后的主界面	第二頁}}else if(KeyNum == 3) //確認按鍵{if(interface_logo == 1) //界面第一頁的選擇確認{if(Line == 5) //燈光變換{LED1_Turn();}			if(Line == 6) //添加指紋{Add_FR(ValidN++);}				if(Line == 7) //刪除指紋{Del_FR(--ValidN);}if(Line == 8){close_door();OLED_Clear();lock = 0;continue;}	main_interface();}else if(interface_logo == 2){//第二頁選擇確認if(Line == 5) //增加卡號{RC522_Add_card();}			if(Line == 6) //刪除卡號{RC522_Rm_card();}				if(Line == 7) //打開警報{if(Beep_Status == 1){Beep_Status = 0;}else{Beep_Status = 1;}}if(Line == 8) //開關電源{if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8) == 0){GPIO_SetBits(GPIOA, GPIO_Pin_8);Power_Status = 1;}else{GPIO_ResetBits(GPIOA, GPIO_Pin_8);Power_Status = 0;}}	main1_interface();		//解鎖后的主界面	第二頁		}}			}else{//關鎖狀態OLED_WriteCN(16,1,0,6);    //顯示->智能門禁系統OLED_WriteCN(56,4,1,1);		 //顯示鎖sprintf(oledBuf,"T:%d.%d  H:%d.%d%%",temperatureH,temperatureL,humidityH,humidityL);OLED_ShowString(8,1,oledBuf);//8*16 “ABC”			RC522_Get_card();//檢測到有刷卡 啟動識別驗證開鎖Check_FR(); //檢測到有指紋 啟動識別驗證開鎖			}if(Fail_Num >=5) //門禁識別錯誤次數達到5次{Beep_Status= 1;	DEBUG_LOG("發布數據 --Warning--- OneNet_Publish");OneNet_Publish(devPubTopic, "{\"Warning\":1}");DEBUG_LOG("==================================================================================");ESP8266_Clear();Fail_Num = 0;}dataPtr = ESP8266_GetIPD(3); //獲取平臺返回的數據if(dataPtr != NULL)OneNet_RevPro(dataPtr);//平臺返回數據檢測delay_ms(10);timeCount++;}
}

RFID卡片

模塊初始化:

void RC522_IO_Init(void) 
{GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);   //開啟AFIO時鐘GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //關閉JTAG因為要使用PB3和4//GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_4;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOB, &GPIO_InitStruct);	SPI_Configuration(SPI1); 
}

通過卡片識別認證來辨別否為正確的卡號:

uint8_t card_number_certification(uint8_t *value)
{uint8_t i ,flag=0;uint8_t ave;int num=0;for(i=0;i<4;i++){DEBUG_LOG("card_id=%d\t",value[i]);num+=value[i]*(i+1);}DEBUG_LOG("num=%d\r\n",num);ave=num/4;DEBUG_LOG("ave=%d\r\n",ave);switch(ave){case 99:flag=1;break;case 248:flag=2;break;case 249:flag=3;break;case 244:flag=4;break;case 98:flag=5;break;case 39:flag=6;break;default :flag=0;Err_Count++;}if(Err_Count==5){Run_flag=0;Lock_flag=1;}return flag;
}

AS608指紋

模塊初始化:

	USART3_Init(57600);	/*初始化串口3,用于與指紋模塊通訊*/PS_StaGPIO_Init();					/*初始化FR讀狀態引腳*/while(PS_HandShake(&AS608Addr))			/*與AS608模塊握手*/{delay_ms(400);DEBUG_LOG("未檢測到模塊\r\n");delay_ms(1000);DEBUG_LOG("嘗試重新連接模塊\r\n"); }DEBUG_LOG("與指紋模塊握手成功!r\n");DEBUG_LOG("通訊成功\r\n");DEBUG_LOG("波特率:%d   地址:%x\r\n",57600,AS608Addr);		/*打印信息*/ensure=PS_ValidTempleteNum(&ValidN);										/*讀庫指紋個數*/if(ensure!=0x00)ShowErrMessage(ensure);								/*顯示確認碼錯誤信息*/ensure=PS_ReadSysPara(&AS608Para);  		/*讀參數 */if(ensure==0x00){DEBUG_LOG("庫容量:%d     對比等級: %d",AS608Para.PS_max-ValidN,AS608Para.PS_level);}elseShowErrMessage(ensure);		

指紋識別詳細過程:

void Check_FR(void) //檢測到有指紋 啟動識別驗證開鎖
{if(PS_Sta)	 //檢測PS_Sta狀態,如果有手指按下{	OLED_Clear();OLED_WriteCN(6,2,4,5);	delay_ms(500);OLED_ShowChar(6,12,'.');delay_ms(500);OLED_ShowChar(6,13,'.');delay_ms(500);OLED_ShowChar(6,14,'.');delay_ms(500);OLED_ShowChar(6,15,'.');while(1){SearchResult seach;u8 ensure;ensure=PS_GetImage();if(ensure==0x00)//獲取圖像成功 {	ensure=PS_GenChar(CharBuffer1);if(ensure==0x00) //生成特征成功{		ensure=PS_HighSpeedSearch(CharBuffer1,0,AS608Para.PS_max,&seach);if(ensure==0x00)//搜索成功{	unlock_interface();break;}else ShowErrMessage(ensure);					}elseShowErrMessage(ensure);}}//delay_ms(100);	
}
}

ESP-8266

  1. 導入ESP8266庫:將ESP8266庫的頭文件和源文件導入到Keil5的工程中,以便進行編程和調試。可以將ESP8266的源代碼拷貝到Keil5工程中,也可以使用庫文件的形式導入,具體的操作方式可以參考相關文檔。
  2. 連接Wi-Fi網絡:使用ESP8266的API函數和庫連接Wi-Fi網絡,需要指定網絡名稱和密碼等參數。例如,使用Keil5和ESP8266庫可以使用如下代碼:

模塊初始化:

void ESP8266_Init(void)
{ESP8266_Clear();DEBUG_LOG("0. AT - 測試MCU-8266通訊");while(ESP8266_SendCmd("AT\r\n", "OK"))delay_ms(500);	DEBUG_LOG("1. AT+RST - 軟復位8266");ESP8266_SendCmd("AT+RST\r\n", "");delay_ms(500);ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");delay_ms(500);DEBUG_LOG("2. AT+CWMODE=1,1 - 設置8266工作模式為STA");while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))delay_ms(500);DEBUG_LOG("3. AT+CWDHCP=1,1 - 使能STA模式下DHCP");while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))delay_ms(500);DEBUG_LOG("4. AT+CWJAP - 連接WIFI");while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))delay_ms(500);DEBUG_LOG("5. AT+CIPSTART - 連接服務器");while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))delay_ms(500);ESP8266_INIT_OK = 1;DEBUG_LOG("6. ESP8266 Init OK - ESP8266初始化成功");DEBUG_LOG("ESP8266初始化			[OK]");}

SG90舵機

首先通過TIM4定時器和PWM信號控制PB6引腳輸出PWM信號,然后通過SG90_SetAngle函數來控制舵機的角度,該函數根據角度計算PWM脈寬,并通過TIM_SetCompare1函數設置PWM脈寬,從而控制舵機轉動。最后在main函數中循環控制舵機轉到0度、90度和180度。需要注意的是,舵機的PWM周期為20ms,脈寬范圍為1000us到2000us,對應舵機轉動角度為0度到180度。

#include "stm32f10x.h"
#define SG90_PERIOD 20000 // PWM周期為20ms,即50Hz
#define SG90_MIN 1000 // 舵機最小PWM脈寬
#define SG90_MAX 2000 // 舵機最大PWM脈寬
void SG90_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct;TIM_TimeBaseInitTypeDef TIM_InitStruct;TIM_OCInitTypeDef TIM_OC_InitStruct;// 使能GPIOB和TIM4時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);// 配置PB6引腳為復用功能并設置推挽輸出GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// TIM4時間基準配置,設定計數器周期為20000,即20ms,時鐘預分頻系數為72-1TIM_InitStruct.TIM_Period = SG90_PERIOD - 1;TIM_InitStruct.TIM_Prescaler = 72 - 1;TIM_InitStruct.TIM_ClockDivision = 0;TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM4, &TIM_InitStruct);// TIM4 PWM輸出配置,設置OC1模式為PWM模式1,使能預裝載寄存器,輸出比較值為0TIM_OC_InitStruct.TIM_OCMode = TIM_OCMode_PWM1;TIM_OC_InitStruct.TIM_OutputState = TIM_OutputState_Enable;TIM_OC_InitStruct.TIM_Pulse = 0;TIM_OC_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OC1Init(TIM4, &TIM_OC_InitStruct);// 啟動TIM4計數器TIM_Cmd(TIM4, ENABLE);
}
void SG90_SetAngle(int angle)
{int pulse = 0;if (angle < 0) angle = 0;if (angle > 180) angle = 180;pulse = SG90_MIN + (SG90_MAX - SG90_MIN) * angle / 180;TIM_SetCompare1(TIM4, pulse);
}
int main(void)
{SG90_Init();while (1){SG90_SetAngle(0); // 舵機轉到0度Delay(1000); // 延時1sSG90_SetAngle(90); // 舵機轉到90度Delay(1000); // 延時1sSG90_SetAngle(180); // 舵機轉到180度Delay(1000); // 延時1s}
}

oled:

字摸軟件設置:

在這里插入圖片描述

DHT11:

```
#include "dht11.h"
#include "delay.h"//復位DHT11
void DHT11_Rst(void)	   
{                 DHT11_IO_OUT(); 	//SET OUTPUTGPIO_ResetBits(GPIOB,GPIO_Pin_1);//拉低DQdelay_ms(20);    	//拉低至少18msGPIO_SetBits(GPIOB,GPIO_Pin_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);//等待40usif(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 *humiH,u8 *humiL,u8 *tempH,u8 *tempL)    
{        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]){*humiH=buf[0];			//坑啊原子哥,說明書明明是濕度在前溫度在后*humiL=buf[1];			*tempH=buf[2];*tempL=buf[3];}}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);	 //使能PA端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;				 //PA0端口配置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_1);						 //PA0 輸出高DHT11_Rst();  //復位DHT11return DHT11_Check();//等待DHT11的回應
} 添加指紋成功顯示``````
#ifndef __DHT11_H
#define __DHT11_H	 
#include "stm32f10x.h"//IO方向設置
#define DHT11_IO_IN()  {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=8<<4;}
#define DHT11_IO_OUT() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=3<<4;}
IO操作函數											   
#define	DHT11_DQ_OUT GPIO_SetBits(GPIOB,GPIO_Pin_1) //數據端口	PA0出方向 
#define	DHT11_DQ_IN  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) //數據端口	PA0入方向u8 DHT11_Init(void);//初始化DHT11
u8 DHT11_Read_Data(u8 *humiH,u8 *humiL,u8 *tempH,u8 *tempL);//讀取溫濕度
u8 DHT11_Read_Byte(void);//讀出一個字節
u8 DHT11_Read_Bit(void);//讀出一個位
u8 DHT11_Check(void);//檢測是否存在DHT11
void DHT11_Rst(void);//復位DHT11  #endif```

FLASH數據存儲:

#include "stm32f10x.h"
#define EEPROM_START_ADDRESS ((uint32_t)0x0800F800) //EEPROM起始地址,選擇Flash存儲器的最后2個扇區
#define EEPROM_BLOCK_SIZE 4 //塊大小,這里設置為4字節
void EEPROM_Write(uint16_t addr, uint32_t data)
{uint32_t sector_number = (EEPROM_START_ADDRESS - FLASH_BASE) / FLASH_PAGE_SIZE + (addr / EEPROM_BLOCK_SIZE); //計算扇區號uint32_t block_number = (addr % EEPROM_BLOCK_SIZE) / 4; //計算塊號uint32_t address = EEPROM_START_ADDRESS + (addr & 0xFFFFFC); //計算地址,按照4字節對齊FLASH_Unlock(); //解鎖Flash存儲器FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); //清除標志位FLASH_ErasePage(address); //擦除頁面if (FLASH_ProgramWord(address + block_number * 4, data) == FLASH_COMPLETE) //寫入數據{FLASH_Lock(); //上鎖Flash存儲器}
}
uint32_t EEPROM_Read(uint16_t addr)
{uint32_t address = EEPROM_START_ADDRESS + addr; //計算地址return (*(__IO uint32_t*)address); //讀取數據
}

使用EEPROM_Write()函數寫入數據,該函數的輸入參數為變量的地址和變量的值。首先計算出變量所在的扇區號和塊號,然后計算出變量所在的地址,按照4字節對齊。接著解鎖Flash存儲器,清除標志位,擦除頁面,最后使用FLASH_ProgramWord()函數將數據寫入Flash存儲器中,寫入操作完成后,上鎖Flash存儲器。使用EEPROM_Read()函數讀取數據,該函數的輸入參數為變量的地址,計算出變量所在的地址,然后直接讀取數據。 需要注意的是,使用Flash存儲器模擬EEPROM功能時,需要考慮Flash存儲器的壽命和寫入次數的限制,以及數據的安全性等問題。如果需要更加安全和可靠的存儲解決方案,可以使用外部EEPROM芯片或FRAM芯片。

wx小程序部分:

這一部門設計可以觀察我的上一篇文章的智能家居,搭建MQTT服務器-》到代碼設計過程都有詳細的編寫。

【基于stm32f103C8T6-小程序智能家居項目實戰-自繪PCB到實現功能一條龍+30分鐘解決-各種bug已修復】_harmony 項目 實戰-CSDN博客

設計思路:

在這里插入圖片描述

在這里插入圖片描述

index.vue

<!--* @Author: your name* @Date: 2023-04-14 20:16:48* @LastEditTime: 2023-04-14 20:16:48* @LastEditors: axiong* @Description: In User Settings Edit* @FilePath: \src\pages\index\index.vue
--><template><div class="wrapper"><div class="header-wrapper"><div class="header-title"><span>日志輸出:{{ Log }}</span><span></span></div><div class="header-text"><span></span><span></span></div><div class="weather-advice">{{ Msg }}</div></div><div class="body-wrapper"><div class="data-wrapper"><div class="data"><img class="data-logo" src="/static/images/wendu.png" /><div class="data-text"><div class="data-title">溫度</div><div class="data-value">{{ Temp }}</div></div></div><div class="data"><img class="data-logo" src="/static/images/shidu.png" /><div class="data-text"><div class="data-title">濕度</div><div class="data-value">{{ Hum }} %</div></div></div></div><div class="data-wrapper"><div class="data"><img class="data-logo" src="/static/images/door.png" /><div class="data-text"><div class="data-title">門禁</div><div class="data-value"><switch @change="onDoorChange" :checked="Door" color="#3d7ef9" /></div></div></div><div class="data"><img class="data-logo" src="/static/images/led.png" /><div class="data-text"><div class="data-title">客廳燈</div><div class="data-value"><switch @change="onLedChange" :checked="Led" color="#3d7ef9" /></div></div></div></div><div class="data-wrapper"><div class="data"><img class="data-logo" src="/static/images/bjq.png" /><div class="data-text"><div class="data-title">報警器</div><switch @change="onBeepChange" :checked="Beep" color="#3d7ef9" /></div></div><div class="data"><img class="data-logo" src="/static/images/power.png" /><div class="data-text"><div class="data-title">門禁電源</div><switch @change="onPowerChange" :checked="Power" color="#3d7ef9" /></div></div></div><div class="data-wrapper"><button class="bt_4" hover-class="hover" @click="addfin">添加指紋</button><button class="bt_4" hover-class="hover" @click="rmfin">刪除指紋</button></div><div class="data-wrapper"><button class="bt_6" hover-class="hover" @click="addcard">添加卡號</button><button class="bt_6" hover-class="hover" @click="rmcard">刪除卡號</button></div></div><div class="body"><!-- 彈框內容 --><div class="vueMdelBox"><div :hidden="!vueShowModel" class="vueContant"><div class="vueTitle">報警提示!</div><div class="vueDetail">觸發報警,有人嘗試爆破門禁!</div><!-- 確定取消按鈕 --><div class="vueBtn"><p @click="vueShowModel = false">取消</p><p @click="close_power">關閉電源</p></div></div><!-- 背景黑色蒙版 --><divclass="vueBgdCol":hidden="!vueShowModel"@click="vueShowModel = false"></div></div><button class="bt_2" hover-class="hover" @click="Get_card">查詢卡號</button><div class="vueMdelBox"><div :hidden="!vueShowModel1" class="vueContant"><div class="vueTitle">卡號查詢成功!</div><div class="vueDetail">{{ Card_Msg }}</div><!-- 確定取消按鈕 --><div class="vueBtn"><p @click="vueShowModel1 = false">取消</p><p @click="confirm">確定</p></div></div><!-- 背景黑色蒙版 --><divclass="vueBgdCol":hidden="!vueShowModel1"@click="vueShowModel1 = false"></div></div><button class="bt_2" hover-class="hover" @click="Get_fin">查詢指紋</button><div class="vueMdelBox"><div :hidden="!vueShowModel2" class="vueContant"><div class="vueTitle">指紋查詢成功!</div><div class="vueDetail">當前指紋存儲的數量為:{{ Fin_Msg }}</div><!-- 確定取消按鈕 --><div class="vueBtn"><p @click="vueShowModel2 = false">取消</p><p @click="confirm">確定</p></div></div><!-- 背景黑色蒙版 --><divclass="vueBgdCol":hidden="!vueShowModel2"@click="vueShowModel2 = false"></div></div></div></div>
</template><script>
import { connect } from "mqtt/dist/mqtt.js";const mqttUrl = "wxs://mqtt.bear321.cn:8084/mqtt";//mqtt 服務器域名/IP
const sub = "/myhome/sub";//  設備訂閱topic(小程序發布命令topic)
const pub = "/myhome/pub";//  設備發布topic(小程序訂閱數據topic)export default {data() {return {client: {},Temp: 0,Hum: 0,Light: 0,Led: false,Beep: false,Door: false,Power: false,Msg: "", //服務器連接狀態提示Log: "", // 命令下發信息Card_Msg: "", //卡號信息Fin_Msg: "", //指紋信息vueShowModel: false, //默認不顯示 警報提示vueShowModel1: false, //默認不顯示 卡號查詢提示vueShowModel2: false, //默認不顯示 指紋查詢顯示};},components: {},methods: {// 點擊按鈕打開彈框Get_card() {var that = this;this.client.publish(sub,'{"target":"Get_card","value":1}',function (err) {if (!err) {console.log("成功下發命令---查詢卡號");that.Log = "成功下發命令---查詢卡號";}});},Get_fin() {var that = this;this.client.publish(sub,'{"target":"Get_fin","value":1}',function (err) {if (!err) {console.log("成功下發命令---查詢指紋");that.Log = "成功下發命令---查詢指紋";}});},// 關閉電源close_power() {var that = this;this.vueShowModel = false;this.client.publish(sub, '{"target":"Power","value":0}', function (err) {if (!err) {console.log("成功下發命令---關閉電源");that.Log = "成功下發命令---關閉電源";}});},// 點擊確定按鈕模態框消失confirm() {this.vueShowModel1 = false;this.vueShowModel2 = false;},onDoorChange(event) {console.log("666!");var that = this;console.log(event.mp.detail);let sw = event.mp.detail.value;that.Door = sw;if (sw) {that.client.publish(sub, '{"target":"Door","value":1}', function (err) {if (!err) {console.log("成功下發命令---開門");that.Log = "成功下發命令---開門";}});} else {that.client.publish(sub, '{"target":"Door","value":0}', function (err) {if (!err) {console.log("成功下發命令---關門");that.Log = "成功下發命令---關門";}});}},onLedChange(event) {var that = this;console.log(event.mp.detail);let sw = event.mp.detail.value;that.Led = sw;if (sw) {that.client.publish(sub, '{"target":"LED","value":1}', function (err) {if (!err) {console.log("成功下發命令---開燈");that.Log = "成功下發命令---開燈";}});} else {that.client.publish(sub, '{"target":"LED","value":0}', function (err) {if (!err) {console.log("成功下發命令---關燈");that.Log = "成功下發命令---關燈";}});}},onBeepChange(event) {var that = this;console.log(event.mp.detail);let sw = event.mp.detail.value;that.Beep = sw;if (sw) {that.client.publish(sub, '{"target":"BEEP","value":1}', function (err) {if (!err) {console.log("成功下發命令---打開警報器");that.Log = "成功下發命令---打開警報器";}});} else {that.client.publish(sub, '{"target":"BEEP","value":0}', function (err) {if (!err) {console.log("成功下發命令---關閉警報器");that.Log = "成功下發命令---關閉警報器";}});}},addfin(event) {var that = this;this.client.publish(sub, '{"target":"Addfin","value":1}', function (err) {if (!err) {console.log("成功下發命令---添加指紋");that.Log = "成功下發命令---添加指紋";}});},rmfin(event) {this.client.publish(sub, '{"target":"Rmfin","value":1}', function (err) {if (!err) {console.log("成功下發命令---刪除指紋");that.Log = "成功下發命令---刪除指紋";}});},addcard(event) {var that = this;this.client.publish(sub,'{"target":"Addcard","value":1}',function (err) {if (!err) {console.log("成功下發命令---添加卡號");that.Log = "成功下發命令---添加卡號";}});},rmcard(event) {this.client.publish(sub, '{"target":"Rmcard","value":1}', function (err) {if (!err) {console.log("成功下發命令---刪除卡號");that.Log = "成功下發命令---刪除卡號";}});},onPowerChange(event) {var that = this;console.log(event.mp.detail);let sw = event.mp.detail.value;that.Power = sw;if (sw) {that.client.publish(sub,'{"target":"Power","value":1}',function (err) {if (!err) {console.log("成功下發命令---打開門禁電源");that.Log = "成功下發命令---打開門禁電源";}else{that.Log = "發送命令失敗---請檢查網絡連接并且重啟小程序";}});} else {that.client.publish(sub,'{"target":"Power","value":0}',function (err) {if (!err) {console.log("成功下發命令---關閉門禁電源");that.Log = "成功下發命令---關閉門禁電源";}else{that.Log = "發送命令失敗---請檢查網絡連接并且重啟小程序";}});}},},// created () {//   // let app = getApp()// }onShow() {var that = this;wx.showToast({title: "連接服務器....",icon: "loading",duration: 10000,mask: true,});let second = 10;var toastTimer = setInterval(() => {second--;if (second) {wx.showToast({title: `連接服務器...${second}`,icon: "loading",duration: 1000,mask: true,});} else {clearInterval(toastTimer);wx.showToast({title: "連接失敗",icon: "error",mask: true,},that.Msg = "設備聯網失敗,請重啟小程序再次嘗試!");}}, 1000);that.client = connect(mqttUrl);that.client.on("connect", function () {console.log("成功連接mqtt服務器!");clearInterval(toastTimer);wx.showToast({title: "連接成功",icon: "success",mask: true,});// 一秒后訂閱主題setTimeout(() => {that.client.subscribe(pub, function (err) {if (!err) {console.log("成功訂閱設備上行數據Topic!");that.Msg = "設備聯網成功,正常運行!";wx.showToast({title: "訂閱成功",icon: "success",mask: true,});}else{that.Msg = "設備聯網失敗,訂閱設備上行數據失敗";}});}, 1000);       });that.client.on("message", function (topic, message) {// console.log(message);let dataFromDev = {};dataFromDev = JSON.parse(message);console.log(dataFromDev);that.Temp = dataFromDev.Temp;that.Hum = dataFromDev.Hum;that.Light = dataFromDev.Light;that.Led = dataFromDev.Led;that.Beep = dataFromDev.Beep;that.Door = dataFromDev.Door;that.Power = dataFromDev.Power;if (dataFromDev.Warning == 1) {that.vueShowModel = true;}if (dataFromDev.value == 1) {that.Card_Msg = dataFromDev.Card_Msg;that.vueShowModel1 = true;}//console.log(dataFromDev.Fin_Msg);if (dataFromDev.Fin_Msg >= 0) {that.Fin_Msg = dataFromDev.Fin_Msg;that.vueShowModel2 = true;}});},
};
</script><style lang="scss" scoped>
/* 從左往右漸變 */
.bt_2 {margin-top: 40rpx;background: linear-gradient(to right, #ead6ee, #a0f1ea);
}/* 從上往下漸變 */
.bt_1 {margin-top: 40rpx;background: linear-gradient(#ccfbff, #ef96c5);
}/* 半透明漸變 */
.bt_4 {margin-top: 40rpx;background: linear-gradient(rgb(252, 126, 67), rgba(255, 0, 0, 0));width: 80%;border-radius: 50rpx;/* background: bg_red; */
}
/* border-radius: 98rpx;是控制按鈕邊變圓 */
.goodbutton {margin-top: 30px;width: 80%;background-color: rgb(252, 126, 67);color: white;border-radius: 98rpx;background: bg_red;
}/* 按下變顏色 */
.hover {top: 3rpx;background: rgb(236, 179, 156);
}/* 多色漸變 */
.bt_6 {margin-top: 40rpx;background: linear-gradient(to right, #f9957f, #f2f5d0, #aebaf8, #c973ff);width: 80%;border-radius: 50rpx;
}
.vueBgdCol {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: black;z-index: 99;-moz-opacity: 0.6;opacity: 0.6;filter: alpha(opacity=88);
}.vueContant {padding: 10px 0px 0px 0px;position: fixed;top: 36%;left: 50%;width: 80%;margin-left: -40%;background-color: white;z-index: 100;overflow: auto;border-radius: 10px;
}.vueTitle {display: flex;justify-content: center;font-weight: bold;
}.vueDetail {font-size: 14px;color: #646566;display: flex;justify-content: center;padding: 16px;
}.vueBtn {display: flex;
}.vueBtn p {width: 50%;padding: 5% 0%;font-weight: bold;text-align: center;border-top: 1px solid #ebedf0;
}.vueBtn p:last-child {color: #ee0a24;border-left: 1px solid #ebedf0;
}.header-wrapper {padding: 15px;background-color: #7d7dd7; //背景顏色border-radius: 20px; //圓角color: rgba(242, 246, 242, 0.867);box-shadow: #7d7d 0px 0px 0px;padding: 15px 30px; //文字上下邊距.header-title {display: flex;justify-content: space-between;}.header-text {font-size: 32px;font-weight: 400;display: flex;justify-content: space-between;}.weather-advice {margin-top: 20px; //間距font-size: 14px;}
}
.data-wrapper {margin-top: 20px;display: flex;justify-content: space-between;.data {background-color: #fff;width: 150px;height: 80px;border-radius: 20px;display: flex;justify-content: space-around;padding: 0 8px;box-shadow: #d6d6d6;box-shadow: #d6d6d6 0px 0px 5px;.data-logo {height: 36px;width: 36px;margin-top: 15px;}.data-text {margin-top: 15px;color: #7f7f7f;.data-value {font-size: 26px;}}}
}
.button-sp-area {margin: 0 auto;
}
</style>

功能整改:

1.實現卡號添加刪除 存入stm32 ==》完成2.蜂鳴器 可以更換語音播報 或者添加語音播報
3.加個溫濕度傳感器 在鎖屏主界面顯示  ==》完成 
4.小程序添加注冊功能 能夠查詢出入門禁時間 什么方式查詢出入 最后一次出入的時間 方式 顯示在小程序頂端5.報警功能 門禁識別連續3次失敗    蜂鳴器(燈)報警 (完成)
溫度若大于37.3 也報警 
6.與小程序交互 (完成大部分 目前存在數據有時不是很快的對應,小程序發送有時會卡死 )添加freetos 
7.刪除指定的指紋-----

項目總結:

項目難點:

1.移植模塊之間會出現有一些不匹配,或者通信不上,這里可能要檢查好是否硬件問題,軟件問題就要查詢相應的代碼是否漏下什么。
2.小程序和stm32之間通過wifi再鏈接到mqtt服務器通信也是一個難點需要打通。
3.邏輯bug,出現過一些數據上傳失敗或者下發失敗,上位機或者下位機沒收到數據問題。

個人總結:

通過這個項目,讓我感受到嵌入式軟件開發的流程,從最開始硬件制作,到完成軟件端的開發,認識到一個產品都是基于一個芯片,然后集成的各種模塊,逐步實現其各種功能。學習到stm32的開發應用,模塊移植,硬件焊接等知識。最后也是有存在問題待解決(延時處理),可以進一步移植使用FreeRTos系統來解決,完善細節,實現更多功能,投入實際運用

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44354.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44354.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44354.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

沖積圖(alluvial plot)展示競爭性內源RNA(ceRNA)網絡

導讀&#xff1a; 用沖積圖展示ceRNA網絡可以更好地查看競爭和吸附關系&#xff0c;讓靜態的圖&#xff0c;“流動”起來。 沖積圖簡介 Alluvial plot&#xff08;沖積圖&#xff09;是一種流程圖&#xff0c;最初設計用于展示網絡結構隨時間的變化。這種圖表特別適用于展示…

sed的替換可用:斜杠/,豎或|,井號# 等符號, 但是查找只能用斜杠/ , sed的查找和替換可以一起用 筆記240711

sed的替換可用:斜杠/,豎或|,井號# 等符號, 但是… … 查找只能用斜杠/ 替換必須用s開頭, 如:s/ , s| , s# 例如: s/正則/替換內容/s/正則/替換內容/gs|正則|替換內容|s|正則|替換內容|gs#正則#替換內容#s#正則#替換內容#g 當內容包含斜杠/時, (例如路徑) , 使用 豎或|,井號…

06_TypeScript 中的函數

TypeScript 中的函數 一、函數的定義es5 定義函數的方法TypeScript 定義函數的方法ts 中定義方法傳參 二、可選參數三、默認參數&#xff08;跟可選參數一樣&#xff09;四、剩余參數&#xff08;三點運算符的應用&#xff09;五、函數重載六、箭頭函數 es6 一、函數的定義 es…

Windows圖形界面(GUI)-SDK-C/C++ - 編輯框(edit)

公開視頻 -> 鏈接點擊跳轉公開課程博客首頁 -> 鏈接點擊跳轉博客主頁 目錄 編輯框(edit) 控件樣式 創建控件 初始控件 消息處理 示例代碼 編輯框(edit) 控件樣式 編輯框&#xff08;Edit Control&#xff09;是Windows中最常用的控件之一&#xff0c;用于接收用戶…

python 知識點累積

.items() tinydict {Google: www.google.com, Runoob: www.runoob.com, taobao: www.taobao.com}print (tinydict.items())運行結果 dict_items([(Google, www.google.com), (Runoob, www.runoob.com), (taobao, www.taobao.com)]) 返回的 dict_items([...]) 是一個字典項的…

【持續集成_05課_Linux部署SonarQube及結合開發項目部署】

一、Linux下安裝SonarQube 1、安裝sonarQube 前置條件&#xff1a;sonarQube不能使用root賬號進行啟動&#xff0c;所以需要創建普通用戶及 其用戶組 1&#xff09;創建組 2&#xff09;添加用戶、組名、密碼 3&#xff09;CMD上傳qube文件-不能傳到home路徑下哦 4&#xff09…

王牌站士Ⅳ--矢量數據庫對 RAG 效率的影響

前言 近年來&#xff0c;檢索增強生成 (RAG) 模型越來越受歡迎。RAG 模型利用大型神經網絡以及外部知識源的檢索機制。這使得模型擁有的知識比其內部存儲的更多&#xff0c;從而使其能夠為廣泛的主題和領域生成高質量的輸出。 影響 RAG 模型性能的關鍵因素之一是從外部源檢索相…

C++之STL簡介

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、STL是什么二、STL的發展歷史三、STL的組成部分3.1 容器&#xff08;Containers&#xff09;3.2 迭代器&#xff08;Iterators&#xff09;3.3 算法&#xf…

【啟明智顯分享】ESP32-S3 4.3寸觸摸串口屏HMI應用方案:WIFI/藍牙無線通信助力烘干設備實現遠程遙控

技術不斷進步&#xff0c;人們對烘干設備的美觀度、功能多樣性提出更高要求&#xff0c;傳統的數碼管顯示、按鍵式控制已經無法滿足客戶的需求。用智能屏替代傳統的數碼管可以很好的解決這個問題&#xff0c;為用戶帶來更好的人機交互體驗。 基于此&#xff0c;啟明智顯提出將樂…

solidity實戰練習2--ERC20實現

//SPDX-License-Identifier:MTT pragma solidity ^0.8.24;interface erc20{function name()external view returns (string memory);//查看代幣的名稱function symbol() external view returns(string memory);//查看代幣簡稱function totalSupply()external view returns(uint…

低代碼:架起產教融合的“立交橋”

隨著信息技術的飛速發展&#xff0c;傳統的軟件開發模式正面臨著前所未有的挑戰。為了應對這一挑戰&#xff0c;低代碼平臺應運而生&#xff0c;其通過簡化開發流程、提高開發效率&#xff0c;成為了推動產業與教育深度融合的重要力量。本文將探討低代碼平臺如何架起產教融合的…

C++基礎(十八):繼承(重點)

各位看官&#xff0c;大家好&#xff01;今天我們將探討C中的三大特性之一&#xff1a;繼承。繼承是一種面向對象編程的重要概念&#xff0c;它允許我們通過創建新的類&#xff0c;從而復用和擴展現有類的功能。通過繼承&#xff0c;我們不僅能夠提高代碼的可重用性和可維護性&…

算法刷題筆記 KMP字符串(C++實現,并給出了求next數組的獨家簡單理解方式)

文章目錄 題目描述基本思路實現代碼 題目描述 給定一個字符串S&#xff0c;以及一個模式串P&#xff0c;所有字符串中只包含大小寫英文字母以及阿拉伯數字。模式串P在字符串S中多次作為子串出現。求出模式串P在字符串S中所有出現的位置的起始下標。 輸入格式 第一行輸入整數…

docker拉取鏡像,報錯error pulling image configuration: download failed after attempts=6: dial tcp 157.240.1

error pulling image configuration: download failed after attempts6: dial tcp 157.240.10.32:443: i/o timeout docker compose pull docker pull langgenius/dify-web:0.6.13 重啟docker sudo systemctl restart dockerhttps://stackoverflow.com/questions/72353203/do…

9.5 柵格圖層符號化多波段彩色渲染

文章目錄 前言多波段彩色渲染QGis設置為多波段彩色二次開發代碼實現多波段彩色 總結 前言 介紹柵格圖層數據渲染之多波段彩色渲染說明&#xff1a;文章中的示例代碼均來自開源項目qgis_cpp_api_apps 多波段彩色渲染 以“3420C_2010_327_RGB_LATLNG.tif”數據為例&#xff0c…

代碼隨想錄打卡第二十一天

代碼隨想錄–二叉樹部分 day 21 二叉樹第八天 文章目錄 代碼隨想錄--二叉樹部分一、力扣669--修建二叉搜索樹二、力扣108--將有序數組轉換為二叉搜索樹三、力扣538--把二叉搜索樹轉換為累加樹 一、力扣669–修建二叉搜索樹 代碼隨想錄題目鏈接&#xff1a;代碼隨想錄 給你二叉…

常見條件控制算法流程圖

內容講解&#xff1a;流程控制[if…else…(if…elif…else…),while,for] 常見條件控制算法流程圖高清圖

新手教學系列——高效管理MongoDB數據:批量插入與更新的實戰技巧

前言 在日常開發中,MongoDB作為一種靈活高效的NoSQL數據庫,深受開發者喜愛。然而,如何高效地進行數據的批量插入和更新,卻常常讓人頭疼。今天,我們將一起探討如何使用MongoDB的bulk_write方法,簡化我們的數據管理流程,讓代碼更加簡潔高效。 常規做法:find、insertone…

Unity 之 抖音小游戲集成排行榜功能詳解

Unity 之 抖音小游戲集成排行榜功能詳解 一,前言1.1 為游戲設計利于傳播的元素?2.2 多人競技、社交傳播?二,集成說明2.1 功能介紹2.2 完整代碼2.3 效果展示三,發現的問題和迭代計劃一,前言 對于 Unity 開發者而言,在開發抖音小游戲時集成排行榜功能是提升游戲社交性和玩…

Java實戰中處理高并發的策略

引言 隨著互聯網的快速發展&#xff0c;高并發成為了許多應用必須面對的挑戰。Java作為一門廣泛應用于企業級開發的語言&#xff0c;提供了豐富的工具和技術來應對高并發問題。本文將詳細探討Java中處理高并發的幾種常見策略和技術。 1. 并發編程基礎 1.1 線程與線程池 Jav…