基于STM32和oneNET云平台的数据采集系统(MQTT协议)

news2024/9/20 18:52:15

文章目录

  • 前言
  • 一、onenet云平台产品创建
  • 二、硬件选择
  • 三、设计理念
  • 四、实战编程
    • 1. 传感器部分
    • 2. ESP8266
    • 3. 定时器
    • 4. 串口
    • 5. MQTT
  • 五、进阶练习


前言

该篇为基于stm32+esp8266通过mqtt协议连接onenet物联网云平台,单片机部分将采集到的数据(温湿度、光照强度、压强等等)上传至云平台服务器,云平台可下发指令操控单片机,实现远程通信。

一、onenet云平台产品创建

1. 第一步,注册账号后点击右上角 控制台
在这里插入图片描述

2. 第二步,看左上角 选择切换旧版本
在这里插入图片描述

3. 第三步,左上角,全部产品中选择多协议接入
在这里插入图片描述

4. 点击添加产品,填好产品信息(红框重点)
在这里插入图片描述

5. 选择添加设备
在这里插入图片描述
6. 至此,完成产品创建,示例如下:
在这里插入图片描述

二、硬件选择

  • stm32开发板
    原子哥、野火老师等各类开发板都可。
  • esp8266 WiFi模块
    ATK-ESP-01或01S都可
  • 各类传感器
    DHT11温湿度传感器、GY-39光强度传感器、MQ-2烟雾传感器、led灯等等

三、设计理念

  1. 本项目需将传感器采集到的数据打印在串口供自己查看,所以这里需消费一个串口

  2. esp8266需通过串口连接服务器,接收云服务器发送来的数据,所以这里也需消费一个串口

  3. 单片机(客户端)需每隔一段时间向云服务器发送ping命令(心跳包),用于保持和服务器连接,因为长时间没给服务器发送数据会被服务器强制踢下线,所以这里需消费一个定时器

  4. 每隔一段时间需检测云服务器那边有没有向这边发送指令,并将串口二接收到的数据依次放入MQTT接收缓冲器中,并及时处理,所以这里需消费一个定时器

  5. 将传感器采集到的数据每隔一段时间重发更新,so,这里也需要消费一个定时器

综上,共消费两个串口,三个定时器。

云端实时检测与远程操控:
在这里插入图片描述
在这里插入图片描述

四、实战编程

1. 传感器部分

(1)DHT11

/*-------------------------------------------------*/
/*函数名:复位DHT11                                */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/  
void DHT11_Rst(void)	   
{                 
  DHT11_IO_OUT(); 	//设置IO输出模式
  DHT11_OUT(0); 	//拉低IO
  DelayMs(30);      //拉低至少18ms,我们拉低30
  DHT11_OUT(1); 	//拉高IO
  DelayUs(30);      //主机拉高20~40us,我们拉高30us
}

/*-------------------------------------------------*/
/*函数名:等待DHT11的回应                           */
/*参  数:无                                       */
/*返回值:1错误 0正确                               */
/*-------------------------------------------------*/ 
char DHT11_Check(void) 	   
{   
	char timeout;                            //定义一个变量用于超时判断  
	
	timeout = 0;                             //超时变量清零    
	DHT11_IO_IN();                           //IO设置输入模式
    while((DHT11_DQ_IN == 1) && (timeout < 70))//DHT11会拉低40~50us,我们等待70us超时时间	
	{	 
		timeout++;                           //超时变量+1
		DelayUs(1);                       	 //延时1us
	} 
	if(timeout >= 70)return 1;               //如果timeout>=70,说明是因为超时退出的while循环,返回1表示错误
	else timeout = 0;                        //反之,说明是因为等到了DHT11拉低IO,退出的while循环,正确并清零timeout
    while((DHT11_DQ_IN == 0) && (timeout < 70))//DHT11拉低后会再次拉高40~50us,,我们等待70us超时时间	
	{ 		
		timeout++;                           //超时变量+1
		DelayUs(1);                          //延时1us
	}
	if(timeout >= 70)return 2;               //如果timeout>=70,说明是因为超时退出的while循环,返回2表示错误  
	return 0;                                //反之正确,返回0
}
/*-------------------------------------------------*/
/*函数名:读取一个位                                */
/*参  数:无                                       */
/*返回值:1或0                                     */
/*-------------------------------------------------*/ 
char DHT11_Read_Bit(void) 			 
{
 	char timeout;                          	   //定义一个变量用于超时判断  
	
	timeout = 0;                               //清零timeout	
	while((DHT11_DQ_IN == 1) && (timeout < 40))//每一位数据开始,是12~14us的低电平,我们等40us
	{   
		timeout++;                             //超时变量+1
		DelayUs(1);                            //延时1us
	}
	timeout = 0;                               //清零timeout	
	while((DHT11_DQ_IN == 0) && (timeout < 60))//接下来,DHT11会拉高IO,根据拉高的时间判断是0或1,我们等60us
	{  
		timeout++;                             //超时变量+1
		DelayUs(1);                            //延时1us
	}
	DelayUs(35);                               //延时35us
	if(DHT11_DQ_IN)return 1;                   //如果延时后,是高电平,那么本位接收的是1,返回1
	else return 0;		                       //反之延时后,是低电平,那么本位接收的是0,返回0
}

/*-------------------------------------------------*/
/*函数名:读取一个字节                              */
/*参  数:无                                       */
/*返回值:数据                                      */
/*-------------------------------------------------*/ 
char DHT11_Read_Byte(void)    
{        
    char i;                       				//定义一个变量用于for循环  
	char dat;                              		//定义一个变量用于保存数据 
	dat = 0;	                        		//清除保存数据的变量
	for (i = 0; i < 8; i++){              		//一个字节8位,循环8次	
   		dat <<= 1;                    			//左移一位,腾出空位    
	    dat |= DHT11_Read_Bit();      			//读取一位数据
    }						    
    return dat;                   				//返回一个字节的数据
}

/*-------------------------------------------------*/
/*函数名:读取一次数据温湿度                         */
/*参  数:temp:温度值                               */
/*参  数:humi:湿度值                               */
/*返回值:1错误 0正确                               */
/*-------------------------------------------------*/ 
char DHT11_Read_Data(char *temp, char *humi)    
{        
 	char buf[5];                                         //一次完整的数据有5个字节,定义一个缓冲区
	
	char i;                                              //定义一个变量用于for循环  
	DHT11_Rst();                                         //复位DHT11	
	if(DHT11_Check() == 0)							     //判断DHT11回复状态=0的话,表示正确,进入if
	{
		for(i = 0; i < 5; i++){                          //一次完整的数据有5个字节,循环5次		
			buf[i] = DHT11_Read_Byte();                  //每次读取一个字节
		}
		if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])//判断数据校验,前4个字节相加应该等于第5个字节,正确的话,进入if	
		{     
			//u1_printf("%d\r\n",buf[0]); 	//温度数据
			//u1_printf("%d\r\n",buf[1]);
			//u1_printf("%d\r\n",buf[2]);	//湿度数据
			//u1_printf("%d\r\n",buf[3]);
			//u1_printf("%d\r\n",buf[4]);
			*humi = buf[0];                              //湿度数据,保存在humi指针指向的地址变量中
			*temp = buf[2];							     //温度数据,保存在temp指针指向的地址变量中
		}else return 1;                                  //反之,数据校验错误,直接返回1
	}else return 2;                                      //反之,如果DHT11回复状态=1的话,表示错误,进入else,直接返回2
	
	return 0;	                                         //读取正确返回0    
}  

/*-------------------------------------------------*/
/*函数名:初始化DHT11                              */
/*参  数:无                                       */
/*返回值:1错误 0正确                              */
/*-------------------------------------------------*/    	 
char DHT11_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;                  //定义一个IO端口参数结构体
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
	
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6;            //准备设置PA8
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     //速率50Mhz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   	  //推免输出方式
	GPIO_Init(GPIOA, &GPIO_InitStructure);            	  //设置PA8	
	DHT11_Rst();                                          //复位DHT11
	return DHT11_Check();                                 //返回DHT11的回复状态
}

(2)MQ-2

void Adc_Init(void)
{
    ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );	  //使能ADC1通道时钟
 

	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

	//PA1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1 

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;	//模数转换工作在单通道模式
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//模数转换工作在单次转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   

  
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
 
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能
}

/**************************** ADC转换值获取函数 ****************************
功  能:获取ADC模块转换后的数值
参  数:无
返回值:存放ADC转换值
***************************************************************************/
u16 Get_adcvalue(void)
{
	//设置指定ADC的规则组通道,一个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  			    
  
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}

u16 Get_Adc_Average(u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=Get_adcvalue();
		DelayMs(5);
	}
	return temp_val/times;
}

(3)LED

/*-------------------------------------------------*/
/*函数名:初始化LED函数                       	    */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void LED_Init(void)
{    	 
	GPIO_InitTypeDef GPIO_InitStr;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE);     //使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE ,ENABLE);
	
	    
	
	GPIO_InitStr.GPIO_Mode=GPIO_Mode_Out_PP;    //结构体变量赋值
	GPIO_InitStr.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStr.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIO_InitStr);       //初始化GPIOB
	GPIO_SetBits(GPIOB,GPIO_Pin_5);       //赋予 GPIO_Pin_5 初始为高电平
	
	
	GPIO_InitStr.GPIO_Mode=GPIO_Mode_Out_PP;  
	GPIO_InitStr.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStr.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOE,&GPIO_InitStr);
	GPIO_SetBits(GPIOE,GPIO_Pin_5);
}

/*-------------------------------------------------*/
/*函数名:LED开启                                  */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void LED_On(void)
{			
	GPIO_ResetBits(GPIOB, GPIO_Pin_5); 						 //PD2 输出低
} 


/*-------------------------------------------------*/
/*函数名:LED关闭                                  */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void LED_Off(void)
{		
	GPIO_SetBits(GPIOB, GPIO_Pin_5); 						 //PD2 输出高
}

2. ESP8266

/*-------------------------------------------------*/
/*函数名:初始化WiFi的复位IO                       */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void WiFi_ResetIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;                    //定义一个设置IO端口参数的结构体
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA , ENABLE); //使能PA端口时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;               //准备设置PA4
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       //速率50Mhz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   	    //推免输出方式
	GPIO_Init(GPIOA, &GPIO_InitStructure);            	    //设置PA4
	RESET_IO(1);                                            //复位IO拉高电平
}
/*-------------------------------------------------*/
/*函数名:WiFi发送设置指令                          */
/*参  数:cmd:指令                                */
/*参  数:timeout:超时时间(100ms的倍数)          */
/*返回值:0:正确   其他:错误                      */
/*-------------------------------------------------*/
char WiFi_SendCmd(char *cmd, int timeout)
{
	WiFi_RxCounter = 0;                           			//WiFi接收数据量变量清零                        
	memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE);     			//清空WiFi接收缓冲区 
	WiFi_printf("%s\r\n", cmd);                  			//发送指令
	while(timeout--)										//等待超时时间到0
	{                           			
		DelayMs(100);                             		    //延时100ms
		if(strstr(WiFi_RX_BUF, "OK"))              			//如果接收到OK表示指令成功
			break;       									//主动跳出while循环
		u1_printf("%d ", timeout);                 			//串口输出现在的超时时间
	}			
	u1_printf("\r\n");                          			//串口输出信息
	if(timeout <= 0)return 1;                    				//如果timeout<=0,说明超时时间到了,也没能收到OK,返回1
	else return 0;		         							//反之,表示正确,说明收到OK,通过break主动跳出while
}
/*-------------------------------------------------*/
/*函数名:WiFi复位                                 */
/*参  数:timeout:超时时间(100ms的倍数)          */
/*返回值:0:正确   其他:错误                      */
/*-------------------------------------------------*/
char WiFi_Reset(int timeout)
{
	RESET_IO(0);                                    	  //复位IO拉低电平
	DelayMs(500);                                  		  //延时500ms
	RESET_IO(1);                                   		  //复位IO拉高电平	
	while(timeout--)									  //等待超时时间到0 
	{                              		  
		DelayMs(100);                                 	  //延时100ms
		if(strstr(WiFi_RX_BUF, "ready"))               	  //如果接收到ready表示复位成功
			break;       						   		  //主动跳出while循环
		u1_printf("%d ", timeout);                     	  //串口输出现在的超时时间
	}
	u1_printf("\r\n");                              	  //串口输出信息
	if(timeout <= 0)return 1;                        		  //如果timeout<=0,说明超时时间到了,也没能收到ready,返回1
	else return 0;		         				   		  //反之,表示正确,说明收到ready,通过break主动跳出while
}
/*-------------------------------------------------*/
/*函数名:WiFi加入路由器指令                       */
/*参  数:timeout:超时时间(1s的倍数)            */
/*返回值:0:正确   其他:错误                     */
/*-------------------------------------------------*/
char WiFi_JoinAP(int timeout)
{		
	WiFi_RxCounter = 0;                                    //WiFi接收数据量变量清零                        
	memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE);              //清空WiFi接收缓冲区 
	WiFi_printf("AT+CWJAP=\"%s\",\"%s\"\r\n", SSID, PASS); //发送指令	
	while(timeout--)									   //等待超时时间到0
	{                                   
		DelayMs(1000);                             		   //延时1s
		if(strstr(WiFi_RX_BUF, "OK"))   //如果接收到WIFI GOT IP表示成功
			break;       						           //主动跳出while循环
		u1_printf("%d ", timeout);                         //串口输出现在的超时时间
	}
	u1_printf("\r\n%s\r\n", WiFi_RX_BUF);
	u1_printf("\r\n");                             	       //串口输出信息
	if(timeout <= 0)return 1;                              //如果timeout<=0,说明超时时间到了,也没能收到WIFI GOT IP,返回1
	return 0;                                              //正确,返回0
}
/*-------------------------------------------------*/
/*函数名:连接TCP服务器,并进入透传模式            */
/*参  数:timeout: 超时时间(100ms的倍数)        */
/*返回值:0:正确  其他:错误                      */
/*-------------------------------------------------*/
char WiFi_Connect_Server(int timeout)
{	
	WiFi_RxCounter=0;                              	//WiFi接收数据量变量清零                        
	memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE);         //清空WiFi接收缓冲区   
	WiFi_printf("AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", ServerIP, ServerPort);//发送连接服务器指令
	while(timeout--)								//等待超时与否
	{                           
		DelayMs(100);                             	//延时100ms	
		if(strstr(WiFi_RX_BUF, "CONNECT"))          //如果接受到CONNECT表示连接成功
			break;                                  //跳出while循环
		if(strstr(WiFi_RX_BUF, "CLOSED"))           //如果接受到CLOSED表示服务器未开启
			return 1;                               //服务器未开启返回1
		if(strstr(WiFi_RX_BUF, "ALREADY CONNECTED"))//如果接受到ALREADY CONNECTED已经建立连接
			return 2;                               //已经建立连接返回2
		u1_printf("%d ", timeout);                   //串口输出现在的超时时间  
	}
	u1_printf("\r\n");                              //串口输出信息
	if(timeout <= 0)return 3;                       //超时错误,返回3
	else                                            //连接成功,准备进入透传
	{
		u1_printf("连接服务器成功,准备进入透传\r\n"); //串口显示信息
		WiFi_RxCounter = 0;                          //WiFi接收数据量变量清零                        
		memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE);    //清空WiFi接收缓冲区     
		WiFi_printf("AT+CIPSEND\r\n");               //发送进入透传指令
		while(timeout--)							 //等待超时与否
		{                            
			DelayMs(100);                            //延时100ms	
			if(strstr(WiFi_RX_BUF, "\r\nOK\r\n\r\n>"))//如果成立表示进入透传成功
				break;                          	 //跳出while循环
			u1_printf("%d ", timeout);                //串口输出现在的超时时间  
		}
		if(timeout <= 0)return 4;                      //透传超时错误,返回4	
	}
	return 0;	                                     //成功返回0	
}
/*-------------------------------------------------*/
/*函数名:WiFi_Smartconfig                         */
/*参  数:timeout:超时时间(1s的倍数)            */
/*返回值:0:正确   其他:错误                     */
/*-------------------------------------------------*/
char WiFi_Smartconfig(int timeout)
{
	
	WiFi_RxCounter=0;                           		//WiFi接收数据量变量清零                        
	memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE);     		//清空WiFi接收缓冲区     
	while(timeout--)									//等待超时时间到0
	{                           		
		DelayMs(1000);                         				//延时1s
		if(strstr(WiFi_RX_BUF, "connected"))    	 		  //如果串口接受到connected表示成功
			break;                                  		//跳出while循环  
		u1_printf("%d ", timeout);                 		//串口输出现在的超时时间  
	}	
	u1_printf("\r\n");                          		//串口输出信息
	if(timeout <= 0)return 1;                     		//超时错误,返回1
	return 0;                                   		//正确返回0
}
/*-------------------------------------------------*/
/*函数名:等待加入路由器                           */
/*参  数:timeout:超时时间(1s的倍数)            */
/*返回值:0:正确   其他:错误                     */
/*-------------------------------------------------*/
char WiFi_WaitAP(int timeout)
{		
	while(timeout--){                               //等待超时时间到0
		DelayMs(1000);                             		//延时1s
		if(strstr(WiFi_RX_BUF, "WIFI GOT IP"))         //如果接收到WIFI GOT IP表示成功
			break;       						   								  //主动跳出while循环
		u1_printf("%d ", timeout);                     //串口输出现在的超时时间
	}
	u1_printf("\r\n");                              //串口输出信息
	if(timeout <= 0)return 1;                         //如果timeout<=0,说明超时时间到了,也没能收到WIFI GOT IP,返回1
	return 0;                                       //正确,返回0
}
/*-------------------------------------------------*/
/*函数名:WiFi连接服务器                           */
/*参  数:无                                       */
/*返回值:0:正确   其他:错误                     */
/*-------------------------------------------------*/
char WiFi_Connect_IoTServer(void)
{	
	u1_printf("准备复位模块\r\n");                   //串口提示数据
	if(WiFi_Reset(50))								//复位,100ms超时单位,总计5s超时时间
	{                             
		u1_printf("复位失败,准备重启\r\n");	      //返回非0值,进入if,串口提示数据
		return 1;                                   //返回1
	}else u1_printf("复位成功\r\n");                 //串口提示数据
	
	u1_printf("准备设置STA模式\r\n");                //串口提示数据
	if(WiFi_SendCmd("AT+CWMODE=1",50))//设置STA模式,100ms超时单位,总计5s超时时间
	{             
		u1_printf("设置STA模式失败,准备重启\r\n");   //返回非0值,进入if,串口提示数据
		return 2;                                   //返回2
	}else u1_printf("设置STA模式成功\r\n");          //串口提示数据
	
	if(wifi_mode==0) //如果联网模式=0:SSID和密码写在程序里 
	{                              
		u1_printf("准备取消自动连接\r\n");            //串口提示数据
		if(WiFi_SendCmd("AT+CWAUTOCONN=0",50))		 //取消自动连接,100ms超时单位,总计5s超时时间
		{       
			u1_printf("取消自动连接失败,准备重启\r\n"); //返回非0值,进入if,串口提示数据
			return 3;                                  //返回3
		}else u1_printf("取消自动连接成功\r\n");        //串口提示数据
				
		u1_printf("准备连接路由器\r\n");                //串口提示数据	
		if(WiFi_JoinAP(30))//连接路由器,1s超时单位,总计30s超时时间
		{                          
			u1_printf("连接路由器失败,准备重启\r\n");  //返回非0值,进入if,串口提示数据
			return 4;                                 //返回4	
		}else u1_printf("连接路由器成功\r\n");         //串口提示数据			
	}
	
	u1_printf("准备设置透传\r\n");                    //串口提示数据
	if(WiFi_SendCmd("AT+CIPMODE=1",50)) 			 //设置透传,100ms超时单位,总计5s超时时间
	{           
		u1_printf("设置透传失败,准备重启\r\n");       //返回非0值,进入if,串口提示数据
		return 8;                                    //返回8
	}else u1_printf("设置透传成功\r\n");              //串口提示数据
	
	u1_printf("准备关闭多路连接\r\n");                //串口提示数据
	if(WiFi_SendCmd("AT+CIPMUX=0",50)) 				 //关闭多路连接,100ms超时单位,总计5s超时时间
	{            
		u1_printf("关闭多路连接失败,准备重启\r\n");   //返回非0值,进入if,串口提示数据
		return 9;                                    //返回9
	}else u1_printf("关闭多路连接成功\r\n");          //串口提示数据
	 
	u1_printf("准备连接服务器\r\n");                  //串口提示数据
	if(WiFi_Connect_Server(100))      				 //连接服务器,100ms超时单位,总计10s超时时间
	{            
		u1_printf("连接服务器失败,准备重启\r\n");     //返回非0值,进入if,串口提示数据
		return 10;                                   //返回10
	}else u1_printf("连接服务器成功\r\n");            //串口提示数据	
	return 0;                                        //正确返回0
}
	

3. 定时器

(1)初始化

/*-------------------------------------------------*/
/*函数名:定时器2使能10s定时                        */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void TIM2_ENABLE_10S(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;             //定义一个设置定时器的变量
	NVIC_InitTypeDef NVIC_InitStructure;                           //定义一个设置中断的变量	
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3		
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);            //使能TIM2时钟	
	TIM_DeInit(TIM2);                                              //定时器2寄存器恢复默认值	
	TIM_TimeBaseInitStructure.TIM_Period = 20000-1; 	           //设置自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 36000-1;             //设置定时器预分频数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    //1分频
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);            //设置TIM2
	
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);                    //清除溢出中断标志位
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);                     //使能TIM2溢出中断    
	TIM_Cmd(TIM2, ENABLE);                                         //开TIM2                          
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;                //设置TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;      //抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;             //子优先级1
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //中断通道使能
	NVIC_Init(&NVIC_InitStructure);                                //设置中断
}

/*-------------------------------------------------*/
/*函数名:定时器3使能30s定时                       */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void TIM3_ENABLE_30S(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;             //定义一个设置定时器的变量
	NVIC_InitTypeDef NVIC_InitStructure;                           //定义一个设置中断的变量
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3		
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);           //使能TIM3时钟	
	TIM_DeInit(TIM3);                                              //定时器3寄存器恢复默认值	
	TIM_TimeBaseInitStructure.TIM_Period = 60000-1; 	           //设置自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 36000-1;             //设置定时器预分频数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    //1分频
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);            //设置TIM3
	
	TIM_ClearITPendingBit(TIM3, TIM_IT_Update);                    //清除溢出中断标志位
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);                     //使能TIM3溢出中断    
	TIM_Cmd(TIM3, ENABLE);                                         //开TIM3                          
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;                //设置TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;      //抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;             //子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //中断通道使能
	NVIC_Init(&NVIC_InitStructure);                                //设置中断
}
/*-------------------------------------------------*/
/*函数名:定时器3使能2s定时                        */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void TIM3_ENABLE_2S(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;             //定义一个设置定时器的变量
	NVIC_InitTypeDef NVIC_InitStructure;                           //定义一个设置中断的变量
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3		
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);           //使能TIM3时钟
	TIM_DeInit(TIM3);                                              //定时器3寄存器恢复默认值	
	TIM_TimeBaseInitStructure.TIM_Period = 20000-1; 	           //设置自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;              //设置定时器预分频数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    //1分频
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);            //设置TIM3
	
	TIM_ClearITPendingBit(TIM3, TIM_IT_Update);                    //清除溢出中断标志位
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);                     //使能TIM3溢出中断    
	TIM_Cmd(TIM3, ENABLE);                                         //开TIM3                          
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;                //设置TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;      //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;             //子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //中断通道使能
	NVIC_Init(&NVIC_InitStructure);                                //设置中断
}

/*-------------------------------------------------*/
/*函数名:定时器4初始化                            */
/*参  数:arr:自动重装值   0~65535                */
/*参  数:psc:时钟预分频数 0~65535                */
/*返回值:无                                       */
/*说  明:定时时间:arr*psc*1000/72000000  单位ms  */
/*-------------------------------------------------*/
void TIM4_Init(unsigned short int arr, unsigned short int psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;              //定义一个设置定时器的变量
	NVIC_InitTypeDef NVIC_InitStructure;                            //定义一个设置中断的变量
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                 //设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3		
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);            //使能TIM4时钟	
    TIM_TimeBaseInitStructure.TIM_Period = arr; 	                //设置自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;                  //设置定时器预分频数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //1分频
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);             //设置TIM4
	
	TIM_ClearITPendingBit(TIM4, TIM_IT_Update);                     //清除溢出中断标志位
	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);                      //使能TIM4溢出中断    
	TIM_Cmd(TIM4, DISABLE);                                         //先关闭TIM4                          
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;                 //设置TIM4中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;       //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;              //子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                 //中断通道使能
	NVIC_Init(&NVIC_InitStructure);                                 //设置中断
}

(2)中断

/*-------------------------------------------------*/
/*函数名:定时器4中断服务函数。处理MQTT数据          */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void TIM4_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)//如果TIM_IT_Update置位,表示TIM4溢出中断,进入if	
	{                	
		memcpy(&MQTT_RxDataInPtr[2], Usart2_RxBuff, Usart2_RxCounter);  //拷贝数据到接收缓冲区
		MQTT_RxDataInPtr[0] = Usart2_RxCounter/256;                  	//记录数据长度高字节
		MQTT_RxDataInPtr[1] = Usart2_RxCounter%256;					 	//记录数据长度低字节
		MQTT_RxDataInPtr += RBUFF_UNIT;                                	//指针下移
		if(MQTT_RxDataInPtr == MQTT_RxDataEndPtr)                     	//如果指针到缓冲区尾部了
			MQTT_RxDataInPtr = MQTT_RxDataBuf[0];                    	//指针归位到缓冲区开头
		Usart2_RxCounter = 0;                                        	//串口2接收数据量变量清零
		TIM_SetCounter(TIM3, 0);                                     	//清零定时器3计数器,重新计时ping包发送时间
		TIM_Cmd(TIM4, DISABLE);                        				 	//关闭TIM4定时器
		TIM_SetCounter(TIM4, 0);                        			 	//清零定时器4计数器
		TIM_ClearITPendingBit(TIM4, TIM_IT_Update);     			 	//清除TIM4溢出中断标志 	
	}
}
/*-------------------------------------------------*/
/*函数名:定时器3中断服务函数                      */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//如果TIM_IT_Update置位,表示TIM3溢出中断,进入if	
		{  
		switch(pingFlag) 					//判断pingFlag的状态
		{                               
			case 0:							//如果pingFlag等于0,表示正常状态,发送Ping报文  
					MQTT_PingREQ(); 		//添加Ping报文到发送缓冲区  
					break;
			case 1:							//如果pingFlag等于1,说明上一次发送到的ping报文,没有收到服务器回复,所以1没有被清除为0,可能是连接异常,我们要启动快速ping模式
					TIM3_ENABLE_2S(); 	    //我们将定时器6设置为2s定时,快速发送Ping报文
					MQTT_PingREQ();			//添加Ping报文到发送缓冲区  
					break;
			case 2:							//如果pingFlag等于2,说明还没有收到服务器回复
			case 3:				            //如果pingFlag等于3,说明还没有收到服务器回复
			case 4:				            //如果pingFlag等于4,说明还没有收到服务器回复	
					MQTT_PingREQ();  		//添加Ping报文到发送缓冲区 
					break;
			case 5:							//如果pingFlag等于5,说明我们发送了多次ping,均无回复,应该是连接有问题,我们重启连接
					connectFlag = 0;        //连接状态置0,表示断开,没连上服务器
					TIM_Cmd(TIM3, DISABLE); //关TIM3 				
					break;			
		}
		pingFlag++;           		   		//pingFlag自增1,表示又发送了一次ping,期待服务器的回复
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIM3溢出中断标志 	
	}
}

/*-------------------------------------------------*/
/*函数名:定时器2中断服务函数                      */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
	extern u16 ADC_Val;
void TIM2_IRQHandler(void)
{
	char humidity;				//定义一个变量,保存湿度值
	char temperature;			//定义一个变量,保存温度值	
				
	char head1[3];
	char temp[50];				//定义一个临时缓冲区1,不包括报头
	char tempAll[100];			//定义一个临时缓冲区2,包括所有数据
	
	int	dataLen = 0;			//报文长度
	ADC_Val_Disp(10,20);    //气敏检测
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)	
	{ 
		DHT11_Read_Data(&temperature,&humidity);//读取温湿度值

		memset(temp,    0, 50);				    //清空缓冲区1
		memset(tempAll, 0, 100);				//清空缓冲区2
		memset(head1,   0, 3);					//清空MQTT头

		sprintf(temp,"{\"MQ\":\"%d\",\"TM\":\"%d\",\"HM\":\"%d\"}",
					  	(ADC_Val/100), temperature, humidity);//构建报文

		head1[0] = 0x03; 						//固定报头
		head1[1] = 0x00; 						//固定报头
		head1[2] = strlen(temp);  				//剩余长度	
		sprintf(tempAll, "%c%c%c%s", head1[0], head1[1], head1[2], temp);
		
		u1_printf("\r\n"); //串口显示相关数据
		u1_printf("%s\r\n", tempAll + 3);
		
		dataLen = strlen(temp) + 3;
		MQTT_PublishQs0(Data_TOPIC_NAME,tempAll, dataLen);//添加数据,发布给服务器
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);   	
	}
}

4. 串口

(1)初始化

/*-------------------------------------------------*/
/*函数名:初始化串口1发送功能                      */
/*参  数:bound:波特率                            */
/*返回值:无                                       */
/*-------------------------------------------------*/
void Usart1_Init(unsigned int bound)
{  	 	
    GPIO_InitTypeDef GPIO_InitStructure;     //定义一个设置GPIO功能的变量
	USART_InitTypeDef USART_InitStructure;   //定义一个设置串口功能的变量
   
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能串口1时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;              //准备设置PA9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //IO速率50M
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	       //复用推挽输出,用于串口1的发送
    GPIO_Init(GPIOA, &GPIO_InitStructure);                 //设置PA9
   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;             //准备设置PA10 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入,用于串口1的接收
    GPIO_Init(GPIOA, &GPIO_InitStructure);                 //设置PA10
	
	USART_InitStructure.USART_BaudRate = bound;                                    //波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;                    //8个数据位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;                         //1个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;                            //无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
                                                                                   //如果不使能接收模式
	USART_InitStructure.USART_Mode = USART_Mode_Tx ;	                           //只发模式        
    USART_Init(USART1, &USART_InitStructure);                                      //设置串口1	
	USART_Cmd(USART1, ENABLE);                            						   //使能串口1
}

/*-------------------------------------------------*/
/*函数名:串口1 printf函数                         */
/*参  数:char* fmt,...  格式化输出字符串和参数     */
/*返回值:无                                       */
/*-------------------------------------------------*/
__align(8) char Usart1_TxBuff[USART1_TXBUFF_SIZE];  

void u1_printf(char * fmt, ...) 
{  
	unsigned int i, length;
	
	va_list ap;
	va_start(ap, fmt);
	vsprintf(Usart1_TxBuff, fmt, ap);
	va_end(ap);	
	
	length = strlen((const char*)Usart1_TxBuff);		
	while((USART1->SR&0X40) == 0);
	for(i = 0; i < length; i++)
	{			
		USART1->DR = Usart1_TxBuff[i];
		while((USART1->SR&0X40) == 0);	
	}	
}

/*-------------------------------------------------*/
/*函数名:初始化串口2发送功能                        */
/*参  数:bound:波特率                             */
/*返回值:无                                        */
/*-------------------------------------------------*/
void Usart2_Init(unsigned int bound)
{  	 	
    GPIO_InitTypeDef GPIO_InitStructure;     //定义一个设置GPIO功能的变量
	USART_InitTypeDef USART_InitStructure;   //定义一个设置串口功能的变量
	NVIC_InitTypeDef NVIC_InitStructure;     //如果使能接收功能,定义一个设置中断的变量

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3	
      
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
	USART_DeInit(USART2);                                  //串口2寄存器重新设置为默认值
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;              //准备设置PA2
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //IO速率50M
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	       //复用推挽输出,用于串口2的发送
    GPIO_Init(GPIOA, &GPIO_InitStructure);                 //设置PA2
   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;              //准备设置PA3
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入,用于串口2的接收
    GPIO_Init(GPIOA, &GPIO_InitStructure);                 //设置PA3
	
	USART_InitStructure.USART_BaudRate = bound;                                    //波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;                    //8个数据位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;                         //1个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;                            //无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	               //收发模式
      
    USART_Init(USART2, &USART_InitStructure);                                      //设置串口2	

	USART_ClearFlag(USART2, USART_FLAG_RXNE);	              //清除接收标志位
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);            //开启接收中断
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;         //设置串口2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		  //子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			  //中断通道使能
	NVIC_Init(&NVIC_InitStructure);	                          //设置串口2中断 

	USART_Cmd(USART2, ENABLE);                                //使能串口2
}

/*-------------------------------------------------*/
/*函数名:串口2 printf函数                          */
/*参  数:char* fmt,...  格式化输出字符串和参数      */
/*返回值:无                                        */
/*-------------------------------------------------*/

__align(8) char USART2_TxBuff[USART2_TXBUFF_SIZE];  

void u2_printf(char* fmt, ...) 
{  
	unsigned int i, length;
	
	va_list ap;
	va_start(ap, fmt);
	vsprintf(USART2_TxBuff, fmt, ap);
	va_end(ap);	
	
	length=strlen((const char*)USART2_TxBuff);		
	while((USART2->SR&0X40) == 0);
	for(i = 0; i < length; i++)
	{			
		USART2->DR = USART2_TxBuff[i];
		while((USART2->SR&0X40) == 0);	
	}	
}

/*-------------------------------------------------*/
/*函数名:串口2发送缓冲区中的数据                    */
/*参  数:data:数据                                */
/*返回值:无                                        */
/*-------------------------------------------------*/
void u2_TxData(unsigned char *data)
{
	int	i;	
	while((USART2->SR&0X40) == 0);
	for(i = 1; i <= (data[0] * 256 + data[1]); i++)
	{			
		USART2->DR = data[i+1];
		while((USART2->SR&0X40) == 0);	
	}
}

(2)中断

/*-------------------------------------------------*/
/*函数名:串口2接收中断函数(最高优先级,处理接收数据)*/
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void USART2_IRQHandler(void)   
{                      
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)    //如果USART_IT_RXNE标志置位,表示有数据到了,进入if分支
	{  
		if(connectFlag == 0) 							     //如果connectFlag等于0,当前还没有连接服务器,处于指令配置状态
		{                                     
			if(USART2->DR)
			{                                     			 //处于指令配置状态时,非零值才保存到缓冲区	
				Usart2_RxBuff[Usart2_RxCounter] = USART2->DR;//保存到缓冲区	
				Usart2_RxCounter++; 						 //每接收1个字节的数据,Usart2_RxCounter加1,表示接收的数据总量+1 
			}					
		}
		else
		{		                                           	 //反之connectFlag等于1,连接上服务器了	
			Usart2_RxBuff[Usart2_RxCounter] = USART2->DR;    //把接收到的数据保存到Usart2_RxBuff中				
			if(Usart2_RxCounter == 0)
			{    									    											 //如果Usart2_RxCounter等于0,表示是接收的第1个数据,进入if分支				
				TIM_Cmd(TIM4, ENABLE); 
			}
			else																						 //else分支,表示果Usart2_RxCounter不等于0,不是接收的第一个数据
			{                        									    
				TIM_SetCounter(TIM4, 0);  
			}	
			Usart2_RxCounter++;         				     //每接收1个字节的数据,Usart2_RxCounter加1,表示接收的数据总量+1 
		}
	}
} 

5. MQTT

/*----------------------------------------------------------*/
/*函数名:初始化接收,发送,命令数据的 缓冲区 以及各状态参数  */
/*参  数:无                                                */
/*返回值:无                                                */
/*----------------------------------------------------------*/
void MQTT_Buff_Init(void)
{	
	MQTT_RxDataInPtr=MQTT_RxDataBuf[0]; 				 //指向发送缓冲区存放数据的指针归位
	MQTT_RxDataOutPtr=MQTT_RxDataInPtr; 				 //指向发送缓冲区读取数据的指针归位
	MQTT_RxDataEndPtr=MQTT_RxDataBuf[R_NUM-1];		     //指向发送缓冲区结束的指针归位
	
	MQTT_TxDataInPtr=MQTT_TxDataBuf[0];					 //指向发送缓冲区存放数据的指针归位
	MQTT_TxDataOutPtr=MQTT_TxDataInPtr;				     //指向发送缓冲区读取数据的指针归位
	MQTT_TxDataEndPtr=MQTT_TxDataBuf[T_NUM-1];           //指向发送缓冲区结束的指针归位
	
	MQTT_CMDInPtr=MQTT_CMDBuf[0];                        //指向命令缓冲区存放数据的指针归位
	MQTT_CMDOutPtr=MQTT_CMDInPtr;                        //指向命令缓冲区读取数据的指针归位
	MQTT_CMDEndPtr=MQTT_CMDBuf[C_NUM-1];              	 //指向命令缓冲区结束的指针归位

	MQTT_ConectPack();                                	 //发送缓冲区添加连接报文
	MQTT_Subscribe(S_TOPIC_NAME,0);	                 	 //发送缓冲区添加订阅topic,等级0	
	
	pingFlag = connectPackFlag = subcribePackFlag = 0;   //各个参数清零
}

/*----------------------------------------------------------*/
/*函数名:云初始化参数,得到客户端ID,用户名和密码          */
/*参  数:无                                                */
/*返回值:无                                                */
/*----------------------------------------------------------*/
void IoT_Parameter_Init(void)
{	
	memset(ClientID,0,128);                              //客户端ID的缓冲区全部清零
	sprintf(ClientID,"%s",DEVICEID);                     //构建客户端ID,并存入缓冲区
	ClientID_len = strlen(ClientID);                     //计算客户端ID的长度
	
	memset(Username,0,128);                              //用户名的缓冲区全部清零
	sprintf(Username,"%s",PRODUCTID);                    //构建用户名,并存入缓冲区
	Username_len = strlen(Username);                     //计算用户名的长度
	
	memset(Passward,0,128);                              //用户名的缓冲区全部清零
	sprintf(Passward,"%s",AUTHENTICATION);               //构建密码,并存入缓冲区
	Passward_len = strlen(Passward);                     //计算密码的长度
	
	memset(ServerIP,0,128);  
	sprintf(ServerIP,"%s","183.230.40.39");              //构建服务器域名
	ServerPort = 6002;                                   //服务器端口号6002
	
	u1_printf("服 务 器:%s:%d\r\n",ServerIP,ServerPort); //串口输出调试信息
	u1_printf("客户端ID:%s\r\n",ClientID);               //串口输出调试信息
	u1_printf("用 户 名:%s\r\n",Username);               //串口输出调试信息
	u1_printf("密    码:%s\r\n",Passward);               //串口输出调试信息
}

/*----------------------------------------------------------*/
/*函数名:连接服务器报文                                    */
/*参  数:无                                                */
/*返回值:无                                                */
/*----------------------------------------------------------*/
void MQTT_ConectPack(void)
{	
	int temp,Remaining_len;
	
	Fixed_len = 1;                                                        //连接报文中,固定报头长度暂时先=1
	Variable_len = 10;                                                    //连接报文中,可变报头长度=10
	Payload_len = 2 + ClientID_len + 2 + Username_len + 2 + Passward_len; //连接报文中,负载长度      
	Remaining_len = Variable_len + Payload_len;                           //剩余长度=可变报头长度+负载长度
	
	temp_buff[0]=0x10;                         //固定报头第1个字节 :固定0x01		
	do{                                        //循环处理固定报头中的剩余长度字节,字节量根据剩余字节的真实长度变化
		temp = Remaining_len%128;              //剩余长度取余128
		Remaining_len = Remaining_len/128;     //剩余长度取整128
		if(Remaining_len>0)               	
			temp |= 0x80;                      //按协议要求位7置位          
		temp_buff[Fixed_len] = temp;           //剩余长度字节记录一个数据
		Fixed_len++;	                       //固定报头总长度+1    
	}while(Remaining_len > 0);                 //如果Remaining_len>0的话,再次进入循环
	
	temp_buff[Fixed_len + 0] = 0x00;     //可变报头第1个字节 :固定0x00	            
	temp_buff[Fixed_len + 1] = 0x04;     //可变报头第2个字节 :固定0x04
	temp_buff[Fixed_len + 2] = 0x4D;	 //可变报头第3个字节 :固定0x4D
	temp_buff[Fixed_len + 3] = 0x51;	 //可变报头第4个字节 :固定0x51
	temp_buff[Fixed_len + 4] = 0x54;	 //可变报头第5个字节 :固定0x54
	temp_buff[Fixed_len + 5] = 0x54;     //可变报头第6个字节 :固定0x54
	temp_buff[Fixed_len + 6] = 0x04;	 //可变报头第7个字节 :固定0x04
	temp_buff[Fixed_len + 7] = 0xC2;	 //可变报头第8个字节 :使能用户名和密码校验,不使用遗嘱,不保留会话
	temp_buff[Fixed_len + 8] = 0x00; 	 //可变报头第9个字节 :保活时间高字节 0x00
	temp_buff[Fixed_len + 9] = 0x64;	 //可变报头第10个字节:保活时间高字节 0x64   100s
	
	/*     CLIENT_ID      */
	temp_buff[Fixed_len+10] = ClientID_len/256;                			  	//客户端ID长度高字节
	temp_buff[Fixed_len+11] = ClientID_len%256;               			  	//客户端ID长度低字节
	memcpy(&temp_buff[Fixed_len+12],ClientID,ClientID_len);                 //复制过来客户端ID字串	
	/*     用户名        */
	temp_buff[Fixed_len+12+ClientID_len] = Username_len/256; 				//用户名长度高字节
	temp_buff[Fixed_len+13+ClientID_len] = Username_len%256; 				//用户名长度低字节
	memcpy(&temp_buff[Fixed_len+14+ClientID_len],Username,Username_len);    //复制过来用户名字串	
	/*      密码        */
	temp_buff[Fixed_len+14+ClientID_len+Username_len] = Passward_len/256;	//密码长度高字节
	temp_buff[Fixed_len+15+ClientID_len+Username_len] = Passward_len%256;	//密码长度低字节
	memcpy(&temp_buff[Fixed_len+16+ClientID_len+Username_len],Passward,Passward_len); //复制过来密码字串

	TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len);      //加入发送数据缓冲区
}

/*----------------------------------------------------------*/
/*函数名:SUBSCRIBE订阅topic报文                            */
/*参  数:QoS:订阅等级                                     */
/*参  数:topic_name:订阅topic报文名称                     */
/*返回值:无                                                */
/*----------------------------------------------------------*/
void MQTT_Subscribe(char *topic_name, int QoS)
{	
	Fixed_len = 2;                              		   //SUBSCRIBE报文中,固定报头长度=2
	Variable_len = 2;                          			   //SUBSCRIBE报文中,可变报头长度=2	
	Payload_len = 2 + strlen(topic_name) + 1;   		   //计算有效负荷长度 = 2字节(topic_name长度)+ topic_name字符串的长度 + 1字节服务等级
	
	temp_buff[0] = 0x82;                                   //第1个字节 :固定0x82                      
	temp_buff[1] = Variable_len + Payload_len;             //第2个字节 :可变报头+有效负荷的长度	
	temp_buff[2] = 0x00;                                   //第3个字节 :报文标识符高字节,固定使用0x00
	temp_buff[3] = 0x01;		                           //第4个字节 :报文标识符低字节,固定使用0x01
	temp_buff[4] = strlen(topic_name)/256;                 //第5个字节 :topic_name长度高字节
	temp_buff[5] = strlen(topic_name)%256;		           //第6个字节 :topic_name长度低字节
	memcpy(&temp_buff[6], topic_name, strlen(topic_name)); //第7个字节开始 :复制过来topic_name字串		
	temp_buff[6 + strlen(topic_name)] = QoS;               //最后1个字节:订阅等级
	
	TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len);  //加入发送数据缓冲区
}

/*----------------------------------------------------------*/
/*函数名:PING报文,心跳包                                   */
/*参  数:无                                                */
/*返回值:无                                                */
/*----------------------------------------------------------*/
void MQTT_PingREQ(void)
{
	temp_buff[0] = 0xC0;              //第1个字节 :固定0xC0                      
	temp_buff[1] = 0x00;              //第2个字节 :固定0x00 

	TxDataBuf_Deal(temp_buff, 2);     //加入数据到缓冲区
}

/*----------------------------------------------------------*/
/*函数名:等级0 发布消息报文                                  */
/*参  数:topic_name:topic名称                              */
/*参  数:data:数据                                         */ 
/*参  数:data_len:数据长度                                 */
/*返回值:无                                                 */
/*----------------------------------------------------------*/
void MQTT_PublishQs0(char *topic, char *data, int data_len)
{	
	int temp,Remaining_len;
	
	Fixed_len = 1;                              //固定报头长度暂时先等于:1字节
	Variable_len = 2 + strlen(topic);           //可变报头长度:2字节(topic长度)+ topic字符串的长度
	Payload_len = data_len;                     //有效负荷长度:就是data_len
	Remaining_len = Variable_len + Payload_len; //剩余长度=可变报头长度+负载长度
	
	temp_buff[0] = 0x30;                      	//固定报头第1个字节 :固定0x30   	
	do{                                         //循环处理固定报头中的剩余长度字节,字节量根据剩余字节的真实长度变化
		temp = Remaining_len%128;           	//剩余长度取余128
		Remaining_len = Remaining_len/128;      //剩余长度取整128
		if(Remaining_len>0)               	
			temp |= 0x80;                    	//按协议要求位7置位          
		temp_buff[Fixed_len] = temp;            //剩余长度字节记录一个数据
		Fixed_len++;	                     	//固定报头总长度+1    
	}while(Remaining_len>0);                    //如果Remaining_len>0的话,再次进入循环
		             
	temp_buff[Fixed_len+0] = strlen(topic)/256;                       //可变报头第1个字节     :topic长度高字节
	temp_buff[Fixed_len+1] = strlen(topic)%256;		                  //可变报头第2个字节     :topic长度低字节
	memcpy(&temp_buff[Fixed_len+2], topic,strlen(topic));             //可变报头第3个字节开始 :拷贝topic字符串	
	memcpy(&temp_buff[Fixed_len + 2 + strlen(topic)], data, data_len);//有效负荷:拷贝data数据
	
	TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len);//加入发送数据缓冲区	
}

/*----------------------------------------------------------*/
/*函数名:处理服务器发来的等级0的推送                          */
/*参  数:redata:接收的数据                                 */
/*返回值:无                                                 */
/*----------------------------------------------------------*/
void MQTT_DealPushdata_Qs0(unsigned char *redata)
{
	int  re_len;               	           //定义一个变量,存放接收的数据总长度
	int  pack_num;                         //定义一个变量,当多个推送一起过来时,保存推送的个数
    int  temp,temp_len;                    //定义一个变量,暂存数据
    int  totle_len;                        //定义一个变量,存放已经统计的推送的总数据量
	int  topic_len;              	       //定义一个变量,存放推送中主题的长度
	int  cmd_len;                          //定义一个变量,存放推送中包含的命令数据的长度
	int  cmd_loca;                         //定义一个变量,存放推送中包含的命令的起始位置
	int  i;                                //定义一个变量,用于for循环
	int  local,multiplier;
	unsigned char tempbuff[RBUFF_UNIT];	   //临时缓冲区
	unsigned char *data;                   //redata过来的时候,第一个字节是数据总量,data用于指向redata的第2个字节,真正的数据开始的地方
		
	re_len = redata[0]*256+redata[1];                     //获取接收的数据总长度		
	data = &redata[2];                                    //data指向redata的第2个字节,真正的数据开始的 
	pack_num = temp_len = totle_len = temp = 0;           //各个变量清零
	local = 1;
	multiplier = 1;
	do{
		pack_num++;                                       //开始循环统计推送的个数,每次循环推送的个数+1	
		do{
			temp = data[totle_len + local];   
			temp_len += (temp & 127) * multiplier;
			multiplier *= 128;
			local++;
		}while ((temp & 128) != 0);
		totle_len += (temp_len + local);                  //累计统计的总的推送的数据长度
		re_len -= (temp_len + local) ;                    //接收的数据总长度 减去 本次统计的推送的总长度      
		local = 1;
		multiplier = 1;
		temp_len = 0;
	}while(re_len!=0);                                    //如果接收的数据总长度等于0了,说明统计完毕了
	u1_printf("本次接收了%d个推送数据\r\n",pack_num);		//串口输出信息
	temp_len = totle_len = 0;                		      //各个变量清零
	local = 1;
	multiplier = 1;
	for(i = 0; i < pack_num; i++)			//已经统计到了接收的推送个数,开始for循环,取出每个推送的数据 
	{                                		
		do{
			temp = data[totle_len + local];   
			temp_len += (temp & 127) * multiplier;
			multiplier *= 128;
			local++;
		}while ((temp & 128) != 0);				
		topic_len = data[local + totle_len]*256 + data[local + 1 + totle_len] + 2; //计算本次推送数据中主题占用的数据量
		cmd_len = temp_len - topic_len;                              			   //计算本次推送数据中命令数据占用的数据量
		cmd_loca = totle_len + local +  topic_len;                  			   //计算本次推送数据中命令数据开始的位置
		memcpy(tempbuff, &data[cmd_loca], cmd_len);                   			   //命令数据拷贝出来		                 
		CMDBuf_Deal(tempbuff, cmd_len);                             			   //加入命令到缓冲区
		totle_len += (temp_len + local);                              			   //累计已经统计的推送的数据长度
		local = 1;
		multiplier = 1;
		temp_len = 0;
	}	
}

/*----------------------------------------------------------*/
/*函数名:处理发送缓冲区                                      */
/*参  数:data:数据                                         */
/*参  数:size:数据长度								                      */
/*返回值:无                                                 */
/*----------------------------------------------------------*/
void TxDataBuf_Deal(unsigned char *data, int size)
{
	memcpy(&MQTT_TxDataInPtr[2], data, size);     //拷贝数据到发送缓冲区	
	MQTT_TxDataInPtr[0] = size/256;               //记录数据长度
	MQTT_TxDataInPtr[1] = size%256;               //记录数据长度
	MQTT_TxDataInPtr += TBUFF_UNIT;               //指针下移
	if(MQTT_TxDataInPtr == MQTT_TxDataEndPtr)     //如果指针到缓冲区尾部了
		MQTT_TxDataInPtr = MQTT_TxDataBuf[0];     //指针归位到缓冲区开头
}

/*----------------------------------------------------------*/
/*函数名:处理命令缓冲区									 */
/*参  数:data:数据                                        */
/*参  数:size:数据长度                                    */
/*返回值:无                                                */
/*----------------------------------------------------------*/
void CMDBuf_Deal(unsigned char *data, int size)
{
	memcpy(&MQTT_CMDInPtr[2], data,size);         //拷贝数据到命令缓冲区
	MQTT_CMDInPtr[0] = size/256;              	  //记录数据长度
	MQTT_CMDInPtr[1] = size%256;                  //记录数据长度
	MQTT_CMDInPtr[size+2] = '\0';                 //加入字符串结束符
	MQTT_CMDInPtr += CBUFF_UNIT;               	  //指针下移
	if(MQTT_CMDInPtr == MQTT_CMDEndPtr)           //如果指针到缓冲区尾部了
		MQTT_CMDInPtr = MQTT_CMDBuf[0];        	  //指针归位到缓冲区开头
}

五、进阶练习

在以上基础上可尝试以下功能:

    1. 在裸机的环境上加入实时操作系统,如FreeRTOS
    1. 将上传至云平台的数据存入本地数据库
    1. 多台客户端同时连接云服务器
    1. 开发移动APP,通过连接数据库查看实时数据并可操控stm32端,例:云服务器并通过更新数据库状态改变向客户端发送相关指令实现远端操控。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/458610.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DX云音乐(安卓)

首先&#xff0c;软件安装好不用注册登录就可以直接使用&#xff0c;在首页这里有很多推荐的热门歌单&#xff0c;比如&#xff0c;有年度热门的DJ歌曲&#xff0c;有抖音热门DJ&#xff0c;有各种跨年晚会&#xff0c;有运动必备的DGM&#xff0c;有90后的经典旋律等等。 还有…

php+vue 校友交流平台

1.普通用户功能分析 &#xff08;1&#xff09;用户注册&#xff1a;用于注册校友录用户。 &#xff08;2&#xff09;用户登录&#xff1a;供校友录用户登录。 &#xff08;3&#xff09;资料修改&#xff1a;修改当前登录使用者信息。 &#xff08;4&#xff09;…

MQTT 5协议中的基础更改(一)

01 协议的基础性变化 MQTT 5是对现有协议规范的重大更新&#xff0c;新版本协议具有以下特征&#xff1a;轻量级、易用性、极强的可扩展性、对移动网络的适用性以及通信参与者的解耦。 02 MQTT仍然是MQTT 如果您熟悉MQTT3.1.1&#xff0c;那么您之前知道的关于MQTT的所有原则…

通用策略03丨RUMI魔改+krange自适应第3版

量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 正 文 大家好&#xff0c;今天我们分享2023年度第3期策略——RUMI魔改krange自适应离场第3版迭代。 本期策略是2023年通用系列第3篇。本期主要内容有对RUMI原版的逻辑魔改&#xff0c;其次重点针对kra…

关于Open Shift(OKD) 中 用户认证、权限管理、SCC 管理的一些笔记

写在前面 因为参加考试&#xff0c;会陆续分享一些 OpenShift 的笔记博文内容为 openshift 用户认证和权限管理以及 scc 管理相关笔记学习环境为 openshift v3 的版本&#xff0c;有些旧这里如果专门学习 openshift &#xff0c;建议学习 v4 版本理解不足小伙伴帮忙指正 对每个…

数据库管理新定义:一款纯Web化免费SQL开发工具,免安装

SQL Studio是一款由麦聪软件研发的多数据库管理工具&#xff0c;提供Windows、Linux 和 MacOS三种版本的软件包&#xff0c;支持中英文两种语言。SQL Studio是用Java编写的&#xff0c;默认使用 JDK 8进行编译。 下载看这里: [SQLStudio] (http://www.maicongs.com/#/home/web)…

云HIS(二级医院,乡镇医院,民营医院,标准化HIS医院信息管理系统源码)

传统 HIS&#xff08;基于医院信息系统&#xff09; 和云 HIS&#xff08;基于云计算的医院信息系统&#xff09;各有优缺点&#xff0c;选择哪种系统需要根据具体情况进行权衡。 传统 HIS 系统通常由医院自行开发和维护&#xff0c;适用于医院内部信息化程度较高、数据安全性…

Sql 优化工具

1、背景 对于正在运行的mysql 性能如何&#xff1f;参数设置的是否合理&#xff1f;账号设置的是否存在安全隐患&#xff1f; 对于Sql执行效率又如何&#xff1f;如何了然于胸&#xff1f; show profile, explain&#xff0c;slow query log &#xff0c;索引优化&#xff0…

【切负荷】计及切负荷和直流潮流(DC-OPF)风-火-储经济调度模型研究【IEEE24节点】(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Qt实现滑动条平滑效果

效果图 基于QPropertyAnimation实现 代码部分 //设置滑动条动画 m_scrollAnimationnew QPropertyAnimation; m_scrollAnimation->setTargetObject(this); m_scrollAnimation->setPropertyName("value"); m_scrollAnimation->setEasingCurve(QEasingCurve:…

如何在已创建的SpringBoot项目基础上添加依赖

假设此时已经创建了一个SpringBoot项目&#xff0c;在使用的时候发现缺少 lombok 依赖&#xff0c;此时如何在不创建新项目的基础上添加该依赖&#xff1f;有两种方式&#xff1a; 通过Maven中央仓库导入依赖使用插件EditStarters导入依赖 1&#xff0c;通过Maven中央仓库导入依…

CVE-2023-21839 Weblogic远程代码执行漏洞复现

目录 0x01 声明&#xff1a; 0x02 简介&#xff1a; 0x03 漏洞概述&#xff1a; 0x04 影响版本&#xff1a; 0x05 环境搭建&#xff1a; Docker环境搭建&#xff1a; 漏洞环境搭建&#xff1a; 1、编辑docker-compose.yml 2、启动docker 3、访问页面&#xff1a; 0x…

PM不想做项目管理了,还能干点啥?

做项目经理太累了&#xff01; 那么 不做项目经理还能做什么呢&#xff1f; 01 铁锅批发商 毕竟 当项目经理的时候 已经囤积了成百上千口锅 十年背锅经验不是瞎吹 并且可现场演示铁锅烙饼 老板亲授&#xff0c;真实还原&#xff0c;充饥必备 02 Office优化师 当项目…

Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建和案例分享)

常用的两款AI可视化交互应用比较&#xff1a; Gradio Gradio的优势在于易用性&#xff0c;代码结构相比Streamlit简单&#xff0c;只需简单定义输入和输出接口即可快速构建简单的交互页面&#xff0c;更轻松部署模型。适合场景相对简单&#xff0c;想要快速部署应用的开发者。 …

43岁程序员,投了上万份简历都已读不回,只好把年龄改成40岁,这才有了面试机会,拿到了offer!...

40多岁找工作有多难&#xff1f; 一位43岁的程序员讲述了自己找工作的经历&#xff1a; 80年&#xff0c;大专&#xff0c;目前没到43周岁&#xff0c;年前被裁&#xff0c;简历上的年龄是42岁&#xff0c;两个多月投了上万份简历&#xff0c;99.5%是已读未回。后来改变策略把简…

阿里最新研究:当ChatGPT 遇上推荐系统,遭遇滑铁卢

文 |小戏 在 GPT-4 屠榜了 NLP&#xff0c;SAM 零样本分割一切让 CV 消失后&#xff0c;不知道大家会不会有好奇 AI 三大现实应用的另一边岁月静好的推荐系统有没有感受到来自遥远大模型带来的巨大压力。 尽管 ChatGPT 的训练过程似乎没有对推荐系统太多的兼容&#xff0c;但是…

[2020.3.13]通过Android反编译找出问题根因

反编译软件推荐如下: (1)JEB.android.decompiler (2)jadx-0.7.1 问题&#xff1a;喜马拉雅初次进入直播功能时显示无网络连接&#xff1b;播放音频时&#xff0c;播放界面下方内容无法加载&#xff0c;显示网络未连接 1 初次进入直播功能时显示无网络连接 2 左滑切换直播间…

Stability AI,新人工智能背景下的开源先锋

今天跟大家聊聊一家总部设在伦敦的AI公司&#xff0c;虽然小&#xff0c;但对这一波AI浪潮的贡献不可谓不小&#xff0c;好产品好公司都值得推荐给大家。 开源生成式绘画 Stable Diffusion 提起Stability AI&#xff0c;多少有些陌生&#xff0c;但提起Stable Diffusion&#x…

程序员,停止焦虑吧!|原创

hi&#xff0c;我是阿笠。今天聊聊程序员的焦虑问题。 不知道是我看到的消息被定向推送&#xff0c;还是因为现实环境就这样&#xff0c;似乎越来越多的人都被焦虑所困扰。 我也是其中一员。 失眠、焦虑 前段时间因为睡眠困扰&#xff0c;我去看了医生&#xff0c;医生给出的诊…