STM32玩转物联网实战篇:5.ESP8266 WIFI模块MQTT通信示例详解

1、准备开发板

开发板功能区分布图
在这里插入图片描述

开发板俯视图
在这里插入图片描述

2、实验讲解

    在之前的章节中,已经讲解过了MQTT的通讯原理和组包过程,现在开始手把手的教大家用代码来实现连接MQTT平台以及数据的交互,实际上这篇文章已经拖更接近两年了,非常感谢那些默默支持我的朋友们。代码的实现过程主要参考了MQTT中文网[https://mqtt.p2hp.com/mqtt311],大家感兴趣的话可以打开进行学习,你们的点赞和支持是我持续更新的动力。

    大家不要被我的代码量给吓到了,下面的代码无非是封装好的AT指令,在下面我也会贴出AT指令实现的过程帮助大家理解,另外我也会贴出逆向后的报文,让大家知道在明文情况下,黑客抓包会获得哪些数据。那些复杂指令集和实现过程也是通过一些重复且简单的指令组成的,希望你能够保持你的自信心,抱着良好的心态去对待生活。

提前准备的数据(Ready)

服务器地址 broker-cn.emqx.io
服务器端口	1883WIFI名称 MCSCV		
WIFI密码 jnszstmOBB	//鉴权三元组
客户端ID	111|hmvcfu	
用户名		hmvcfu
密码		a02d6361531c2dae941d5b022982d944MQTT上报主题		sys/hmvcfu/post"				MQTT下发主题		sys/hmvcfu/control"		

初始化并连接服务器(Init)

//设置STA模式
ESP8266> AT+CWMODE=1
//设置单连接模式
ESP8266> AT+CIPMUX=0
//连接路由器WIFI
ESP8266> AT+CWJAP="MCSCV","jnszstmOBB"
//连接服务器
ESP8266> AT+CIPSTART="TCP","broker-cn.emqx.io",1883

发送MQTT连接报文(Connect)

//发送连接报文
ESP8266> AT+CIPSEND=66
ESP8266> 10 40 00 04 4D 51 54 54 04 C0 00 78 00 0A 31 31 31 7C 68 6D 76 63 66 75 00 06 68 6D 76 63 66 75 00 20 61 30 32 64 36 33 36 31 35 33 31 63 32 64 61 65 39 34 31 64 35 62 30 32 32 39 38 32 64 39 34 34 
//逆向后的连接报文
◇@\0MQTT繺0x\0
111|hmvcfu\0hmvcfu\0 a02d6361531c2dae941d5b022982d944□
//发送后回应
ESP8266< +IPD,5: 20 02 00 00

发送MQTT订阅主题报文(Subscribe)

//发送订阅主题报文
ESP8266> AT+CIPSEND=25
ESP8266> 82 17 00 01 00 12 73 79 73 2F 68 6D 76 63 66 75 2F 63 6F 6E 74 72 6F 6C 00   
//逆向后的订阅主题报文
?\0\0sys/hmvcfu/control\0//发送后回应
ESP8266< +IPD,5:90 03 00 01 00 

发送MQTT发布主题报文(Publish)

//发送发布主题报文
ESP8266> AT+CIPSEND=64
ESP8266> 30 3E 00 0F 73 79 73 2F 68 6D 76 63 66 75 2F 70 6F 73 74 7B 22 6D 73 67 22 3A 7B 22 70 61 72 61 6D 64 61 74 61 22 3A 5B 7B 22 74 65 6D 70 22 3A 32 30 2C 22 68 75 6D 69 22 3A 39 38 7D 5D 7D 7D 
//逆向后的发布主题报文
0>\0sys/hmvcfu/post{"msg":{"paramdata":[{"temp":20,"humi":98}]}}//接收到SEND OK说明发布成功
ESP8266< SEND OK

接收来自MQTT发布者发布的报文(Receive)

//接收到openLed指令
ESP8266< +IPD,29:30 1B 00 12 73 79 73 2F 68 6D 76 63 66 75 2F 63 6F 6E 74 72 6F 6C 6F 70 65 6E 4C 65 64 
//逆向后的接收指令
0\0sys/hmvcfu/controlopenLed□
//接收到CloseLed指令
ESP8266< +IPD,30:30 1C 00 12 73 79 73 2F 68 6D 76 63 66 75 2F 63 6F 6E 74 72 6F 6C 63 6C 6F 73 65 4C 65 64 
//逆向后的接收指令
0\0sys/hmvcfu/controlcloseLed□

3、在MDK中编写代码

MQTT组包函数
MQTT_PacketConnect连接服务器的组包函数
MQTT_UnPacketConnectAck连接消息解包
MQTT_PacketPublishMQTT发布消息组包函数
MQTT_UnPacketPublishAckMQTT发布回复消息解包函数
MQTT_UnPacketPublishMQTT收到发布消息解包函数
MQTT_PacketSubscribeMQTT订阅消息组包函数
MQTT_UnPacketSubscribeMQTT订阅回复消息解包函数
MQTT_UnPacketUnSubscribeMQTT取消订阅回复消息解包函数
MQTT_PacketUnSubscribeMQTT取消订阅消息组包函数
MQTT_PacketDisConnect断开连接消息组包
MQTT_PacketPingMQTT心跳请求组包函数
MQTT_UnPacketRecvMQTT接收消息解包函数
MQTT基础函数
MQTTClient_DevLinkMQTT连接函数
MQTTClient_SubscribeMQTT订阅函数
MQTTClient_PublishMQTT客户端发布函数
MQTTClient_RevProMQTT客户端协议接收函数
MQTTClient_HeartBeatMQTT客户端心跳函数

新建GloalConfig.h文件,添加以下代码

#ifndef __GLOAL_CONFIG_H_
#define __GLOAL_CONFIG_H_#define SERVER_IP "broker-cn.emqx.io"	//服务器地址
#define SERVER_PORT	"1883"//服务器端口#define WIFI_NAME "MCSCV"		//WIFI名称
#define WIFI_PSWD "jnszstmOBB"	//WIFI密码//鉴权三元组
#define MQTT_CLIENTID	"111|hmvcfu"	//客户端ID
#define MQTT_USERNAME	"hmvcfu"	//用户名
#define MQTT_PASSWORD	"a02d6361531c2dae941d5b022982d944"	//密码#define MQTT_PUB_TOPIC		"sys/hmvcfu/post"				//MQTT上报主题
#define MQTT_SUB_TOPIC		"sys/hmvcfu/control"			//MQTT下发主题#endif

新建shfm_mqtt.c文件,添加以下代码

#include "shfm_mqtt.h"/*
*@brief		连接服务器的组包函数
*@param		Username       用户名
*					Password       密码
*					ClientID       客户端ID
*					mqttPacket     数据包指针
*@return	数据包长度
*/
uint16_t MQTT_PacketConnect(char *Username,char *Password,char *ClientID,uint8_t *mqttPacket)
{int16_t  len;int16_t  ClientIDLen;int16_t  UsernameLen;int16_t  PasswordLen;uint16_t mqttPacketLen = 0;ClientIDLen = strlen(ClientID);UsernameLen = strlen(Username);PasswordLen = strlen(Password);//可变报头(10)+Payload  每个字段包含两个字节的长度标识len = 10 + (ClientIDLen + 2) + (UsernameLen + 2) + (PasswordLen + 2);	//加2是因为每个字段占两个字节//固定报头//控制报文类型mqttPacket[mqttPacketLen++] = 0x10;		//MQTT Message Type CONNECT//剩余长度(不包括固定头部),剩余长度最多可以用四个字节来表示do{uint8_t encodedByte = len % 128;len = len / 128;// 如果有更多的数据要编码,请设置该字节的最高位if ( len > 0 )encodedByte = encodedByte | 128;mqttPacket[mqttPacketLen++] = encodedByte;} while ( len > 0 );//可变报头//协议名mqttPacket[mqttPacketLen++] = 0;        		// 协议名长度高位mqttPacket[mqttPacketLen++] = 4;        		// 协议名长度低位mqttPacket[mqttPacketLen++] = 'M';        	// ASCII 字符 MmqttPacket[mqttPacketLen++] = 'Q';        	// ASCII 字符 QmqttPacket[mqttPacketLen++] = 'T';        	// ASCII 字符 TmqttPacket[mqttPacketLen++] = 'T';        	// ASCII 字符 T//协议级别mqttPacket[mqttPacketLen++] = 4;        		// MQTT协议版本号 4//连接标志mqttPacket[mqttPacketLen++] = MQTT_CONNECT_WILL_QOS0|MQTT_CONNECT_USER_NAME|MQTT_CONNECT_PASSORD;        	// 连接标志mqttPacket[mqttPacketLen++] = WORD_MSB(MQTT_KEEP_LIVE_TIME);        		// 保活时间长度高位mqttPacket[mqttPacketLen++] = WORD_LSB(MQTT_KEEP_LIVE_TIME);        		// 保活时间长度低位mqttPacket[mqttPacketLen++] = WORD_MSB(ClientIDLen);// 客户端ID长度高位mqttPacket[mqttPacketLen++] = WORD_LSB(ClientIDLen);// 客户端ID长度低位memcpy(&mqttPacket[mqttPacketLen],ClientID,ClientIDLen);mqttPacketLen += ClientIDLen;if(UsernameLen > 0){mqttPacket[mqttPacketLen++] = WORD_MSB(UsernameLen);		//用户名长度高位mqttPacket[mqttPacketLen++] = WORD_LSB(UsernameLen);    	//用户名长度低位memcpy(&mqttPacket[mqttPacketLen],Username,UsernameLen);mqttPacketLen += UsernameLen;}if(PasswordLen > 0){mqttPacket[mqttPacketLen++] = WORD_MSB(PasswordLen);		//密码长度高位mqttPacket[mqttPacketLen++] = WORD_LSB(PasswordLen);    	//密码长度低位memcpy(&mqttPacket[mqttPacketLen],Password,PasswordLen);mqttPacketLen += PasswordLen;}return mqttPacketLen;
}/*
*@brief		连接消息解包
*@param		rev_data       接收的数据
*@return	数据包长度
*/
uint8_t MQTT_UnPacketConnectAck(uint8_t *rev_data)
{if(rev_data[1] != 0x02)return 1;if(rev_data[2] == 0x00 || rev_data[2] == 0x01)return rev_data[3];elsereturn 255;}/*
*@brief		断开连接消息组包
*@param		mqttPacket       数据包指针
*@return	数据包长度
*/
uint8_t MQTT_PacketDisConnect(uint8_t *mqttPacket)
{uint16_t mqttPacketLen = 0;//固定报头mqttPacket[mqttPacketLen++] = M_DISCONNECT << 4;//剩余长度值mqttPacket[mqttPacketLen++] = 0;return 0;}/*
*@brief		MQTT订阅消息组包函数
*@param		topic       主题名称
*					qos					消息等级
*					mqttPacket  数据包指针
*@return	数据包长度
*/
uint16_t MQTT_PacketSubscribe(char *topic,uint8_t qos,uint8_t *mqttPacket)
{int16_t len;int16_t topiclen;uint16_t mqttPacketLen = 0;topiclen = strlen(topic);len = 2 + (topiclen + 2) + 1;	//可变报头的长度(2字节)加上有效载荷的长度//固定报头//控制报文类型mqttPacket[mqttPacketLen++] = 0x82; //消息类型和标志订阅//剩余长度do{unsigned char  encodedByte = len % 128;len = len / 128;// 如果有更多的数据要编码,请设置该字节的最高位if ( len > 0 )encodedByte = encodedByte | 128;mqttPacket[mqttPacketLen++] = encodedByte;} while ( len > 0 );//可变报头mqttPacket[mqttPacketLen++] = 0;				//消息标识符 MSBmqttPacket[mqttPacketLen++] = 0x01;           //消息标识符 LSB//有效载荷mqttPacket[mqttPacketLen++] = WORD_MSB(topiclen);//主题长度 MSBmqttPacket[mqttPacketLen++] = WORD_LSB(topiclen);//主题长度 LSBmemcpy(&mqttPacket[mqttPacketLen],topic,topiclen);mqttPacketLen += topiclen;mqttPacket[mqttPacketLen++] = qos;//QoS级别return mqttPacketLen;
}/*
*@brief		MQTT订阅回复消息解包函数
*@param		rev_data       接收的数据
*@return	0-成功		其他-失败
*/
uint8_t MQTT_UnPacketSubscribe(uint8_t *rev_data)
{uint8_t result = 255;if(rev_data[2] == WORD_MSB(MQTT_SUBSCRIBE_ID) && rev_data[3] == WORD_LSB(MQTT_SUBSCRIBE_ID)){switch(rev_data[4]){case 0x00:case 0x01:case 0x02://MQTT订阅成功result = 0;break;case 0x80://MQTT订阅失败result = 1;break;default://MQTT未知错误result = 2;break;}}return result;
}/*
*@brief		MQTT取消订阅消息组包函数
*@param		topic       主题名称
*					qos					消息等级
*					mqttPacket  数据包指针
*@return	数据包长度
*/
uint16_t MQTT_PacketUnSubscribe(char *topic,uint8_t qos,uint8_t *mqttPacket)
{int16_t topiclen;int16_t len;uint16_t mqttPacketLen = 0;topiclen = strlen(topic);len = 2 + (topiclen + 2);//可变报头的长度(2字节)加上有效载荷的长度//固定报头//控制报文类型mqttPacket[mqttPacketLen++] = 0xA2;    //取消订阅//剩余长度do{uint8_t encodedByte = len % 128;len = len / 128;// if there are more data to encode, set the top bit of this byteif ( len > 0 )encodedByte = encodedByte | 128;mqttPacket[mqttPacketLen++] = encodedByte;} while ( len > 0 );//可变报头mqttPacket[mqttPacketLen++] = 0;				//消息标识符 MSBmqttPacket[mqttPacketLen++] = 0x01;           //消息标识符 LSB//有效载荷mqttPacket[mqttPacketLen++] = WORD_MSB(topiclen);//主题长度 MSBmqttPacket[mqttPacketLen++] = WORD_LSB(topiclen);//主题长度 LSBmemcpy(&mqttPacket[mqttPacketLen],topic,topiclen);mqttPacketLen += topiclen;return mqttPacketLen;
}/*
*@brief		MQTT取消订阅回复消息解包函数
*@param		rev_data       接收的数据
*@return	0-成功		其他-失败
*/
uint8_t MQTT_UnPacketUnSubscribe(uint8_t *rev_data)
{uint8_t result = 1;if(rev_data[2] == WORD_MSB(MQTT_UNSUBSCRIBE_ID) && rev_data[3] == WORD_LSB(MQTT_UNSUBSCRIBE_ID)){result = 0;}return result;}/*
*@brief		MQTT发布消息组包函数
*@param		topic       主题
*					message     消息
*					qos       	消息等级
*					mqttPacket  数据包指针
*@return	0-成功		其他-失败
*/
uint16_t MQTT_PacketPublish(char *topic, char *message, uint8_t qos,uint8_t *mqttPacket)
{int16_t len;int16_t topicLength = strlen(topic);int16_t messageLength = strlen(message);static uint16_t id=0;uint16_t  mqttPacketLen = 0;//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度//QOS为0时没有标识符//数据长度             主题名   报文标识符   有效载荷if(qos)	len = (2+topicLength) + 2 + messageLength;else	len = (2+topicLength) + messageLength;//固定报头//控制报文类型mqttPacket[mqttPacketLen++] = 0x30;    // MQTT 消息类型是 PUBLISH//剩余长度do{uint8_t  encodedByte = len % 128;len = len / 128;// 如果有更多的数据要编码,请设置该字节的最高位if ( len > 0 )encodedByte = encodedByte | 128;mqttPacket[mqttPacketLen++] = encodedByte;} while ( len > 0 );mqttPacket[mqttPacketLen++] = WORD_MSB(topicLength);//主题长度MSBmqttPacket[mqttPacketLen++] = WORD_LSB(topicLength);//主题长度LSBmemcpy(&mqttPacket[mqttPacketLen],topic,topicLength);//拷贝主题mqttPacketLen += topicLength;//报文标识符if(qos){mqttPacket[mqttPacketLen++] = WORD_MSB(id);mqttPacket[mqttPacketLen++] = WORD_LSB(id);id++;}memcpy(&mqttPacket[mqttPacketLen],message,messageLength);mqttPacketLen += messageLength;return mqttPacketLen;
}/*
*@brief		MQTT发布回复消息解包函数
*@param		rev_data       接收的数据
*@return	0-成功		其他-失败
*/
uint8_t MQTT_UnPacketPublishAck(uint8_t *rev_data)
{if(rev_data[1] != 2)return 1;if(rev_data[2] == WORD_MSB(MQTT_PUBLISH_ID) && rev_data[3] == WORD_LSB(MQTT_PUBLISH_ID))return 0;elsereturn 1;}/*
*@brief		MQTT收到发布消息解包函数
*@param		rev_data    接收的数据
*					topic     	主题
*					topic_len   主题长度
*					payload  		有效载荷payload_len 有效载荷长度
*@return	0-成功		其他-失败
*/
uint8_t MQTT_UnPacketPublish(uint8_t *rev_data, int8_t *topic, uint16_t *topic_len, int8_t *payload, uint16_t *payload_len)
{int i = 0;uint8_t *msgPtr;uint32_t remain_len = 0;uint32_t multiplier = 1;uint8_t *in;in = rev_data+1;for(i = 0; i < 4; ++i){remain_len += (in[i] & 0x7f) * multiplier;if(!(in[i] & 0x80)){
//					return i + 1;i += 1;break;}multiplier <<= 7;if(multiplier >= 2097152)		//128 * 128 * 128{return -2;					// error, out of range}}msgPtr = rev_data + i + 1;if(remain_len < 2)							//retainreturn 255;*topic_len = (uint16_t)msgPtr[0] << 8 | msgPtr[1];if(remain_len < *topic_len + 2)return 255;memcpy(topic, (int8_t *)msgPtr + 2, *topic_len);		//复制数据*payload_len = remain_len - 2 - *topic_len;memcpy(payload, (int8_t *)msgPtr + 2 + *topic_len, *payload_len);return 0;
}/*
*@brief		MQTT心跳请求组包函数
*@param		mqttPacket       数据包指针
*@return	0-成功		其他-失败
*/
uint8_t MQTT_PacketPing(uint8_t *mqttPacket)
{uint8_t mqttPacketLen = 0;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket[mqttPacketLen++] = M_PINGREQ << 4;//固定头部----------------------剩余长度-------------------------------------------------mqttPacket[mqttPacketLen++] = 0;return mqttPacketLen;}/*
*@brief		MQTT接收消息解包函数
*@param		dataPtr       消息包指针
*@return	0-成功		其他-失败
*/
uint8_t MQTT_UnPacketRecv(uint8_t *dataPtr)
{uint8_t status = 255;uint8_t type = dataPtr[0] >> 4;				//类型检查if(type < 1 || type > 14)return status;if(type == M_PUBLISH)status = type;elsestatus = type;return status;
}

新建shfm_mqtt.h文件,添加以下代码

#ifndef __MQTTData_H
#define __MQTTData_H//C库
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>#define WORD_MSB(A)         (uint8_t)((A & 0xFF00) >> 8)
#define WORD_LSB(A)         (uint8_t)(A & 0x00FF)#define MQTT_PUBLISH_ID			10
#define MQTT_SUBSCRIBE_ID		20
#define MQTT_UNSUBSCRIBE_ID		30#define MQTT_CONNECT_WILL_QOS0    0x00
#define MQTT_CONNECT_PASSORD      0x40
#define MQTT_CONNECT_USER_NAME    0x80#define MQTT_KEEP_LIVE_TIME		120	//单位 S#define _DEBUG_MQTT 1typedef enum
{//名字 	    值 			报文流动方向 	描述M_RESERVED1	=0	,	//	禁止	保留M_CONNECT		,	//	客户端到服务端	客户端请求连接服务端M_CONNACK		,	//	服务端到客户端	连接报文确认M_PUBLISH		,	//	两个方向都允许	发布消息M_PUBACK		,	//	两个方向都允许	QoS 1消息发布收到确认M_PUBREC		,	//	两个方向都允许	发布收到(保证交付第一步)M_PUBREL		,	//	两个方向都允许	发布释放(保证交付第二步)M_PUBCOMP		,	//	两个方向都允许	QoS 2消息发布完成(保证交互第三步)M_SUBSCRIBE		,	//	客户端到服务端	客户端订阅请求M_SUBACK		,	//	服务端到客户端	订阅请求报文确认M_UNSUBSCRIBE	,	//	客户端到服务端	客户端取消订阅请求M_UNSUBACK		,	//	服务端到客户端	取消订阅报文确认M_PINGREQ		,	//	客户端到服务端	心跳请求M_PINGRESP		,	//	服务端到客户端	心跳响应M_DISCONNECT	,	//	客户端到服务端	客户端断开连接M_RESERVED2		,	//	禁止	保留
} _typdef_mqtt_message;uint16_t MQTT_PacketConnect(char *Username,char *Password,char *ClientID,uint8_t *mqttPacket);
uint8_t MQTT_UnPacketConnectAck(uint8_t *rev_data);
uint16_t MQTT_PacketPublish(char *topic, char *message, uint8_t qos,uint8_t *mqttPacket);
uint8_t MQTT_UnPacketPublishAck(uint8_t *rev_data);
uint8_t MQTT_UnPacketPublish(uint8_t *rev_data, int8_t *topic, uint16_t *topic_len, int8_t *payload, uint16_t *payload_len);
uint16_t MQTT_PacketSubscribe(char *topic,uint8_t qos,uint8_t *mqttPacket);
uint8_t MQTT_UnPacketSubscribe(uint8_t *rev_data);
uint8_t MQTT_UnPacketUnSubscribe(uint8_t *rev_data);
uint16_t MQTT_PacketUnSubscribe(char *topic,uint8_t qos,uint8_t *mqttPacket);
uint8_t MQTT_PacketDisConnect(uint8_t *mqttPacket);
uint8_t MQTT_PacketPing(uint8_t *mqttPacket);
uint8_t MQTT_UnPacketRecv(uint8_t *dataPtr);
#endif

新建mqttclient.h文件,添加以下代码

#ifndef __MQTT_CLIENT_H_
#define __MQTT_CLIENT_H_#include "sys.h"#ifndef MQTTCLIENT_OK
#define MQTTCLIENT_OK                                              0
#endif#ifndef MQTTCLIENT_NOK
#define MQTTCLIENT_NOK                                             1
#endif#define  MQTTCLIENT_RETTYPE		unsigned char MQTTCLIENT_RETTYPE MQTTClient_DevLink(void);
MQTTCLIENT_RETTYPE MQTTClient_Subscribe(const char *topic);
MQTTCLIENT_RETTYPE MQTTClient_Publish(const char *topic, const char *msg);
MQTTCLIENT_RETTYPE MQTTClient_HeartBeat(void);
void MQTTClient_RevPro(uint8_t *cmd);
#endif

新建mqttclient.c文件,编写以下代码

#include "mqttclient.h"
#include "shfm_mqtt.h"
#include "ESP8266.h"
#include "stdio.h"
#include "GloalConfig.h"
#include "gpio.h"/*
*@brief		MQTT连接函数
*@param		void
*@return	MQTTCLIENT_OK-成功	其余-失败	
*/
MQTTCLIENT_RETTYPE MQTTClient_DevLink(void)
{uint8_t mqttPacket[100]= {0};uint16_t mqttPacketLen = 0;uint8_t *dataPtr = NULL;UsartPrintf(USART_DEBUG,"Tips: Connect Mqtt\r\n");UsartPrintf(USART_DEBUG,"Tips: UserName:%s\r\n",MQTT_USERNAME);UsartPrintf(USART_DEBUG,"Tips: Password:%s\r\n",MQTT_PASSWORD);UsartPrintf(USART_DEBUG,"Tips: ClientId:%s\r\n",MQTT_CLIENTID);mqttPacketLen = MQTT_PacketConnect(MQTT_USERNAME,MQTT_PASSWORD,MQTT_CLIENTID,mqttPacket);if(mqttPacketLen < 2)return MQTTCLIENT_NOK;ESP8266_SendData(DISABLE,mqttPacket, mqttPacketLen,Single_ID_0);			//上传平台dataPtr = ESP8266_GetIPD(DISABLE,200);									//等待平台响应if(dataPtr != NULL){if(MQTT_UnPacketRecv(dataPtr) == M_CONNACK){return MQTT_UnPacketConnectAck(dataPtr); //返回0为连接成功}}return MQTTCLIENT_NOK;
}/*
*@brief		MQTT订阅函数
*@param		topic			主题
*@return	MQTTCLIENT_OK-成功	其余-失败	
*/
MQTTCLIENT_RETTYPE MQTTClient_Subscribe(const char *topic)
{uint8_t mqttPacket[100]= {0};uint16_t mqttPacketLen = 0;uint8_t *dataPtr = NULL;UsartPrintf(USART_DEBUG,"Tips: Subscribe Topic:%s\r\n",topic);mqttPacketLen = MQTT_PacketSubscribe((char*)topic,MQTT_CONNECT_WILL_QOS0,mqttPacket);if(mqttPacketLen < 2)return MQTTCLIENT_NOK;ESP8266_SendData(DISABLE,mqttPacket, mqttPacketLen,Single_ID_0);			//上传平台dataPtr = ESP8266_GetIPD(DISABLE,200);									//等待平台响应if(dataPtr != NULL){if(MQTT_UnPacketRecv(dataPtr) == M_SUBACK){return MQTT_UnPacketSubscribe(dataPtr); //返回0为连接成功}}return MQTTCLIENT_NOK;}/*
*@brief		MQTT客户端发布函数
*@param		topic			主题
*					msg				发布消息
*@return	MQTTCLIENT_OK-成功	其余-失败	
*/
MQTTCLIENT_RETTYPE MQTTClient_Publish(const char *topic, const char *msg)
{uint8_t mqttPacket[100]= {0};uint16_t mqttPacketLen = 0;uint8_t *dataPtr = NULL;uint8_t ucExecRes = MQTTCLIENT_NOK;UsartPrintf(USART_DEBUG,"Tips: Publish Topic:%s,Message:%s\r\n",topic,msg);mqttPacketLen = MQTT_PacketPublish((char*)topic,(char*)msg,MQTT_CONNECT_WILL_QOS0,mqttPacket);if(mqttPacketLen < 2)return MQTTCLIENT_NOK;if(ESP8266_SendData(DISABLE,mqttPacket, mqttPacketLen,Single_ID_0) == ESP8266_OK)					//向平台发送订阅请求ucExecRes = MQTTCLIENT_OK;return ucExecRes;
}/*
*@brief		MQTT客户端心跳函数
*@param		void
*@return	MQTTCLIENT_OK-成功	其余-失败	
*/
MQTTCLIENT_RETTYPE MQTTClient_HeartBeat(void)
{uint8_t mqttPacket[2]= {0};uint16_t mqttPacketLen = 0;uint8_t *dataPtr = NULL;UsartPrintf(USART_DEBUG,"Tips: HearBeat\r\n");mqttPacketLen = MQTT_PacketPing(mqttPacket);ESP8266_SendData(DISABLE,mqttPacket, sizeof(mqttPacket),Single_ID_0);						//向平台上传心跳请求dataPtr = ESP8266_GetIPD(DISABLE,200);if(dataPtr != NULL){if(dataPtr[0] == M_PINGRESP){return MQTTCLIENT_OK;}}return MQTTCLIENT_NOK;
}/*
*@brief		MQTT客户端协议接收函数
*@param		cmd				接收到的命令
*@return	MQTTCLIENT_OK-成功	其余-失败	
*/
void MQTTClient_RevPro(uint8_t *cmd)
{uint8_t type = 0;uint8_t result = 0;int8_t cmdid_topic[50] = {0};int8_t req_payload[100] = {0};uint16_t topic_len = 0;uint16_t req_len = 0;type = MQTT_UnPacketRecv(cmd); //进行解包if(type == M_PUBLISH){result = MQTT_UnPacketPublish(cmd, cmdid_topic,&topic_len,req_payload, &req_len);if(result == 0){UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",cmdid_topic, topic_len, req_payload, req_len);if(strstr((char*)req_payload,"openLed")){LED_Set(LED_ON);	//打开LED灯}else if(strstr((char*)req_payload,"closeLed")){LED_Set(LED_OFF);	//关闭LED灯}}}
}

打开main.c文件,在main函数输入以下代码

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
ESP8266_RETTYPE netStatus = ESP8266_NOK;
unsigned char netErrCount = 0;
/* USER CODE END PTD */
/* USER CODE BEGIN 1 */unsigned char *dataPtr = NULL;uint32_t send_time = 0;ESP8266_RETTYPE ucExecRes = MQTTCLIENT_NOK;uint8_t mqttWork = 0;char sendData[100];/* 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_DMA_Init();MX_LPUART1_UART_Init();MX_USART1_UART_Init();MX_TIM2_Init();/* USER CODE BEGIN 2 */USART_Interupt_Enable();		//使能串口中断TIM_Interupt_Enable();			//使能定时器中断/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(!netDeviceInfo.netWork)	//如果网络未初始化{if(!NET_DEVICE_Init())	//进行网络初始化{mqttWork = MQTTClient_DevLink() == 0 ? 1 : 0;		//连接MQTT服务器if(mqttWork == 1)	//如果连接MQTT成功{printf("连接服务器成功\r\n");MQTTClient_Subscribe(MQTT_SUB_TOPIC);	//订阅MQTT下发主题}}}if(mqttWork){dataPtr = ESP8266_GetIPD(DISABLE,0);									//等待平台响应if(dataPtr != NULL){if(MQTT_UnPacketRecv(dataPtr) == M_PUBLISH){MQTTClient_RevPro(dataPtr);}}}if(time2Count - send_time >= 10000)		//(1ms * 10000)相当于延时10秒钟{send_time = time2Count;				//记下当前定时器的数值if(mqttWork){sprintf(sendData,"{\"msg\":{\"paramdata\":[{\"temp\":20,\"humi\":98}]}}");	//ucExecRes = MQTTClient_Publish(MQTT_PUB_TOPIC,sendData);if(ucExecRes == MQTTCLIENT_NOK){netErrCount++;	//错误次数进行累加if(netErrCount >= 3)	//超过三次,进行自检{netStatus = ESP8266_Get_LinkStatus();	//检查连接状态if(netStatus == 4)		//网络已经断开{netErrCount = 0;		//将错误清零netDeviceInfo.netWork = 0;	//标志网络断开netDeviceInfo.initStep = 0;	//将初始化步骤清零}}}else{netErrCount = 0;	//无错误,清除错误计数}}}}/* USER CODE END 3 */

4、实验现象

实现的功能
1、上电自动连接WIFI
2、连接WIFI成功后,连接MQTT服务器
3、连接MQTT服务器后,接收来自服务器的指令,并执行指令中的开关灯操作
4、每10秒向服务器上传一次数据
5、上传数据失败超过3次,自动进行网络初始化

客户端连接服务器并上传数据

在这里插入图片描述
在这里插入图片描述

MQTT.fx发送开关灯指令

在这里插入图片描述

客户端接收到数据并执行

在这里插入图片描述

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

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

相关文章

《QT实用小工具·三十九》仿 Windows10 画图3D 的颜色选择器, 但更加强大

1、概述 源码放在文章末尾 该项目实现了仿 Windows10 画图3D 的颜色选择器&#xff0c;功能更加丰富更加强大。 项目部分代码如下所示&#xff1a; import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtGraphicalEffects 1.15Item {id…

【leetcode面试经典150题】72. 从前序与中序遍历序列构造二叉树(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

opencv绘制线段------c++

绘制线段 bool opencvTool::drawLines(std::string image_p, std::vector<cv::Point> points) {cv::Mat ima cv::imread(image_p.c_str()); // 读取图像&#xff0c;替换为你的图片路径 cv::Scalar red cv::Scalar(0, 0, 255); // Red color int thickness 2;// 遍…

面试遇到算法题:实现LRU缓存

请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构。 这是一道大厂面试高频出现的算法题&#xff0c;难度为⭐️⭐️⭐️&#xff0c;属于中等&#xff0c;老铁们来一起看看这个题该怎么解&#xff1f; 1. 原题再现 没有废话&#xff0c;翠花&#xff0c;上酸菜&…

JS 添加数组元素( 4种方法 )

No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、四种…

Spring Boot 集成 EasyExcel 3.x

Spring Boot 集成 EasyExcel 3.x Spring Boot 集成 EasyExcel 3.x 本章节将介绍 Spring Boot 集成 EasyExcel&#xff08;优雅实现Excel导入导出&#xff09;。 &#x1f916; Spring Boot 2.x 实践案例&#xff08;代码仓库&#xff09; 介绍 EasyExcel 是一个基于 Java 的、…

HZNUCTF -- web

HZNUCTF第五届校赛实践赛初赛 Web方向 WriteUp-CSDN博客 ezssti 下载文件 访问 /login 可由源代码中看到 Eval 函数 &#xff0c;可以任意命令执行 按照格式&#xff0c;可执行命令 POST &#xff1a;name{{.Eval "env"}} 可以得到flag &#xff08;尝试ls 只能列出…

「ChatGPT」掀起新一轮AI热潮!超越GPT-4 Turbo,商汤日日新大升级!

目录 拳打 GPT-4 Turbo &#xff0c;脚踢 DALLE 3 端侧大模型&#xff0c;唯快不破 AI 应用落地需要一个即插即用的大模型超市 并不存在 AI 这个行业&#xff0c;只有 AI行业&#xff0c;强调 AI 需要与传统产业合作&#xff0c;这种关系是结合与赋能&#xff0c;而不是颠覆…

java开发之路——用户管理中心_简单初始化

用户管理中心_简单初始化 (一) 初始化项目1. 使用 Ant Design Pro(现成的管理系统) 进行前端初始化2. 后端初始化三种初始化java项目 (二) 遇到的问题【问题1】Ant design pro页面打不开&#xff0c;一直在budiling控制台出现错误error-./src/components/index.ts【问题2】初始…

基于SSM的物业管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的物业管理系统2拥有三种角色 管理员&#xff1a;用户管理、物业管理、房产信息管理、小区概况管理、开发商管理、收费标准管理、物业公司管理等 物业&#xff1a;住户管理、收费…

如何通过cURL库实现远程控制插座

如何通过cURL库实现远程控制插座呢&#xff1f; 本文描述了使用cURL库调用HTTP接口&#xff0c;实现控制插座&#xff0c;即插即用&#xff0c;先插入插座&#xff0c;再接电器&#xff0c;实现远程控制。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格…

udp/tcp错误总结

udp tcp——多进程 tcp——多线程 tcp——线程池 tcp——守护进程 &#x1f386;udp  ✨pthread_create 错误总结  ✨LockGuard错误总结  ✨服务端需要写成多线程  ✨客户端也需要写成多线程  ✨多线程调试工具 &#x1f386;tcp  ✨tcp独有调试工具——telnet  ✨Threa…

基于瞬时频率的语言信号清/浊音判决和高音检测(MATLAB R2021)

语音是由气流激励声道从嘴唇或鼻孔辐射出来而产生的。根据声带是否振动&#xff0c;发音可分为浊音和清音。浊音和清音有明显的区别&#xff0c;浊音具有周期信号的特征&#xff0c;而清音则具有随机噪声的特征&#xff1b;浊音在频域上具有共振峰结构&#xff0c;其能量主要集…

⑤【Shiro】SpringBoot整合Shiro,实现登录认证

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ⑤【Shiro】SpringBoot整合Shiro&#xff0c;实…

AI助力科研创新与效率双提升:ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写

2022年11月30日&#xff0c;可能将成为一个改变人类历史的日子——美国人工智能开发机构OpenAI推出了聊天机器人ChatGPT3.5&#xff0c;将人工智能的发展推向了一个新的高度。2023年4月&#xff0c;更强版本的ChatGPT4.0上线&#xff0c;文本、语音、图像等多模态交互方式使其在…

计算机网络4——网络层2

文章目录 一、地址解析协议ARP二、IP数据报格式1、IP 数据报首部的固定部分中的各字段2、IP 数据报首部的可变部分 三、IP 层转发分组的过程1、流程2、案例分析3、最长前缀匹配4、分组转发算法5、使用二叉线索查找转发表 一、地址解析协议ARP 在实际应用中&#xff0c;我们经常…

第一篇【传奇开心果系列】Python深度学习库技术点案例示例:深度解读深度学习在自动驾驶领域的应用

传奇开心果博文系列 系列博文目录Python深度学习库技术点案例示例系列 博文目录前言一、深度学习在自动驾驶方面的应用介绍二、目标检测和识别示例代码三、路况感知示例代码四、行为预测示例代码五、路径规划示例代码六、自动驾驶控制示例代码七、感知融合示例代码八、高精度地…

【数据结构】串(String)

文章目录 基本概念顺序存储结构比较当前串与串s的大小取子串插入删除其他构造函数拷贝构造函数扩大数组空间。重载重载重载重载[]重载>>重载<< 链式存储结构链式存储结构链块存储结构 模式匹配朴素的模式匹配算法(BF算法)KMP算法字符串的前缀、后缀和部分匹配值nex…

Android 10.0 Launcher3替换桌面app图标后大小和其他app图标不一样的问题解决方案

1.前言 在10.0的系统ROM产品定制化开发中,在关于launcher3的产品定制化开发中,在有些时候需要对一些第三方的app图标做 替换或者是做一些动态图标的替换,发现在替换以后图标大小和其他app的图标大小不一样,所以就需要看是具体哪里 对app的图标做了缩放功能,接下来就需要去…

【网页在线小游戏源码】

网页在线小游戏源码 效果图部分源码领取源码下期更新预报 效果图 部分源码 index.html <!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <meta id"viewport" na…