【毕业设计】基于STM32和WiFi模块的智能植物养护系统设计

🆔作者简介:一名电子信息大学生.
📑 个人主页:神奇的汪同学主页👀
📫 如果文章知识点有错误的地方,请指正!和大家一起学习进步
🔥 如果感觉博主的文章还不错的话,还请不吝关注、点赞、收藏

前言

【毕业设计】:基于STM32和WIFI模块的智能植物养护系统设计
【工程链接】:https://oshwhub.com/wyb1661463799/zhi-neng-zhi-wu-yang-hu-ji-tong-she-ji
【代码链接】:https://www.wcfaka.com/details/AE408673
【项目备注】:原理图和PCB见上方的【工程链接】,希望该设计能够为大家提供一些帮助,同时也感谢大家一键三连对UP的支持。最新代码暂不更新,有需要的可以点击上方【代码链接】获取哦!
【项目简介】:主控采用STM32F103C8T6单片机,同时对植物成长的多个环境参数进行实时采集,如土壤湿度、土壤PH值、室内温湿度和光照强度等环境数据,并发送给主控制器;然后通过WiFi模块无线传输到后台上位机对数据进行分析处理,并可以通过手机APP端或者语音助手进行补水和补光的智能养护控制。
下面是STM32单片机的代码整体思路梳理,还有LD3320模块的代码下篇文章再展示!

历时一个月毕业设计终于要完成咯!


一、主函数【main()】

提示:这里是该项目的->主函数【main()】,下面代码仅供参考

首先该函数执行 【Sys_Init();】即系统初始化函数,这个函数里面包含了该项目所需初始化的所有函数;其次执行【TIM4_Init(20-1,3600-1);】该函数是将定时器4设置为1ms的定时器,为执行【Sound_Output();】即蜂鸣器驱动和串口接收数据做准备;最后在主循环里面执行【GUI_Refresh();】即OLED更新显示函数,为实现OLED的多级菜单做准备。

/*--------------------------------------------------*/
/*函数名:系统主函数                                  */
/*参  数:无                                     	  */
/*返回值:无                                         */
/*--------------------------------------------------*/
int main(void)
{
	Sys_Init();//系统初始化函数
	/*F=72MHz/(arr+1)/(psc+1)=(72000 000)/10000/7200=1Hz*/
	TIM4_Init(20-1,3600-1);//每隔1ms进入中断一次
	while(1)
	{
		GUI_Refresh();//OLED更新显示函数
	}
}

1、系统初始化函数【Sys_Init()】

提示:以下是该项目的->系统初始化函数【Sys_Init()】,下面代码仅供参考

/*--------------------------------------------------*/
/*函数名:系统初始化函数           	                */
/*参  数:无                                        */
/*返回值:无                                        */
/*--------------------------------------------------*/
void Sys_Init()
{
	Delay_Init();//延时函数初始化
	Usart1_Init(9600);//串口1功能初始化,波特率9600
	Usart3_Init(9600);//串口3功能初始化,波特率9600
	OLED_Init();//OLED初始化
	OLED_Clear();//OLED清屏
	KEY_Init();//按键初始化
	BEEP_Init();//蜂鸣器初始化
	LED_Init();//LED初始化
 	/*数据初始化*/
	DHT11_Init();//DHT11初始化 PB14
	ADC_Configuration();//3通道ADC采集配置函数
	/*WIFI始化*/
	WiFi_ResetIO_Init();//初始化WiFi的复位IO
	AliIoT_Parameter_Init();//初始化连接阿里云IoT平台MQTT服务器的参数
}

2、定时器4初始化函数【TIM4_Init()】

提示:以下是该项目的->定时器4初始化函数【TIM4_Init()】,下面代码仅供参考

/*-------------------------------------------------*/
/*函数名:定时器4初始化                            */
/*参  数:arr:自动重装值   0~65535                */
/*参  数:psc:时钟预分频数 0~65535                */
/*返回值:无                                       */
/*说  明:定时时间:arr*psc*1000/72000000  单位ms  */
/*-------------------------------------------------*/
void TIM4_Init(u16 arr,u16 psc)
{  
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//使能TIM4和相关GPIO时钟
	//TIM4 PWM初始化 arr重装载值 psc预分频系数  f=72MHz/(arr+1)/(psc+1) =>1Hz=72000 000/7200/10000
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_Period = arr;//设置自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;//预分频系数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数器向上溢出
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟的分频因子,起到了一点点的延时作用,一般设为TIM_CKD_DIV1
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//TIM4初始化设置(设置PWM的周期)
	TIM_ClearFlag(TIM4,TIM_FLAG_Update);
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	TIM_Cmd(TIM4,ENABLE);//使能TIM4
}

3、OLED更新显示函数【GUI_Refresh()】

提示:以下是该项目的->OLED更新显示函数【GUI_Refresh()】,下面代码仅供参考

/*
函数功能:刷新界面
参数:无
返回值:无
*/
void GUI_Refresh(void)
{
	u8 key_val=KEY_Scan(0);//读取按键的键值
	if(key_val!=0)//只有按键按下才刷屏
	{
		last_index=func_index;//更新上一界面索引值
		switch(key_val)
		{
			case KEY_PREVIOUS: func_index=table[func_index].previous;//更新索引值
					break;
			case KEY_ENTER: func_index=table[func_index].enter;//更新索引值
					break;
			case KEY_NEXT:func_index=table[func_index].next;//更新索引值
					break;
			case KEY_BACK:func_index=table[func_index].back;//更新索引值
					break;
			default:break;
		}
		OLED_Clear();//清屏
	}
	current_operation_func=table[func_index].current_operation;
	(*current_operation_func)(func_index,key_val);//执行当前索引对应的函数
}

二、阿里云服务器连接及信息显示界面【Wifi_Child()】

提示:以下是该项目的->阿里云服务器连接及信息显示界面【Wifi_Child()】,下面代码仅供参考
以下函数是WIFI连接及信息显示界面,通过按键选择进入该界面函数后,执行WIFI和阿里云服务器额的连接、报文的处理以及空气温湿度等数据的上传。

/*
函数功能:Wifi选项子菜单
参数:u8 page_index,u8 key_val
返回值:无
*/
u8 Mode=0;//养护模式模式: 0自动养护模式 1手动养护模式
void Wifi_Child(u8 page_index,u8 key_val)
{
	static u8 cur_pos=1;
	OLED_ShowCHineseWord_Line(0,0,"智能植物养护系统",1);//显示智能植物养护系统
	if(last_index!=_Wifi_Option)//判断是否是第一次进入此界面
	{
		switch(key_val)
		{
			case KEY_PREVIOUS: 
					break;
			case KEY_ENTER: Mode^=1;//切换模式
							Led_flag=0;
							Fan_flag=0;
							Pump_flag=0;
					break;
			case KEY_NEXT: beep_mode^=1;//切换报警模式
					break;
			default:break;
		}
	}
	else cur_pos=1;//第一次进入此界面,界面指针清零
	
	if(cur_pos<=4)
	{
		if(Connect_flag==1)//Connect_flag=1同服务器建立了连接
		{
			Connet_On();//程序连接成功接收处理程序
			Data_State();//数据上传函数
		}else Connet_First();//准备连接服务器
	}
}

该界面主要的逻辑函数如下:
首先STM32单片机进行WIFI的连接,然后再进行阿里云服务器的连接;若未连接上阿里云服务器便一直进行阿里云服务器的连接,当连接阿里云服务器成功之后进行报文的处理和数据的上传!

if(cur_pos<=4)
	{
		if(Connect_flag==1)//Connect_flag=1同服务器建立了连接
		{
			Connet_On();//程序连接成功接收处理程序
			Data_State();//数据上传函数
		}else Connet_First();//准备连接服务器
	}

1、连接阿里云服务器函数【Connet_First()】

提示:以下是该项目的->连接阿里云服务器函数【Connet_First()】,下面代码仅供参考

/*第一次连接*/
/*程序开始连接*/
void Connet_First(void)
{
	u1_printf("准备连接服务器\r\n");//串口输出信息
	OLED_ShowCHineseWord_Line(0,2,">正在连接服务器<",0);//显示:准备连接服务器
	Usart2_Init(115200);//串口2功能初始化,波特率115200	
	//TIM_Cmd(TIM4,DISABLE);//关闭TIM4 
	TIM_Cmd(TIM3,DISABLE);//关闭TIM3  
	TIM_Cmd(TIM2,DISABLE);//关闭TIM2    
	WiFi_RxCounter=0;//WiFi接收数据量变量清零                        
	memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE);//清空WiFi接收缓冲区 
	if(WiFi_Connect_IoTServer()==0)//如果WiFi连接云服务器函数返回0,表示正确,进入if
	{
		u1_printf("建立TCP连接成功\r\n");//串口输出信息
		Delay_Ms(3000);//延时3s
		OLED_Clear();//清屏
		Usart2_IDELInit(115200);//串口2 开启DMA 和 空闲中断
		Connect_flag = 1;//Connect_flag置1,表示连接成功	
		WiFi_RxCounter=0;//WiFi接收数据量变量清零                        
		memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE);//清空WiFi接收缓冲区 
		MQTT_Buff_Init();//初始化发送接收命令缓冲区 
		TIM3_ENABLE_30S();//启动定时器3 30s的PING保活定时器
		TIM2_ENABLE_1S();//启动定时器2 1s的定时器
	}
}

2、报文处理函数【Connet_On()】

提示:以下是该项目的->报文处理函数【Connet_On()】,下面代码仅供参考
连接成功之后处理服务器发来的报文。

/*程序连接成功*/
void Connet_On(void)
{
	if(MQTT_TxDataOutPtr != MQTT_TxDataInPtr)//if成立的话,说明发送缓冲区有数据了
	{
		//3种情况可进入if
		//第1种:0x10 连接报文
		//第2种:0x82 订阅报文,且ConnectPack_flag置位,表示连接报文成功
		//第3种:SubcribePack_flag置位,说明连接和订阅均成功,其他报文可发
		if((MQTT_TxDataOutPtr[2]==0x10)||((MQTT_TxDataOutPtr[2]==0x82)&&(ConnectPack_flag==1))||(SubcribePack_flag==1))
		{
			u1_printf("发送数据:0x%x\r\n",MQTT_TxDataOutPtr[2]);//串口提示信息
			MQTT_TxData(MQTT_TxDataOutPtr);//发送数据
			MQTT_TxDataOutPtr += TBUFF_UNIT;//指针下移
			if(MQTT_TxDataOutPtr==MQTT_TxDataEndPtr)//如果指针到缓冲区尾部了
				MQTT_TxDataOutPtr = MQTT_TxDataBuf[0];//指针归位到缓冲区开头
		}
	}//处理发送缓冲区数据的else if分支结尾
	/*-------------------------------------------------------------*/
	/*                     处理接收缓冲区数据                      */
	/*-------------------------------------------------------------*/
	if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr)//if成立的话,说明接收缓冲区有数据了	
	{
	u1_printf("接收到数据:");//串口提示信息
	/*-----------------------------------------------------*/
	/*                    处理CONNACK报文                  */
	/*-----------------------------------------------------*/				
	//if判断,如果第一个字节是0x20,表示收到的是CONNACK报文
	//接着我们要判断第4个字节,看看CONNECT报文是否成功
	if(MQTT_RxDataOutPtr[2]==0x20){             			
			switch(MQTT_RxDataOutPtr[5]){					
			case 0x00 : u1_printf("CONNECT报文成功\r\n");//串口输出信息	
							ConnectPack_flag = 1;//CONNECT报文成功
						break;//跳出分支case 0x00                                              
			case 0x01 : u1_printf("连接已拒绝,不支持的协议版本,准备重启\r\n");//串口输出信息
						Connect_flag = 0;//Connect_flag置零,重启连接
						break;//跳出分支case 0x01   
			case 0x02 : u1_printf("连接已拒绝,不合格的客户端标识符,准备重启\r\n");//串口输出信息
						Connect_flag = 0;//Connect_flag置零,重启连接
						break;//跳出分支case 0x02 
			case 0x03 : u1_printf("连接已拒绝,服务端不可用,准备重启\r\n");//串口输出信息
						Connect_flag = 0;//Connect_flag置零,重启连接
						break;//跳出分支case 0x03
			case 0x04 : u1_printf("连接已拒绝,无效的用户名或密码,准备重启\r\n");//串口输出信息
						Connect_flag = 0;//Connect_flag置零,重启连接						
						break;//跳出分支case 0x04
			case 0x05 : u1_printf("连接已拒绝,未授权,准备重启\r\n");//串口输出信息
						Connect_flag = 0;//Connect_flag置零,重启连接						
						break;//跳出分支case 0x05 		
			default   : u1_printf("连接已拒绝,未知状态,准备重启\r\n");//串口输出信息 
						Connect_flag = 0;//Connect_flag置零,重启连接					
						break;//跳出分支case default 								
		}	
	}
	/*-----------------------------------------------------*/
	/*                    处理SUBACK报文                   */
	/*-----------------------------------------------------*/				
	//if判断,第一个字节是0x90,表示收到的是SUBACK报文
	//接着我们要判断订阅回复,看看是不是成功
	else if(MQTT_RxDataOutPtr[2]==0x90){
			switch(MQTT_RxDataOutPtr[6]){					
			case 0x00 :
			case 0x01 : u1_printf("订阅成功\r\n");//串口输出信息
								  SubcribePack_flag = 1;//SubcribePack_flag置1,表示订阅报文成功
									/*上报服务器当前设备状态*/
									Param_State();//上传阈值参数
									LED_State();//上报LED状态
									//AIDE_State();//上报语音助手状态
									FAN_State();//上报风扇状态
									PUMP_State();//上报水泵状态
						break;//跳出分支                                             
			default   : u1_printf("订阅失败,准备重启\r\n");//串口输出信息 
						Connect_flag = 0;//Connect_flag置零,重启连接
						break;//跳出分支 								
		}
	}
	/*-----------------------------------------------------*/
	/*                  处理PINGRESP报文                   */
	/*-----------------------------------------------------*/
	//if判断,第一个字节是0xD0,表示收到的是PINGRESP报文
	else if(MQTT_RxDataOutPtr[2]==0xD0){ 
		u1_printf("PING报文回复\r\n");//串口输出信息 
		if(Ping_flag==1){//如果Ping_flag=1,表示第一次发送
			 Ping_flag = 0;//要清除Ping_flag标志
		}else if(Ping_flag>1){//如果Ping_flag>1,表示是多次发送了,而且是2s间隔的快速发送
			Ping_flag = 0;//要清除Ping_flag标志
			TIM3_ENABLE_30S();//PING定时器重回30s的时间
		}
	}
	/*-----------------------------------------------------*/
	/*                  处理数据推送报文                    */
	/*-----------------------------------------------------*/				
	//if判断,如果第一个字节是0x30,表示收到的是服务器发来的推送数据
	//我们要提取控制命令
	else if((MQTT_RxDataOutPtr[2]==0x30)){ 
		u1_printf("服务器等级0推送\r\n");//串口输出信息 
		MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr);//处理等级0推送数据
	}

	MQTT_RxDataOutPtr +=RBUFF_UNIT;//接收指针下移
	if(MQTT_RxDataOutPtr==MQTT_RxDataEndPtr)//如果接收指针到接收缓冲区尾部了
		MQTT_RxDataOutPtr = MQTT_RxDataBuf[0];//接收指针归位到接收缓冲区开头                        
	}
	/*-------------------------------------------------------------*/
	/*                     处理命令缓冲区数据                      */
	/*-------------------------------------------------------------*/
	if(MQTT_CMDOutPtr != MQTT_CMDInPtr)//if成立的话,说明命令缓冲区有数据了			  
	{
		Publish_Message();//订阅信息函数
		MQTT_CMDOutPtr += CBUFF_UNIT;//指针下移
		if(MQTT_CMDOutPtr==MQTT_CMDEndPtr)//如果指针到缓冲区尾部了
			MQTT_CMDOutPtr = MQTT_CMDBuf[0];//指针归位到缓冲区开头				
	}//处理命令缓冲区数据的else if分支结尾	
}

3、数据上传阿里云服务器函数【Data_State()】

提示:以下是该项目的->数据上传阿里云服务器函数【Data_State()】,下面代码仅供参考
将各种数据上传到阿里云服务器显示在手机APP同时也可以在OLED显示屏上显示数据。

/*-------------------------------------------------*/
/*函数名:定时上传各种数据的任务                   */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void Data_State(void)
{
	char temp[500];//缓冲区
	if((SystemTimer-TEHUTimer)>=2)//全局时间和温湿度计时时间至少差2s,进入if
	{
		TEHUTimer = SystemTimer;//把当前的全局时间,记录到温湿度计时变量
		//*数据上传阿里云*//
		//Param_State();//上传阈值参数
	  	Information();//OLED显示实时数据信息函数
		u1_printf("温度:%d  湿度:%d\r\n",tempdata,humidata);//串口输出数据:温湿度
		u1_printf("光照:%d  土壤湿度:%d  土壤PH值:%d\r\n",light_intensity,soil_humi,soil_PH);//串口输出数:光照强度、土壤湿度
		sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\
		\"air_humidity\":%d,\"air_temperature\":%d,\"illuminanceState\":%d,\"soilhumidity\":%d,\"soil_PH\":%d,\
		\"tempdata_hight\":%d,\"tempdata_low\":%d,\"humidata_hight\":%d,\"humidata_low\":%d,\"soil_humi_hight\":%d,\
		\"soil_humi_low\":%d,\"light_intensity_hight\":%d,\"light_intensity_low\":%d,\"soil_PH_hight\":%d,\"soil_PH_low\":%d},\
		\"version\":\"1.0.0\"}",humidata,tempdata,light_intensity,soil_humi,soil_PH,tempdata_hight,tempdata_low,humidata_hight,
		humidata_low,soil_humi_hight,soil_humi_low,light_intensity_hight,light_intensity_low,soil_PH_hight,soil_PH_low);//构建数据
		MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));//添加数据到发送缓冲区
	}
}

※OLED显示实时数据信息函数※【Information()】

提示:以下是该项目的->OLED显示实时数据信息函数【Information()】,下面代码仅供参考
第一行显示:智能植物养护系统
第二行显示:温度: C 湿度: %
第三行显示:光照: % 土湿: %
第三行显示:PH值: 自动模式/手动模式
出现以下四种情况:
1、空气温度过高->打开排风扇
2、空气湿度过高->打开排风扇
3、土壤湿度过低->打开补水泵
4、光照强度过低->打开补光灯

void Information(void)//OLED显示实时数据信息
{
	/*第1行*/
	OLED_ShowCHineseWord_Line(0,0,"智能植物养护系统",1);//显示:智能植物养护系统
	/*第2行*/
	OLED_ShowCHineseWord_Line(0,2,"温度:  C",0);//显示:"温度:  C"
	OLED_ShowNum(40,2,tempdata,2,16);//显示温度
	OLED_ShowCHineseWord_Line(64,2,"湿度:  %",0);//显示:"湿度:  %"
	OLED_ShowNum(104,2,humidata,2,16);//显示湿度
	/*第3行*/
	OLED_ShowCHineseWord_Line(0,4,"光照:  %",0);//显示:"光照:  %"
	OLED_ShowNum(40,4,light_intensity,2,16);//显示光照强度
	OLED_ShowCHineseWord_Line(64,4,"土湿:  %",0);//显示:"土湿:  %"
	OLED_ShowNum(104,4,soil_humi,2,16);//显示土壤湿度
	/*第4行*/
	OLED_ShowCHineseWord_Line(0,6,"PH值:  ",0);//显示:"PH值:  "
	OLED_ShowNum(40,6,soil_PH,2,16);//显示PH值
	
	if(Mode==0)//自动养护模式
	{
		OLED_ShowCHineseWord_Line(64,6,"自动模式",1);//显示:自动模式
		//1、空气温度过高->打开排风扇
		//2、空气湿度过高->打开排风扇
		//3、土壤湿度过低->打开补水泵
		//4、光照强度过低->打开补光灯
		if((tempdata>tempdata_hight)&&(beep_mode==0))//默认启动报警模式
		{
			OLED_ShowCHineseWord_Line(64,6,"温度过高",1);//显示:空气温度过高->打开排风扇
			if((tempdata>tempdata_hight)&&(beep_mode==0)&&(Fan_flag==0))
			{
				data_sta(""APP_air_temperature_alarm"", 1);//给app发布空气温度报警信息
				u3_printf("FAN_ON\r\n");//串口3发送打开风扇指令
				Fan_flag=1;//关闭排风扇启动标志位
			}
			beep_flag=1;//打开蜂鸣器开关
		}else if((tempdata_low>tempdata)&&(beep_mode==0))
		{
			OLED_ShowCHineseWord_Line(64,6,"温度过低",1);//显示:空气温度过低
			beep_flag=1;//打开蜂鸣器开关
		}else if((humidata>humidata_hight)&&(beep_mode==0)) 
		{
			OLED_ShowCHineseWord_Line(64,6,"湿度过高",1);//显示:空气湿度过高->打开排风扇
			if((humidata>humidata_hight)&&(beep_mode==0)&&(Fan_flag==0))
			{
				data_sta(""APP_air_humidity_alarm"",1);//给app发布空气湿度报警信息
				u3_printf("FAN_ON\r\n");//串口3发送打开风扇指令
				Fan_flag=1;//关闭排风扇启动标志位
			}
			beep_flag=1;//打开蜂鸣器开关
		}else if((humidata_low>humidata)&&(beep_mode==0))
		{
			OLED_ShowCHineseWord_Line(64,6,"湿度过低",1);//显示:空气湿度过低
			beep_flag=1;//打开蜂鸣器开关
		}else if((soil_humi>soil_humi_hight)&&(beep_mode==0)) 
		{
			OLED_ShowCHineseWord_Line(64,6,"土湿过高",1);//显示:土湿过高
			beep_flag=1;//打开蜂鸣器开关
		}else if((soil_humi_low>soil_humi)&&(beep_mode==0))
		{
			OLED_ShowCHineseWord_Line(64,6,"土湿过低",1);//显示:土湿过低
			//OLED_ShowString(64,6,"Soil Low",16);//显示土壤湿度过低->打开补水泵
			if((soil_humi_low>soil_humi)&&(beep_mode==0)&&(Pump_flag==0))
			{
				data_sta(""APP_soil_moisture_alarm"", 1);//给app发布土壤湿度报警信息
				u3_printf("PUMP_ON\r\n");//串口3发送打开水泵指令
				Pump_flag=1;//关闭补水泵启动标志位
			}
			beep_flag=1;//打开蜂鸣器开关
		}else if((light_intensity>light_intensity_hight)&&(beep_mode==0)) 
		{
			OLED_ShowCHineseWord_Line(64,6,"光照过高",1);//显示:光照强度过高
			beep_flag=1;//打开蜂鸣器开关
		}else if((light_intensity_low>light_intensity)&&(beep_mode==0))
		{
			OLED_ShowCHineseWord_Line(64,6,"光照过低",1);//显示:光照强度过低->打开补光灯
			if((light_intensity_low>light_intensity)&&(beep_mode==0)&&(Led_flag==0))
			{
				data_sta(""APP_light_intensity_alarm"", 1);//给app发布光照强度报警信息
				u3_printf("LED_ON\r\n");//串口3发送开灯指令
				Led_flag=1;//关闭补光灯启动标志位
			}
			beep_flag=1;//打开蜂鸣器开关
		}
		else if((soil_PH>soil_PH_hight)&&(beep_mode==0))
		{
			OLED_ShowString(64,6,"PH Hig!",16);//显示土壤PH值过高->打开补水泵
			beep_flag=1;//打开蜂鸣器开关
		}else if((soil_PH_low>soil_PH)&&(beep_mode==0))
		{
			OLED_ShowString(64,6,"PH Low!",16);//显示土壤PH值过低
			beep_flag=1;//打开蜂鸣器开关
		}
	}else if(Mode==1)//手动养护模式
	{
		OLED_ShowCHineseWord_Line(64,6,"手动模式",1);//显示:手动模式
	}
}

三、※报文处理函数※【Connet_On()】

1、处理SUBACK报文

当报文订阅成功之后,向服务器上报当前设备的状态,下面代码展示了本项目进行:参数阈值、LED、风扇和水泵的初始状态。

/*-----------------------------------------------------*/
/*                    处理SUBACK报文                   */
/*-----------------------------------------------------*/				
//if判断,第一个字节是0x90,表示收到的是SUBACK报文
//接着我们要判断订阅回复,看看是不是成功
else if(MQTT_RxDataOutPtr[2]==0x90)
{
	switch(MQTT_RxDataOutPtr[6])
	{					
			case 0x00 :
			case 0x01 : u1_printf("订阅成功\r\n");//串口输出信息
						SubcribePack_flag = 1;//SubcribePack_flag置1,表示订阅报文成功
						/*上报服务器当前设备状态*/
						Param_State();//上传阈值参数
						LED_State();//上报LED状态
						//AIDE_State();//上报语音助手状态
						FAN_State();//上报风扇状态
						PUMP_State();//上报水泵状态
						break;//跳出分支                                             
			default   : u1_printf("订阅失败,准备重启\r\n");//串口输出信息 
						Connect_flag = 0;//Connect_flag置零,重启连接
						break;//跳出分支 								
	}
}

2、处理命令缓冲区数据

在命令缓冲区进行执行订阅信息函数。

/*-------------------------------------------------------------*/
/*                     处理命令缓冲区数据                      */
/*-------------------------------------------------------------*/
if(MQTT_CMDOutPtr != MQTT_CMDInPtr)//if成立的话,说明命令缓冲区有数据了			  
{
	Publish_Message();//订阅信息函数
	MQTT_CMDOutPtr += CBUFF_UNIT;//指针下移
	if(MQTT_CMDOutPtr==MQTT_CMDEndPtr)//如果指针到缓冲区尾部了
		MQTT_CMDOutPtr = MQTT_CMDBuf[0];//指针归位到缓冲区开头				
}//处理命令缓冲区数据的else if分支结尾	

四、订阅信息函数【Publish_Message()】

提示:以下是该项目的->订阅信息函数【Publish_Message()】,下面代码仅供参考
用于STM32接收手机APP端进行开关灯、风扇以及水泵和调节各种阈值参数的指令!

/*
下发数据的样式:
命令:{"method":"thing.service.property.set","id":"1087087436","params":{"TempH":41},"version":"1.0.0"}
star_data=params":{"TempH":41},"version":"1.0.0"}
star_data+10=TempH":41},"version":"1.0.0"}
end_data=":41},"version":"1.0.0"}
*/

/*-------------------------------------------------*/
/*函数名:信息订阅函数,从服务器订阅信息              */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void Publish_Message(void)
{
	char *star_data,*end_data; //用于提取标识符
	char identifier_temp[50];//用于存放标识符
	int identifier_data;//用于存放下发的数据
	
	u1_printf("命令:%s\r\n",&MQTT_CMDOutPtr[2]);//串口输出信息
	star_data=strstr((char *)MQTT_CMDOutPtr + 2, (char *)"params");
	
	/*定位得到标识符前面的字符,用于求出标识符有多大*/
	if(star_data==strstr((char *)MQTT_CMDOutPtr + 2, (char *)"params"))
  	{
		end_data=strstr(star_data+10,"\":");//数据尾端:end_data=":41},"version":"1.0.0"}
		memcpy(identifier_temp,star_data+10,(end_data)-(star_data+10));//将标识符提取
		identifier_data = Extract_digit((unsigned char *)end_data+2); //得到下发的数据
		u1_printf("end_data+2为:%s\r\n", end_data+2);//串口输出信息
		u1_printf("identifier_data为:%d\r\n", identifier_data);//串口输出信息
		
		//*空气温湿度、土壤湿度、光照强度和土壤PH值参数修改区*//
		if(!(strcmp(identifier_temp,""APP_Temp_Hight"")))
		{
			tempdata_hight = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Temp_Low"")))
		{
			tempdata_low = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Humi_Hight"")))
		{
			humidata_hight = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Humi_Low"")))
		{
			humidata_low = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Soilhumi_Hight"")))
		{
			soil_humi_hight = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Soilhumi_Low"")))
		{
			soil_humi_low = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Light_Intensity_Hight"")))
		{
			light_intensity_hight = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Light_Intensity_Low"")))
		{
			light_intensity_low = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Soil_PH_Hight"")))
		{
			soil_PH_hight = identifier_data;
		}else if(!(strcmp(identifier_temp,""APP_Soil_PH_Low"")))
		{
			soil_PH_low = identifier_data;
		}
		memset(identifier_temp, 0, 255);
	}
	
	//*补光灯、风扇、水泵控制区*//
	if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"fill_light\":1}"))//如果搜索到"params":{"fill_light":1}说明服务器下发打开开关1
	{
		//LED1_ON;//打开LED
		u3_printf("LED_ON\r\n");//串口3发送开灯指令
		LED_State();//判断补光灯状态,并发布给服务器
	}else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"fill_light\":0}"))//如果搜索到"params":{"fill_light":0}说明服务器下发关闭开关1
	{
		//LED1_OFF;//关闭LED
		u3_printf("LED_OFF\r\n");//串口3发送关灯指令
		LED_State();//判断补光灯状态,并发布给服务器
	}else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"Voice_Assistant\":1}"))//如果搜索到"params":{"Voice_Assistant":1}说明服务器下发打开开关2
	{
		//LED2_ON;//打开语音助手
		u3_printf("AIDE_ON\r\n");//串口3发送打开语音助手指令
		AIDE_State();//判断开关状态,并发布给服务器
	}else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"Voice_Assistant\":0}"))//如果搜索到"params":{"Voice_Assistant":0}说明服务器下发关闭开关2
	{
		//LED2_OFF;//关闭语音助手
		u3_printf("AIDE_OFF\r\n");//串口3发送关闭语音助手指令
		AIDE_State();//判断开关状态,并发布给服务器
	}else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"exhaust_fan\":1}"))//如果搜索到"params":{"exhaust_fan":1}说明服务器下发打开开关3
	{
		//LED3_ON;//打开风扇
		u3_printf("FAN_ON\r\n");//串口3发送打开风扇指令
		FAN_State();//判断风扇状态,并发布给服务器
	}else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"exhaust_fan\":0}"))//如果搜索到"params":{"exhaust_fan":0}说明服务器下发关闭开关3
	{
		//LED3_OFF;//关闭风扇
		u3_printf("FAN_OFF\r\n");//串口3发送关闭风扇指令
		FAN_State();//判断风扇状态,并发布给服务器
	}else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"small_pump\":1}"))//如果搜索到"params":{"small_pump":1}说明服务器下发打开开关4
	{
		//LED4_ON;//打开水泵
		u3_printf("PUMP_ON\r\n");//串口3发送打开水泵指令
		PUMP_State();//判断水泵状态,并发布给服务器
	}else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"small_pump\":0}"))//如果搜索到"params":{"small_pump":0}说明服务器下发关闭开关4
	{
		//LED4_OFF;//关闭水泵
		u3_printf("PUMP_OFF\r\n");//串口3发送关闭水泵指令
		PUMP_State();//判断水泵状态,并发布给服务器
	}
}

五、各设备状态上传服务器函数

提示:以下是该项目的->各设备状态上传服务器函数【Param_State()】【LED_State()】【FAN_State()】【PUMP_State()】,下面代码仅供参考

1、各种参数数据状态上传函数

/*-------------------------------------------------*/
/*函数名:上传各种参数数据的任务                   */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void Param_State(void)
{
	char temp[TBUFF_UNIT];//缓冲区
	sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\
	\"tempdata_hight\":%d,\"tempdata_low\":%d,\"humidata_hight\":%d,\"humidata_low\":%d,\"soil_humi_hight\":%d,\
	\"soil_humi_low\":%d,\"light_intensity_hight\":%d,\"light_intensity_low\":%d,\"soil_PH_hight\":%d,\"soil_PH_low\":%d},\
	\"version\":\"1.0.0\"}",tempdata_hight,tempdata_low,humidata_hight,humidata_low,soil_humi_hight,soil_humi_low,light_intensity_hight,light_intensity_low,soil_PH_hight,soil_PH_low);//构建数据
	MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));//添加数据到发送缓冲区
}

2、LED状态上传函数

/*-------------------------------------------------*/
/*函数名:判断LED的状态,并发布给服务器              */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void LED_State(void)
{
	char temp[TBUFF_UNIT];//定义一个临时缓冲区
	if(LED_STA==1) sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\"fill_light\":1},\"version\":\"1.0.0\"}");
	else            sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\"fill_light\":0},\"version\":\"1.0.0\"}");
	MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));//添加数据,发布给服务器	
}

2、风扇状态上传函数

/*-------------------------------------------------*/
/*函数名:判断风扇状态,并发布给服务器               */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void FAN_State(void)
{
	char temp[TBUFF_UNIT];//定义一个临时缓冲区
	if(FAN_STA==1) sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\"exhaust_fan\":1},\"version\":\"1.0.0\"}");
	else            sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\"exhaust_fan\":0},\"version\":\"1.0.0\"}");
	MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));//添加数据,发布给服务器		
}

2、水泵状态上传函数

/*-------------------------------------------------*/
/*函数名:判断水泵状态,并发布给服务器              */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void PUMP_State(void)
{
	char temp[TBUFF_UNIT];//定义一个临时缓冲区
	if(PUMP_STA==1) sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\"small_pump\":1},\"version\":\"1.0.0\"}");
	else            sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\"small_pump\":0},\"version\":\"1.0.0\"}");
	MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));//添加数据,发布给服务器		
}

总结

【工程链接】:https://oshwhub.com/wyb1661463799/zhi-neng-zhi-wu-yang-hu-ji-tong-she-ji
【代码链接】:https://www.wcfaka.com/details/AE408673
【项目备注】:原理图和PCB见上方的【工程链接】,希望该设计能够为大家提供一些帮助,同时也感谢大家一键三连对UP的支持。最新代码暂不更新,有需要的可以点击上方【代码链接】获取哦!
下面是视频介绍:

历时一个月的毕业设计—硬件部分介绍


历时一个月的毕业设计成果演示(一)

历时一个月的毕业设计成果演示(二)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

神奇的汪同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值