STM32(hal库)通过MQTT协议连接onenet

在中国移动物联网平台onenet,选择用多协议的mqtt旧版协议新建产品和设备。然后根据自己的情况更改程序中的参数。数据流上传时要根据自己的数据将程序中固定的更改为动态赋值就好了。本程序参考超级大洋葱806的STM32CubeMX ESP8266 AT指令实现MQTT协议并接入阿里云IOT平台源码解析【附Github源码地址】
一、esp8266驱动
我将esp8266设置为station透传模式

#include "esp8266_at.h"

//usart1发送和接收数组
uint8_t usart1_txbuf[256];
uint8_t usart1_rxbuf[512];
uint8_t usart1_rxone[1];
uint8_t usart1_rxcounter;


//串口1发送一个字节
static void USART1_SendOneByte(uint8_t val)
{
	((UART_HandleTypeDef *)&huart1)->Instance->DR = ((uint16_t)val & (uint16_t)0x01FF);
	while((((UART_HandleTypeDef *)&huart1)->Instance->SR&0X40)==0);//等待发送完成
}


//向ESP8266发送定长数据
void ESP8266_ATSendBuf(uint8_t* buf,uint16_t len)
{
	memset(usart1_rxbuf,0, 256);
	
	//每次发送前将接收串口接收总数置0,为了接收
	usart1_rxcounter = 0;	
	
	//定长发送
	HAL_UART_Transmit(&huart1,(uint8_t *)buf,len,0xFFFF);
}

//向ESP8266发送字符串
void ESP8266_ATSendString(char* str)
{
  memset(usart1_rxbuf,0, 256);
	
	//每次发送前将接收串口接收总数置0,为了接收
	usart1_rxcounter = 0;	
	
	//发送方法1
	while(*str)		USART1_SendOneByte(*str++);
	
	//发送法法2
//	HAL_UART_Transmit(&huart1,(uint8_t *)str,strlen(str),0xFFFF);
}

//退出透传
void ESP8266_ExitUnvarnishedTrans(void)
{
	ESP8266_ATSendString("+++");HAL_Delay(50);
	ESP8266_ATSendString("+++");HAL_Delay(50);	
}

//查找字符串中是否包含另一个字符串
uint8_t FindStr(char* dest,char* src,uint16_t retry_nms)
{
	retry_nms/=10;                   //超时时间

	while(strstr(dest,src)==0 && retry_nms--)//等待串口接收完毕或超时退出
	{		
		HAL_Delay(10);
	}

	if(retry_nms) return 1;                       

	return 0; 
}

/**
 * 功能:检查ESP8266是否正常
 * 参数:None
 * 返回值:ESP8266返回状态
 *        非0 ESP8266正常
 *        0 ESP8266有问题  
 */
uint8_t ESP8266_Check(void)
{
	uint8_t check_cnt=5;
	while(check_cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf)); 	 //清空接收缓冲
		ESP8266_ATSendString("AT\r\n");     		 			//发送AT握手指令	
		if(FindStr((char*)usart1_rxbuf,"OK",200) != 0)
		{
			return 1;
		}
	}
	return 0;
}

/**
 * 功能:初始化ESP8266
 * 参数:None
 * 返回值:初始化结果,非0为初始化成功,0为失败
 */
uint8_t ESP8266_Init(void)
{
	//清空发送和接收数组
	memset(usart1_txbuf,0,sizeof(usart1_txbuf));
	memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));
	
	ESP8266_ExitUnvarnishedTrans();		//退出透传
	HAL_Delay(500);
	ESP8266_ATSendString("AT+RST\r\n");
	HAL_Delay(800);
	if(ESP8266_Check()==0)              //使用AT指令检查ESP8266是否存在
	{
		return 0;
	}
	
	memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));    //清空接收缓冲
//	ESP8266_ATSendString("ATE0\r\n");     	//关闭回显 
	if(FindStr((char*)usart1_rxbuf,"OK",500)==0)  //设置不成功
	{
			return 0;      
	}
	return 1;                         //设置成功
}

/**
 * 功能:恢复出厂设置
 * 参数:None
 * 返回值:None
 * 说明:此时ESP8266中的用户设置将全部丢失回复成出厂状态
 */
void ESP8266_Restore(void)
{
	ESP8266_ExitUnvarnishedTrans();          	//退出透传
  HAL_Delay(500);
	ESP8266_ATSendString("AT+RESTORE\r\n");		//恢复出厂 	
}

/**
 * 功能:连接热点
 * 参数:
 *         ssid:热点名
 *         pwd:热点密码
 * 返回值:
 *         连接结果,非0连接成功,0连接失败
 * 说明: 
 *         失败的原因有以下几种(UART通信和ESP8266正常情况下)
 *         1. WIFI名和密码不正确
 *         2. 路由器连接设备太多,未能给ESP8266分配IP
 */
uint8_t ESP8266_ConnectAP(char* ssid,char* pswd)
{
	uint8_t cnt=5;
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));     
		ESP8266_ATSendString("AT+CWMODE=1\r\n");              //设置为STATION模式	
		if(FindStr((char*)usart1_rxbuf,"OK",200) != 0)
		{
			break;
		}             		
	}
	if(cnt == 0)
		return 0;

	cnt=2;
	while(cnt--)
	{                    
		memset(usart1_txbuf,0,sizeof(usart1_txbuf));//清空发送缓冲
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));//清空接收缓冲
		sprintf((char*)usart1_txbuf,"AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,pswd);//连接目标AP
		ESP8266_ATSendString((char*)usart1_txbuf);	
		if(FindStr((char*)usart1_rxbuf,"OK",8000)!=0)                      //连接成功且分配到IP
		{
			return 1;
		}
	}
	return 0;
}

//开启透传模式
static uint8_t ESP8266_OpenTransmission(void)
{
	//设置透传模式
	uint8_t cnt=2;
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));    
		ESP8266_ATSendString("AT+CIPMODE=1\r\n");  
		if(FindStr((char*)usart1_rxbuf,"OK",200)!=0)
		{	
			return 1;
		}
	}
	return 0;
}

/**
 * 功能:使用指定协议(TCP/UDP)连接到服务器
 * 参数:
 *         mode:协议类型 "TCP","UDP"
 *         ip:目标服务器IP
 *         port:目标是服务器端口号
 * 返回值:
 *         连接结果,非0连接成功,0连接失败
 * 说明: 
 *         失败的原因有以下几种(UART通信和ESP8266正常情况下)
 *         1. 远程服务器IP和端口号有误
 *         2. 未连接AP
 *         3. 服务器端禁止添加(一般不会发生)
 */
uint8_t ESP8266_ConnectServer(char* mode,char* ip,uint16_t port)
{
	uint8_t cnt;
   
	ESP8266_ExitUnvarnishedTrans();                   //多次连接需退出透传
	HAL_Delay(500);

	//连接服务器
	cnt=2;
	while(cnt--)
	{
		memset(usart1_txbuf,0,sizeof(usart1_txbuf));//清空发送缓冲
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));//清空接收缓冲   
		sprintf((char*)usart1_txbuf,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);
		ESP8266_ATSendString((char*)usart1_txbuf);
		if(FindStr((char*)usart1_rxbuf,"CONNECT",8000) !=0 )
		{
			break;
		}
	}
	if(cnt == 0) 
		return 0;
	
	//设置透传模式
	if(ESP8266_OpenTransmission()==0) return 0;
	
	//开启发送状态
	cnt=2;
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf)); //清空接收缓冲   
		ESP8266_ATSendString("AT+CIPSEND\r\n");//开始处于透传发送状态
		if(FindStr((char*)usart1_rxbuf,">",200)!=0)
		{
			return 1;
		}
	}
	return 0;
}

/**
 * 功能:主动和服务器断开连接
 * 参数:None
 * 返回值:
 *         连接结果,非0断开成功,0断开失败
 */
uint8_t DisconnectServer(void)
{
	uint8_t cnt;
	
	ESP8266_ExitUnvarnishedTrans();	//退出透传
	HAL_Delay(500);
	
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf)); //清空接收缓冲   
		ESP8266_ATSendString("AT+CIPCLOSE\r\n");//关闭链接

		if(FindStr((char*)usart1_rxbuf,"CLOSED",200)!=0)//操作成功,和服务器成功断开
		{
			break;
		}
	}
	if(cnt) return 1;
	return 0;
}

二、mqtt连接onenet服务器及设备驱动

#include "esp8266_mqtt.h"
#include "esp8266_at.h"

#define BYTE0(dwTemp)       (*( char *)(&dwTemp))
#define BYTE1(dwTemp)       (*((char *)(&dwTemp) + 1))
#define BYTE2(dwTemp)       (*((char *)(&dwTemp) + 2))
#define BYTE3(dwTemp)       (*((char *)(&dwTemp) + 3))
//连接成功服务器回应 20 02 00 00
//客户端主动断开连接 e0 00
const uint8_t parket_connetAck[] = {0x20,0x02,0x00,0x00};
const uint8_t parket_disconnet[] = {0xe0,0x00};
const uint8_t parket_heart[] = {0xc0,0x00};
const uint8_t parket_heart_reply[] = {0xc0,0x00};
const uint8_t parket_subAck[] = {0x90,0x03};

volatile uint16_t MQTT_TxLen;

//MQTT发送数据
void MQTT_SendBuf(uint8_t *buf,uint16_t len)
{
	ESP8266_ATSendBuf(buf,len);
}	

//发送心跳包
void MQTT_SentHeart(void)
{
	MQTT_SendBuf((uint8_t *)parket_heart,sizeof(parket_heart));
}

//MQTT无条件断开
void MQTT_Disconnect()
{
	MQTT_SendBuf((uint8_t *)parket_disconnet,sizeof(parket_disconnet));
}

//MQTT初始化
void MQTT_Init(uint8_t *prx,uint16_t rxlen,uint8_t *ptx,uint16_t txlen)
{
	memset(usart1_txbuf,0,sizeof(usart1_txbuf)); //清空发送缓冲
	memset(usart1_rxbuf,0,sizeof(usart1_rxbuf)); //清空接收缓冲
	
	//无条件先主动断开
	MQTT_Disconnect();HAL_Delay(100);
	MQTT_Disconnect();HAL_Delay(100);
}

//==========================================================
//	函数名称:	MQTT_Connect   MQTT连接服务器的打包函数
//
//	函数功能:	Connect连接消息组包
//
//	入口参数:	ClientID:设备ID
//              Username:产品ID(用户名)
//				Password:密码:鉴权信息或apikey
//
//	返回参数:	1-成功		0-失败
//
//	说明:		
//==========================================================
uint8_t MQTT_Connect(char *ClientID,char *Username,char *Password)
{
	int ClientIDLen = strlen(ClientID);
	int UsernameLen = strlen(Username);
	int PasswordLen = strlen(Password);
	int DataLen;
	MQTT_TxLen=0;
	//可变报头+Payload  每个字段包含两个字节的长度标识
    DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
	
	//固定报头
	//控制报文类型
    usart1_txbuf[MQTT_TxLen++] = 0x10;		//MQTT Message Type CONNECT
	//剩余长度(不包括固定头部)
	do
	{
		uint8_t encodedByte = DataLen % 128;
		DataLen = DataLen / 128;
		// if there are more data to encode, set the top bit of this byte
		if ( DataLen > 0 )
			encodedByte = encodedByte | 128;
		usart1_txbuf[MQTT_TxLen++] = encodedByte;
	}while ( DataLen > 0 );
    	
	//可变报头
	//协议名
	usart1_txbuf[MQTT_TxLen++] = 0;        		// Protocol Name Length MSB    
	usart1_txbuf[MQTT_TxLen++] = 4;        		// Protocol Name Length LSB    
	usart1_txbuf[MQTT_TxLen++] = 'M';        	// ASCII Code for M    
	usart1_txbuf[MQTT_TxLen++] = 'Q';        	// ASCII Code for Q    
	usart1_txbuf[MQTT_TxLen++] = 'T';        	// ASCII Code for T    
	usart1_txbuf[MQTT_TxLen++] = 'T';        	// ASCII Code for T    
	//协议级别
	usart1_txbuf[MQTT_TxLen++] = 4;        		// MQTT Protocol version = 4    
	//连接标志
	usart1_txbuf[MQTT_TxLen++] = 0xc2;        	// conn flags 
	usart1_txbuf[MQTT_TxLen++] = 0;        		// Keep-alive Time Length MSB    
	usart1_txbuf[MQTT_TxLen++] = 60;        	// Keep-alive Time Length LSB  60S心跳包  

	usart1_txbuf[MQTT_TxLen++] = BYTE1(ClientIDLen);// Client ID length MSB    
	usart1_txbuf[MQTT_TxLen++] = BYTE0(ClientIDLen);// Client ID length LSB  	
	memcpy(&usart1_txbuf[MQTT_TxLen],ClientID,ClientIDLen);
	MQTT_TxLen += ClientIDLen;
	
	if(UsernameLen > 0)
	{   
		usart1_txbuf[MQTT_TxLen++] = BYTE1(UsernameLen);		//username length MSB    
		usart1_txbuf[MQTT_TxLen++] = BYTE0(UsernameLen);    	//username length LSB    
		memcpy(&usart1_txbuf[MQTT_TxLen],Username,UsernameLen);
		MQTT_TxLen += UsernameLen;
	}
	
	if(PasswordLen > 0)
	{    
		usart1_txbuf[MQTT_TxLen++] = BYTE1(PasswordLen);		//password length MSB    
		usart1_txbuf[MQTT_TxLen++] = BYTE0(PasswordLen);    	//password length LSB  
		memcpy(&usart1_txbuf[MQTT_TxLen],Password,PasswordLen);
		MQTT_TxLen += PasswordLen; 
	}    
	
	uint8_t cnt=2;
	uint8_t wait;
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));
		MQTT_SendBuf(usart1_txbuf,MQTT_TxLen);
		wait=30;//等待3s时间
		while(wait--)
		{
			//CONNECT
			if(usart1_rxbuf[0]==parket_connetAck[0] && usart1_rxbuf[1]==parket_connetAck[1]) //连接成功			   
			{
				return 1;//连接成功
			}
			HAL_Delay(100);			
		}
	}
	return 0;
}

//MQTT订阅/取消订阅数据打包函数
//topic       主题 
//qos         消息等级 
//whether     订阅/取消订阅请求包
uint8_t MQTT_SubscribeTopic(char *topic,uint8_t qos,uint8_t whether)
{    
	MQTT_TxLen=0;
	int topiclen = strlen(topic);
	
	int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
	//固定报头
	//控制报文类型
	if(whether) usart1_txbuf[MQTT_TxLen++] = 0x82; //消息类型和标志订阅
	else	usart1_txbuf[MQTT_TxLen++] = 0xA2;    //取消订阅

	//剩余长度
	do
	{
		uint8_t encodedByte = DataLen % 128;
		DataLen = DataLen / 128;
		// if there are more data to encode, set the top bit of this byte
		if ( DataLen > 0 )
			encodedByte = encodedByte | 128;
		usart1_txbuf[MQTT_TxLen++] = encodedByte;
	}while ( DataLen > 0 );	
	
	//可变报头
	usart1_txbuf[MQTT_TxLen++] = 0;				//消息标识符 MSB
	usart1_txbuf[MQTT_TxLen++] = 0x01;           //消息标识符 LSB
	//有效载荷
	usart1_txbuf[MQTT_TxLen++] = BYTE1(topiclen);//主题长度 MSB
	usart1_txbuf[MQTT_TxLen++] = BYTE0(topiclen);//主题长度 LSB   
	memcpy(&usart1_txbuf[MQTT_TxLen],topic,topiclen);
	MQTT_TxLen += topiclen;

	if(whether)
	{
		usart1_txbuf[MQTT_TxLen++] = qos;//QoS级别
	}
	
	uint8_t cnt=2;
	uint8_t wait;
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));
		MQTT_SendBuf(usart1_txbuf,MQTT_TxLen);
		wait=30;//等待3s时间
		while(wait--)
		{
			if(usart1_rxbuf[0]==parket_subAck[0] && usart1_rxbuf[1]==parket_subAck[1]) //订阅成功			   
			{
				return 1;//订阅成功
			}
			HAL_Delay(100);			
		}
	}
	if(cnt) return 1;	//订阅成功
	return 0;
}

//MQTT发布数据打包函数
//topic   主题 
//message 消息
//qos     消息等级 
uint8_t MQTT_PublishData(char *topic, char *message, uint8_t qos)
{  
	int topicLength = strlen(topic);    
	int messageLength = strlen(message);     
	static uint16_t id=0;
	int DataLen;
	MQTT_TxLen=0;
	//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
	//QOS为0时没有标识符
	//数据长度             主题名   报文标识符   有效载荷
	if(qos)	DataLen = (2+topicLength) + 2 + messageLength;       
	else	DataLen = (2+topicLength) + messageLength;   

    //固定报头
	//控制报文类型
	usart1_txbuf[MQTT_TxLen++] = 0x30;    // MQTT Message Type PUBLISH  

	//剩余长度
	do
	{
		uint8_t encodedByte = DataLen % 128;
		DataLen = DataLen / 128;
		// if there are more data to encode, set the top bit of this byte
		if ( DataLen > 0 )
			encodedByte = encodedByte | 128;
		usart1_txbuf[MQTT_TxLen++] = encodedByte;
	}while ( DataLen > 0 );	
	
	usart1_txbuf[MQTT_TxLen++] = BYTE1(topicLength);//主题长度MSB
	usart1_txbuf[MQTT_TxLen++] = BYTE0(topicLength);//主题长度LSB 
	memcpy(&usart1_txbuf[MQTT_TxLen],topic,topicLength);//拷贝主题
	MQTT_TxLen += topicLength;
        
	//报文标识符
	if(qos)
	{
			usart1_txbuf[MQTT_TxLen++] = BYTE1(id);
			usart1_txbuf[MQTT_TxLen++] = BYTE0(id);
			id++;
	}
	memcpy(&usart1_txbuf[MQTT_TxLen],message,messageLength);
    MQTT_TxLen += messageLength;
        
	MQTT_SendBuf(usart1_txbuf,MQTT_TxLen);
  return MQTT_TxLen;
}

/**
//数据流上传格式为:固定报文头0x30(0x31,0x32),剩余数据长度,可变报文头(topic:$dp。json格式采用json格式3:0x03。playload:{"tem":34.3})
//0x30+剩余长度+topic长度+topic(即$dp)+json格式+数据消息长度+数据消息内容
//MQTT:0x30 0x1420 0x00 0x03 0x24$ 0x64d 0x70p 0x03 0x00 0x0C12 0x7B{ 0x22" 0x74t 0x65e 0x6Dm 0x22" 0x3A: 0x333 0x344 0x2E. 0x333 0x7D} 
//发送数据为:30 14 00 03 24 64 70 03 00 0C 7B 22 74 65 6D 22 3A 33 33 2E 34 7D
*/
//MQTT上传数据流打包函数
//topic   主题$dp 
//message 消息
//qos     消息等级 此程序对于qos还有待完善
uint8_t MQTT_PublishJsonData(char *topic, char *message, uint8_t qos,uint8_t jsonnum)
{  
	int topicLength = strlen(topic);    
	int messageLength = strlen(message);     
	static uint16_t id=0;
	int DataLen;
    int i=0;
	MQTT_TxLen=0;
    
	//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
	//QOS为0时没有标识符
	//数据长度   主题名   报文标识符(qos为0没有报文标识符)   有效载荷(json格式代码+消息长度)
	if(qos)	DataLen = (2+topicLength) + 1 + 2+(2 + messageLength);       
	else	DataLen = (2+topicLength) + 1 +(2 + messageLength);   

    //固定报头
	//控制报文类型
    if(qos==1){
        usart1_txbuf[MQTT_TxLen++] = 0x31;
    }else if(qos==2)
    {
        usart1_txbuf[MQTT_TxLen++] = 0x32;
    }else	usart1_txbuf[MQTT_TxLen++] = 0x30;    // MQTT Message Type PUBLISH  

	//剩余长度
	do
	{
		uint8_t encodedByte = DataLen % 128;
		DataLen = DataLen / 128;
		// if there are more data to encode, set the top bit of this byte
		if ( DataLen > 0 )
			encodedByte = encodedByte | 128;
		usart1_txbuf[MQTT_TxLen++] = encodedByte;
	}while ( DataLen > 0 );	
	
	usart1_txbuf[MQTT_TxLen++] = BYTE1(topicLength);//主题长度MSB
	usart1_txbuf[MQTT_TxLen++] = BYTE0(topicLength);//主题长度LSB 
	memcpy(&usart1_txbuf[MQTT_TxLen],topic,topicLength);//拷贝主题
	MQTT_TxLen += topicLength;
    
    usart1_txbuf[MQTT_TxLen++] = jsonnum;//json格式1-7
        
	//报文标识符
	if(qos)
	{
			usart1_txbuf[MQTT_TxLen++] = BYTE1(id);
			usart1_txbuf[MQTT_TxLen++] = BYTE0(id);
			id++;
	}
    usart1_txbuf[MQTT_TxLen++] = BYTE1(messageLength);//数据长度MSB
	usart1_txbuf[MQTT_TxLen++] = BYTE0(messageLength);//数据长度LSB 
	memcpy(&usart1_txbuf[MQTT_TxLen],message,messageLength);
    MQTT_TxLen += messageLength;

	MQTT_SendBuf(usart1_txbuf,MQTT_TxLen);
  return MQTT_TxLen;
}


三、主程序中初始化及具体发布订阅等相关操作实现

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  使用usart1传输AT指令控制ESP8266,使用usart2打印调试信息
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//#include "hal_temp_hum.h"
#include "esp8266_at.h"
#include "esp8266_mqtt.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define USER_MAIN_DEBUG

#ifdef USER_MAIN_DEBUG
#define user_main_printf(format, ...) printf( format "\r\n",##__VA_ARGS__)
#define user_main_info(format, ...) printf("【main】info:" format "\r\n",##__VA_ARGS__)
#define user_main_debug(format, ...) printf("【main】debug:" format "\r\n",##__VA_ARGS__)
#define user_main_error(format, ...) printf("【main】error:" format "\r\n",##__VA_ARGS__)
#else
#define user_main_printf(format, ...)
#define user_main_info(format, ...)
#define user_main_debug(format, ...)
#define user_main_error(format, ...)
#endif
//--------------------------
//ESP8266连接服务器相关信息

//加入onenet服务器协议地址端口号
#define SERVER_INFO		"AT+CIPSTART=\"TCP\",\"183.230.40.39\",6002"

//OneNet物联网服务器的设备信息
#define MQTT_DEVID		"6613523"   //你的设备ID
#define MQTT_PROID		"3945"      //你的产品ID
#define MQTT_AUTH_INFO	"mysqj"     //你的鉴权信息

#define MQTT_SERVER_MODE	"TCP"              //服务器协议TCP/UDP
#define MQTT_SERVER_IP		"183.230.40.39"    //服务器IP
#define MQTT_SERVER_PORT	6002               //服务器端口号
//--------------------------------
//此处根据自己的wifi作调整
#define WIFI_NAME "TP-LINK_xB"
#define WIFI_PASSWD "999999999"

//主题
#define	MQTT_PUBLISH_TOPIC "/MYMQTT/mystm/pub"
#define MQTT_SUBSCRIBE_TOPIC "/MYMQTT/mystm/sub"
#define MQTT_SYS_TOPIC "$dp"

//此处是主循环运行延时宏定义
#define LOOPTIME 30 	//程序周期循环延时时间:30ms
#define COUNTER_LEDBLINK			(300/LOOPTIME)		//LED运行闪烁时间:300ms
#define COUNTER_RUNINFOSEND		(5000/LOOPTIME)		//运行串口提示:5s
#define COUNTER_MQTTHEART     (5000/LOOPTIME)		//MQTT发送心跳包:5s
#define COUNTER_STATUSREPORT	(3000/LOOPTIME)	

#define LEDpc13_ON       HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_SET)
#define LEDpc13_OFF      HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_RESET)
#define LEDpc13_ON_OFF   HAL_GPIO_TogglePin(LEDpc13_GPIO_Port,LEDpc13_Pin)

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
char mqtt_message[300];	//MQTT的上报消息缓存

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void Enter_ErrorMode(uint8_t mode);
void ES8266_MQTT_Init(void);
void Change_LED_Status(void);
void STM32DHT11_StatusReport(void);
void deal_MQTT_message(uint8_t* buf,uint16_t len);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
	//开启USART1接收中断
	HAL_UART_Receive_IT(&huart1,usart1_rxone,1);			//打开USART1中断,接收订阅消息
	ES8266_MQTT_Init();										//初始化MQTT
    printf("test\r\n");

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  	uint16_t Counter_RUNLED_Blink = 0;
	uint16_t Counter_RUNInfo_Send = 0;
	uint16_t Counter_MQTT_Heart = 0;
	uint16_t Counter_StatusReport = 0;

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      		//运行灯闪烁
//		if(Counter_RUNLED_Blink++>COUNTER_LEDBLINK)
//		{
//			Counter_RUNLED_Blink = 0;
//			LEDpc13_ON_OFF;
//		}
		
		//运行状态打印
		if(Counter_RUNInfo_Send++>COUNTER_RUNINFOSEND)
		{
			Counter_RUNInfo_Send = 0;
			user_main_info("程序正在运行!\r\n");
		}
		
		//心跳包发送
//		if(Counter_MQTT_Heart++>COUNTER_MQTTHEART)
//		{
//			Counter_MQTT_Heart = 0;
//			MQTT_SentHeart();
//		}
		
		//本机状态上报
		if(Counter_StatusReport++>COUNTER_STATUSREPORT)
		{
			Counter_StatusReport = 0;
			STM32DHT11_StatusReport();
		}
		
		//如果接收缓存有数据
		if(usart1_rxcounter)
		{
			deal_MQTT_message(usart1_rxbuf,usart1_rxcounter);//打印出数据
		}

		HAL_Delay(LOOPTIME);
		
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
/******************************  USART1接收中断代码  *****************************/

// ES8266驱动串口接收中断处理函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)	// 判断是由哪个串口触发的中断
	{
		//将接收到的数据放入接收usart1接收数组
		usart1_rxbuf[usart1_rxcounter] = usart1_rxone[0];
		usart1_rxcounter++;	//接收数量+1
		
		//重新使能串口1接收中断
		HAL_UART_Receive_IT(&huart1,usart1_rxone,1);		
	}
}

/******************************  按键中断代码  *****************************/

//KEY1按下动作执行函数
void KEY1_Pressed(void)
{
	user_main_debug("我按下了KEY_1\r\n");
	Change_LED_Status();
}

//KEY2按下动作执行函数
void KEY2_Pressed(void)
{
	user_main_debug("我按下了KEY_2\r\n");
}

//按键中断处理函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    switch(GPIO_Pin)  
    {  
        case KEYpa0_Pin:KEY1_Pressed();break;  
        case KEYpa1_Pin:KEY2_Pressed();break;   
        default:break;  
    }  
}

//改变LED灯状态
void Change_LED_Status(void)
{
    static uint8_t ledstatus = 0;
    
    switch(ledstatus++)
    {
        case 0:
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_RESET);
            HAL_Delay(500);
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_SET);
            HAL_Delay(500);
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_RESET);
        break;
        case 1:
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_SET);
            HAL_Delay(1000);
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_RESET);
            HAL_Delay(10000);
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_SET);
        case 2:
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_RESET);
            HAL_Delay(1000);
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_SET);
            HAL_Delay(1000);
            HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_RESET);
        default:
            ledstatus=0;break;
    }
}

/******************************  进入错误模式代码  *****************************/

//进入错误模式等待手动重启
void Enter_ErrorMode(uint8_t mode)
{
	HAL_GPIO_WritePin(LEDpc13_GPIO_Port,LEDpc13_Pin,GPIO_PIN_SET);
	while(1)
	{
		switch(mode){
			case 0:user_main_error("ESP8266初始化失败!\r\n");break;
			case 1:user_main_error("ESP8266连接热点失败!\r\n");break;
			case 2:user_main_error("ESP8266连接阿里云服务器失败!\r\n");break;
			case 3:user_main_error("ESP8266阿里云MQTT登陆失败!\r\n");break;
			case 4:user_main_error("ESP8266阿里云MQTT订阅主题失败!\r\n");break;
			default:user_main_info("Nothing\r\n");break;
		}
		user_main_info("请重启开发板");
		//HAL_GPIO_TogglePin(LEDpc13_GPIO_Port,LEDpc13_Pin);
		HAL_Delay(200);
	}
}


/******************************  STM32 MQTT业务代码  *****************************/

//MQTT初始化函数
void ES8266_MQTT_Init(void)
{
	uint8_t status=0;

	//初始化
	if(ESP8266_Init())
	{
		user_main_info("ESP8266初始化成功!\r\n");
		status++;
	}
	else Enter_ErrorMode(0);

	//连接热点
	if(status==1)
	{
		if(ESP8266_ConnectAP(WIFI_NAME,WIFI_PASSWD))
		{
			user_main_info("ESP8266连接热点成功!\r\n");
			status++;
		}
		else Enter_ErrorMode(1);
	}
	
	//连接中移动IOT服务器
	if(status==2)
	{
		if(ESP8266_ConnectServer(MQTT_SERVER_MODE,MQTT_SERVER_IP,MQTT_SERVER_PORT)!=0)
		{
			user_main_info("ESP8266连接中移动服务器成功!\r\n");
			status++;
		}
		else Enter_ErrorMode(2);
	}
	
	//登陆MQTT
	if(status==3)
	{
		if(MQTT_Connect(MQTT_DEVID, MQTT_PROID, MQTT_AUTH_INFO) != 0)
		{
			user_main_info("ESP8266中移动MQTT登陆成功!\r\n");
			status++;
		}
		else Enter_ErrorMode(3);
	}

	//订阅主题
	if(status==4)
	{
		if(MQTT_SubscribeTopic(MQTT_SUBSCRIBE_TOPIC,0,1) != 0)
		{
			user_main_info("ESP8266中移动MQTT订阅主题成功!\r\n");
		}
		else Enter_ErrorMode(4);
	}
}

//单片机状态上报
//数据流上传格式为:固定报文头0x30(0x31,0x32),剩余数据长度,可变报文头(topic:$dp。json格式采用json格式3:0x03。playload:{"tem":34.3})
//MQTT:0x30 0x1420 0x00 0x03 0x24$ 0x64d 0x70p 0x03 0x00 0x0C12 0x7B{ 0x22" 0x74t 0x65e 0x6Dm 0x22" 0x3A: 0x333 0x344 0x2E. 0x333 0x7D} 
//发送数据为:30 14 00 03 24 64 70 03 00 0C 7B 22 74 65 6D 22 3A 33 33 2E 34 7D
//            30 16 00 03 24 64 70 03 00 0E 03 1C 7B 22 74 65 6D 22 3A 33 33 2E 34 7D        
void STM32DHT11_StatusReport(void)
{
    //获取温湿度信息
	uint8_t temperature=13;
	uint8_t humidity=12;

    //上报一次数据
	uint8_t led_status = HAL_GPIO_ReadPin(KEYpa0_GPIO_Port,KEYpa0_Pin) ? 0:1;
    //sprintf(mqtt_message0,"{\"tem\":%.1f}",(float)temperature);
    //strcat(mqtt_message,mqtt_message0);
	strcat(mqtt_message,"{\"tem\":35.3}");//注意:此处要先将获取的数据放进去,再连接字符串

	MQTT_PublishJsonData(MQTT_SYS_TOPIC,mqtt_message,0,3);
}
char temp_str[30];    // 临时子串
void ReadStrUnit(char * str,char *temp_str,int idx,int len)  // 从母串中获取与子串长度相等的临时子串
{
    int index;
    for(index = 0; index < len; index++)
    {
        temp_str[index] = str[idx+index];
    }
    temp_str[index] = '\0';
}


int GetSubStrPos(char *str1,char *str2)
{
    int idx = 0;
    int len1 = strlen(str1);
    int len2 = strlen(str2);

    if( len1 < len2)
    {
        //printf("error 1 \n"); // 子串比母串长
        return -1;
    }

    while(1)
    {
        ReadStrUnit(str1,temp_str,idx,len2);    // 不断获取的从 母串的 idx 位置处更新临时子串
        if(strcmp(str2,temp_str)==0)break;      // 若临时子串和子串一致,结束循环
        idx++;                                  // 改变从母串中取临时子串的位置
        if(idx>=len1)return -1;                 // 若 idx 已经超出母串长度,说明母串不包含该子串
    }

    return idx;    // 返回子串第一个字符在母串中的位置
}

/********处理MQTT下发的消息*****************
在onenet的应用管理中将开关的属性EDP设置为onoff:{V},
然后按下开关单片机将会受到平台下发的如下信息
03 *$creq/fd287588-5d6c-5389-81c6-e401ea9c8117onoff:1       //【main】info:MQTT收到消息,数据长度=53 
03 *$creq/517635c5-ed9e-51d9-8c0d-e0ddd24c49baonoff:0     //【main】info:MQTT收到消息,数据长度=53 

*/
void deal_MQTT_message(uint8_t* buf,uint16_t len)
{
    uint8_t data[512];
	uint16_t data_len = len;
    int ret;
	for(int i=0;i<data_len;i++)
	{
		data[i] = buf[i];
		HAL_UART_Transmit(&huart2,&data[i],1,100);
	}
    printf("收到的数据=%s\r\n",data);
	memset(usart1_rxbuf,0,sizeof(usart1_rxbuf)); //清空接收缓冲  
	usart1_rxcounter=0;
	user_main_info("MQTT收到消息,数据长度=%d \n",data_len);
    
    printf("收到的数据1=%s\r\n",data);
    
    //通过下发的数据来控制LED灯开关
	ret = GetSubStrPos((char*)&data[3],"onoff:1");//由于数据第3个是0x00,会当作字符结束符,因此从第4个开始查找
//    ret=strstr((char*)data,"onoff:0");
    printf("收到的ret=%d\r\n",ret);
	if( ret>0 )  { LEDpc13_OFF;printf("onoff=%s\r\n",ret);}
    else LEDpc13_ON;
}


/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


版权声明:本文为sea1216原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。