STM32+W5500_MQTT连阿里云_移植说明


关键词:STM32+W5500_MQTT连阿里云_移植说明  W5500  WIZnet


简介

本文主要介绍W5500如何通过MQTT协议将设备连接到阿里云IoT,并通过MQTT协议实现通信。MQTT协议是基于TCP的协议,所以我们只需要在单片机端实现TCP客户端代码之后就很容易移植MQTT了。

MQTT简介:

1) MQTT协议特点

MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M) 通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。

MQTT协议当前版本为,2014年发布的MQTT v3.1.1。除标准版外,还有一个简化版MQTT-SN,该协议主要针对嵌入式设备,这些设备一般工作于百TCP/IP网络,如:ZigBee。

MQTT协议运行在TCP/IP或其他网络协议,提供有序、无损、双向连接。其特点包括:使用的发布/订阅消息模式,它提供了一对多消息分发,以实现与应用程序的解耦;对负载内容屏蔽的消息传输机制; 对传输消息有三种服务质量(QoS):

2) MQTT协议原理及实现方式

实现MQTT协议需要:客户端和服务器端

MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。

MQTT传输的消息分为:主题(Topic)和消息的内容(payload)两部分 :Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);payload,可以理解为消息的内容,是指订阅者具体要使用的内容

阿里云连接步骤:

MQTT移植步骤:

MQTT代码源码下载地址:(http://www.eclipse.org/paho/)
MQTT的移植非常简单,将C/C++ MQTT Embedded clients的代码添加到工程中,然后我们只需要再次封装4个函数即可:
int transport_sendPacketBuffer(unsigned char* buf, int buflen), 通过网络以TCP的方式发送数据;int transport_getdata(unsigned char* buf, int count),TCP方式从服务器端读取数据,该函数目前属于阻塞函数; int transport_open(void),打开一个网络接口,其实就是和服务器建立一个TCP连接;int transport_close(void);关闭网络接口。
如果已经移植好了socket方式的TCP客户端的程序,那么这几个函数的封装也是非常简单的,程序代码如下所示:

							 /**
				  * @brief  通过TCP方式发送数据到TCP服务器
				  * @param  buf数据首地址
				  * @param  buflen数据长度
				  * @retval 小于0表示发送失败
				  */
				  
				  /*订阅消息*/
				 int Subscribe_sendPacketBuffer(unsigned char* buf, int buflen)
				 {
				     return send(SOCK_TCPS,buf,buflen);
				 }
				 
				 /*发布消息*/
				 int Published_sendPacketBuffer(unsigned char* buf, int buflen)
				 {
				     return send(SOCK_TCPC,buf,buflen);
				 }
				  
				 /**
				 * @brief  阻塞方式接收TCP服务器发送的数据
				 * @param  buf数据存储首地址•
				 * @param  count数据缓冲区长度
				 * @retval 小于0表示接收数据失败
				 */
				 int Subscribe_getdata(unsigned char* buf, int count)
				 {
				 
				     return recv(SOCK_TCPS,buf,count);
				 
				 }
		 
				 int Published_getdata(unsigned char* buf, int count)
				 {
				     return recv(SOCK_TCPC,buf,count);
				 
				 }
				 
				 /**
				 * @brief  打开一个socket并连接到服务器
				 * @param  无
				 * @retval 小于0表示打开失败
				 */
				 int Subscribe_open(void)
				 {
				     int32_t ret;
				     //新建一个socket并绑定本地端口5000
				 	 ret = socket(SOCK_TCPS,Sn_MR_TCP,50000,0x00);
				     if (ret != 1) {
				         printf("%d:Socket Error\r\n",SOCK_TCPS);
				         while (1);
				     	} else {
				         printf("%d:Opened\r\n",SOCK_TCPS);
			     				}
				 
				 
			     while (getSn_SR(SOCK_TCPS)!=SOCK_ESTABLISHED) {
		         printf("connecting\r\n");
				       //连接TCP服务器÷
		         ret = connect(SOCK_TCPS,server_ip,1883);
				            //端口必须为1883
				   }
				 if (ret != 1) {
				         printf("%d:Socket Connect Error\r\n",SOCK_TCPS);
				         while (1);
				     } else {
				         printf("%d:Connected\r\n",SOCK_TCPS);
				     }
				     return 0;
				 }
				 
				 int Published_open(void)
				 {
				     int32_t ret;
				 
				     ret = socket(SOCK_TCPC,Sn_MR_TCP,5001,0x00);
				 
				     if (ret != 1) {
				         printf("%d:Socket1 Error1\r\n",SOCK_TCPC);
				         while (1);
				     } else {
				         printf("%d:socket1 Opened\r\n",SOCK_TCPC);
				     }
				 
				 
				     while (getSn_SR(SOCK_TCPC)!=SOCK_ESTABLISHED) {
				         ret = connect(SOCK_TCPC,server_ip,1883);
				            //端口必须为1883
				 }
				     if (ret != 1) {
				         printf("%d:Socket Connect1 Error\r\n",SOCK_TCPC);
				         while (1);
				     } else {
				         printf("%d:Connected1\r\n",SOCK_TCPC);
				     }
				     return 0;
				 }
				 
				 }
				 
				 /**
				 * @brief  关闭socket
				 * @param  无
				 * @retval 小于0表示关闭失败
				 */
				 int Subscribe_close(void)
				 {
				     disconnect(SOCK_TCPS);
				     printf("close0\n\r");
				 
				     while (getSn_SR(SOCK_TCPC)!=SOCK_CLOSED) {
				         ;
				     }
				     return 0;
				 }
				 
				 
				 int Published_close(void)
				 {
				     disconnect(SOCK_TCPC);
				     printf("close1\n\r");
				 
				     while (getSn_SR(SOCK_TCPC)!=SOCK_CLOSED) {
				         ;
				     }
				     return 0;
				 }
				 

MQTT配置

1) MQTT连接参数说明


举例:


2) MQTT与阿里云连接函数:


参考阿里云内MQTT设备接入手册,计算出设备连接的各项参数,例如下列程序中框中的部分为本例程MQTT与阿里云连接的参数的配置,详细内容如下:
clientId = 192.168.207.115
deviceName = MQTT1
productKey = TKKMt4nMF8U
timestamp = 789(毫秒值)
signmethod = hmacsha1(算法类型)
deviceSecret = secret
那么使用tcp方式提交给mqtt参数分别如下:
(1) mqttClientId:clientId+"|securemode=3,signmethod=hmacsha1,timestamp=789|" clientId=192.168.207.115|securemode=3,signmethod=hmacsha1,timestamp=789|
(2) keepalive时间需要设置超过60秒以上,否则会拒绝连接。
(3) Cleansession为1;
(4) mqttUsername: deviceName+"&"+productKey
username = "MQTT1&TKKMt4nMF8U" (5) password=hmacsha1("secret","clientId192.168.207.115deviceNameMQTT1productKeyTKKMt4nMF8Utimestamp789").toHexString();
最后是二进制转16制字符串大小写不敏感。这个例子结果为 9076b0ebc04dba8a8ebba1f0003552dbc862c9b9
MQTT连接函数原型,tcp_client.c文件中的MQTT_CON_ALI函数中调用make_con_msg函数并通过阿里云设备的参数,设置MQTT连接阿里云函数的参数:

					void make_con_msg(char* clientID,int keepalive, uint8 cleansession,char*username,char* password,unsigned char*buf,int buflen)
	  				{
				      int32_t len,rc;
				      MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
				      data.clientID.cstring = clientID;
				      data.keepAliveInterval = keepalive;
			     	  data.cleansession = cleansession;
				      data.username.cstring = username;
				      data.password.cstring = password;
				     len = MQTTSerialize_connect(buf, buflen, &data);
				           //构造链接报文
	     			return;
					}
				

MQTT连接过程:

					void MQTT_CON_ALI(void)
				  {
				      int len;
				      int type;
				      switch (getSn_SR(0)) {                  
				              //获取socket0的状态
				      case SOCK_INIT:                      
				      //Socket处于初始化完成(打开)状态
				          connect(0, server_ip,server_port);    
				                 //配置Sn_CR为CONNECT,并向TCP服务器发出连接请求¢
				   
				 
				         break;
				     case SOCK_ESTABLISHED:                // 
				     Socket处于连接建立状态
				         if (getSn_IR(0) & Sn_IR_CON) {
				             setSn_IR(0, Sn_IR_CON);       // 
				                      Sn_IR的CON位置1,通知W5500连接已建立
				              
				         }
				         memset(msgbuf,0,sizeof(msgbuf));
				         if ((len=getSn_RX_RSR(0))==0) {
				             if (1==CONNECT_FLAG) {
				                 printf("send connect\r\n");
				 
				                 /*MQTTÆ拼接连接报文
				                 *根据阿里云平台MQTT设备接入手册配置
				           
				                 */
				 
				                 //void make_con_msg(char* clientID,int keepalive, 
				                                     uint8 cleansession,char*username,
				                                     char* password,unsigned char*buf,
				                                     int buflen)
				                 make_con_msg("192.168.207.115|securemode=3,
				                              signmethod=hmacsha1,timestamp=789|",180, 
				                              1,"MQTT1&TKKMt4nMF8U",
				                              "9076b0ebc04dba8a8ebba1f0003552dbc862c9b9"
				                              ,msgbuf,sizeof(msgbuf));
				 
				 
				                 //printf(" server_ip: %d.%d.%d.%d\r\n", server_ip[0],
				                          server_ip[1],server_ip[2],server_ip[3]);
				                 //printf("connect ALY\r\n");
				                 CONNECT_FLAG = 0;
				                 send(0,msgbuf,sizeof(msgbuf));
				                 Delay_s(2);
				                 while ((len=getSn_RX_RSR(0))==0) {
				                     Delay_s(2);
				                     send(0,msgbuf,sizeof(msgbuf));
				                 };
				                 recv(0,msgbuf,len);
				                 while (mqtt_decode_msg(msgbuf)!=CONNACK) { 
				                        //判断是不是CONNACK
				                     printf("wait ack\r\n");
				                 }
				             } else if (SUB_FLAG == 1) {
				                 memset(msgbuf,0,sizeof(msgbuf));
				                 make_sub_msg(topic,msgbuf,sizeof(msgbuf));
				                 // make_pub_msg(topic,msgbuf,sizeof(msgbuf),"hello");
				                 send(0,msgbuf,sizeof(msgbuf)); 
				                     // 接收到数据后再回给服务器,完成数据回环
				                      
				                 SUB_FLAG = 0;
				                 Delay_s(2);
				                 while ((len=getSn_RX_RSR(0))==0) {
				                     Delay_s(2);
				                     send(0,msgbuf,sizeof(msgbuf));
				                 };
				                 recv(0,msgbuf,len);
				                 while (mqtt_decode_msg(msgbuf)!=SUBACK) { 
				                        //判断是不是SUBACK
				                     printf("wait suback\r\n");
				                 }
				                 TIM_Cmd(TIM2, ENABLE);
				                 printf("send sub\r\n");
				 
				             }
				 #if 1
				             else {
				                 //count++;
				                 // Delay_s(2);
				                 if (count>10000) {
				                     count = 0;
				                     make_ping_msg(msgbuf,sizeof(msgbuf));
				                     send(0,msgbuf,sizeof(msgbuf));
				 
				                     while ((len=getSn_RX_RSR(0))==0) {
				                         //Delay_s(2);
				                         //send(0,msgbuf,sizeof(msgbuf));
				                         printf("wait pingresponse");
				 
				                     };
				                     recv(0,msgbuf,len);
				                     printf("ping len : %d\r\n",len);
				                     if (len>2) {
				                         if (PUBLISH==mqtt_decode_msg(msgbuf+2)) {
				                             printf("publish\r\n");
				                             MQTTDeserialize_publish(&dup, &qos, 
				                                                     &retained, 
				                                                     &mssageid, 
				                                                     &receivedTopic,
				                                                     &payload_in, 
				                                                     &payloadlen_in, 
				                                                     msgbuf+2, len-2);
				                             // printf("message arrived %d: %s\n\r", 
				                                       payloadlen_in, payload_in);
				                             memset(topic,0,sizeof(topic));
				                             memset(ser_cmd,0,sizeof(ser_cmd));
				                             memcpy(topic,receivedTopic.lenstring.data,
				                                    receivedTopic.lenstring.len);
				                             replace_string(new_topic,topic , "request",
				                                            "response");
				                             printf("topic:%s\r\n",topic);
				                             strcpy(ser_cmd,(const char *)payload_in);
				                             //parse_topic(ser_cmd);
				                             // printf("message is %s\r\n",ser_cmd);
				                             memset(msgbuf,0,sizeof(msgbuf));
				                             make_pub_msg(new_topic,msgbuf,sizeof(
				                                          msgbuf),"hello");
				                             send(0,msgbuf,sizeof(msgbuf));
				                         }
				                     }
				                 }
				             }
				 #endif
				 #if 0
				             if (PUB_FLAG==1) {
				                 memset(msgbuf,0,sizeof(msgbuf));
				                 // make_sub_msg(topic,msgbuf,sizeof(msgbuf));
				                 make_pub_msg(topic,msgbuf,sizeof(msgbuf),"hello");
				                 if (count == 10000) {
				 PUB:
				                     send(0,msgbuf,sizeof(msgbuf));  // 
				                          接收到数据后再回给服务器,完成数据回环
				                          ÷£¬Íê³ÉÊý¾Ý»Ø»•
				 
				                     Delay_s(2);
				                     // while((len=getSn_RX_RSR(0))==0)
				                     //  {
				                     // Delay_s(2);
				                     //send(0,msgbuf,sizeof(msgbuf));
				                     //    printf("puback\r\n");
				                     //  };
				                     // recv(0,msgbuf,len);
				                     //  if(mqtt_decode_msg(msgbuf)!=PUBACK)
				                            //ÅжÏÊDz»ÊÇSUBACK
				                     //  {
				                     //      goto PUB;
				                     //      printf("wait Puback\r\n");
				                     //  }
				                     printf("send Pub\r\n");
				                 }
				             }
				 #endif
				         }
				 #if 1
				         if ((len=getSn_RX_RSR(0))>0) {
				             recv(0,msgbuf,len);
				             if (PUBLISH== mqtt_decode_msg(msgbuf)) {
				                 printf("publish\r\n");
				                 MQTTDeserialize_publish(&dup, &qos, &retained, 
				                                         &mssageid, &receivedTopic,
				                                         &payload_in, &payloadlen_in, 
				                                         msgbuf, len);
				                 // printf("message arrived %d: %s\n\r", payloadlen_in, 
				                           payload_in);
				                 memset(topic,0,sizeof(topic));
				                 memcpy(topic,receivedTopic.lenstring.data,
				                        receivedTopic.lenstring.len);
				                 replace_string(new_topic,topic , "request","response");
				 
				                 printf("topic:%s\r\n",topic);
				 
				                 memset(ser_cmd,0,sizeof(ser_cmd));
				                 memcpy(ser_cmd,(const char *)payload_in,strlen((char*)
				                        payload_in));
				                 memset(msgbuf,0,sizeof(msgbuf));
				                 make_pub_msg(new_topic,msgbuf,sizeof(msgbuf),rebuf);
				                 send(0,msgbuf,sizeof(msgbuf));
				                 //printf("%s\n",msgbuf);
				             } else if (PINGRESP== mqtt_decode_msg(msgbuf)) {
				                 if (len>2) {
				                     if (PUBLISH==mqtt_decode_msg(msgbuf+2)) {
				                         printf("publish\r\n");
				                         MQTTDeserialize_publish(&dup, &qos, &retained, 
				                                                 &mssageid, 
				                                                 &receivedTopic,
				                                                 &payload_in, 
				                                                 &payloadlen_in, msgbuf+
				                                                 2, len-2);
				                         // printf("message arrived %d: %s\n\r", 
				                                   payloadlen_in, payload_in);
				                         memset(topic,0,sizeof(topic));
				                         memcpy(topic,receivedTopic.lenstring.data,
				                                receivedTopic.lenstring.len);
				                         replace_string(new_topic,topic,"request",
				                                        "response");
				                         printf("topic:%s\r\n",topic);
				                         memset(ser_cmd,0,sizeof(ser_cmd));
				                         strcpy(ser_cmd,(const char *)payload_in);
				                         // printf("message is %s\r\n",ser_cmd);
				                         //parse_topic(ser_cmd);
				                         memset(msgbuf,0,sizeof(msgbuf));
				                         make_pub_msg(new_topic,msgbuf,sizeof(msgbuf),
				                                      "hello");
				                         send(0,msgbuf,sizeof(msgbuf));
				                     }
				                 }
				 
			             } else {
				                 printf("wait publish\r\n");
				             }
				         }
				         //  printf("send ping\r\n");
				 
				 #endif
				         break;
				     case SOCK_CLOSE_WAIT:                       
				     //Socket处于等待关闭状态
				         close(0);                               // 关闭Socket0
				         break;
				     case SOCK_CLOSED:                           
				    // Socket处于关闭状态
				         socket(0,Sn_MR_TCP,local_port,Sn_MR_ND);
				               // 打开Socket0,并配置为TCP无延时模式,打开一个本地端口
				             
				         break;
				     }
				 }
				 

Password有两种获得方法:

通过网页“在线加密解密”HamcSHA1获得;(http://encode.chahuo.com/),通过hmacsha1算法解析获得解析步骤如下:

				   void hmac_sha1(uint8_t *key, uint16_t key_length, uint8_t *data, 
                		uint16_t data_length, uint8_t *digest)
					  {
					      uint8_t b = 64;                               /* blocksize */
					      uint8_t ipad = 0x36;
					      uint8_t opad = 0x5c;
					      uint8_t k0[64];
					      uint8_t k0xorIpad[64];
					      uint8_t step7data[64];
					     uint8_t step5data[MAX_MESSAGE_LENGTH+128];
					     uint8_t step8data[64+20];
					     uint16_t i;
					 
					     for (i=0; i<64; i++) {
					         k0[i] = 0x00;
					     }
					 
					     /* Step 1 */
					     if (key_length != b) {                          
					         //判断秘钥K字节长度是否等于B
					         /* Step 2 */
					         if (key_length > b) {                       
					             //如果大于B,则另K0=H(K)
					             sha1(key, key_length, digest);
					             for (i=0; i<20; i++) {
					                 k0[i]=digest[i];
					             }
					         }
					         /* Step 3 */
					         else if (key_length < b) {                  
					                  //如果小于B,则在末尾添加B-length(K)
					                  位的0
					             for (i=0; i < key_length; i++) {
					                 k0 [i] =  key [i];
					             }
					         }
					     } else {
					         for (i=0; i < b; i++) {
					             k0[i] = key[i];
					         }
					     }
					 
					 
					 #ifdef HMAC_DEBUG
					     debug_out("k0",k0,64);
					 #endif
					 
					     /* Step 4 */
					     for (i=0; i<64; i++) {
					         k0xorIpad[i] = k0[i] ^ ipad;                
					                        //将K0和ipad进行异或运算
					     }
					 
					 #ifdef HMAC_DEBUG
					     debug_out("k0 xor ipad",k0xorIpad,64);
					 #endif
					 
					     /* Step 5 */
					     for (i=0; i<64; i++) {
					         step5data[i] = k0xorIpad[i];
					     }
					     for (i=0; i < data_length; i++) {
					         step5data[i+64] = data[i];                
					                           //将数据添加在第4步生成的字节串
					                           后面
					     }
					 
					 
					 #ifdef HMAC_DEBUG
					     debug_out("(k0 xor ipad) || text",step5data,data_length+64);
					 #endif
					 
					 
					     /* Step 6 */
					     sha1(step5data, data_length+b, digest);       
					          //将第5步的结果运用H函数
					 
					 #ifdef HMAC_DEBUG
					     debug_out("Hash((k0 xor ipad) || text)",digest,20);
					 #endif
					 
					     /* Step 7 */
					     for (i=0; i<64; i++) {
					         step7data[i] = k0[i] ^ opad;              
					                        //将K0和opad进行异或运算
					     }
					 
					 #ifdef HMAC_DEBUG
					     debug_out("(k0 xor opad)",step7data,64);
					 #endif
					 
					     /* Step 8 */
					     for (i=0; i<64; i++) {
					         step8data[i] = step7data[i];
					     }
					     for (i=0; i<20; i++) {
					         step8data[i+64] = digest[i];              
					                           //将第6步的结果加在第7步生成的��
					                           �节串上
					     }
					 
					 #ifdef HMAC_DEBUG
					     debug_out("(k0 xor opad) || Hash((k0 xor ipad) || text)",step8data,
					               20+64);
					 #endif
					 
					     /* Step 9 */
					     sha1(step8data, b+20, digest);
					 
					 #ifdef HMAC_DEBUG
					     debug_out("HASH((k0 xor opad) || Hash((k0 xor ipad) || text))",
					               digest,20);
					 #endif
					 }
			

3) 配置远程服务器IP地址和服务器端口

通过域名解析获取IP地址有两种方法:a、通过在终端下ping域名的方法获取IP地址;b、通过DNS域名解析的方法获取IP地址。

a) 通过在终端下ping域名的方法获取IP地址


把 ${productKey}替换为您的产品key,并在终端对MQTT进行ping操作,来获取服务器IP地址


b) 通过DNS域名解析的方法获取IP地址

首先完成W5500的DNS域名解析例程的移植,把DNS相关部分移植到本程序中,再进行相关配置即可完成(DNS相关例程下载地址http://www.w5500.com/) ,DNS解析域名成功后,把解析出的IP地址赋值给MQTT的 server_ip,用于MQTT与阿里云的连接,完成MQTT协议通信 连接成功后,通过串口调试助手验证DNS域名解析是否正确,若正确则MQTT与阿里云连接成功,并可成功的发布订阅消息:


4) 设置发布订阅的主题:

在tcp_client.c文件中设置MQTT与阿里云连接参数,并通过调用mqtt_fun.c文件中的相关底层函数来完成MQTT与阿里云连接:


底层的订阅发布函数

							 /*****************拼接订阅报文**************************************/
			  void make_sub_msg(char *Topic,unsigned char*msgbuf,int buflen)
			  {
			      int msgid = 1;
			      int req_qos = 0;
			      unsigned char topic[100];
			      MQTTString topicString= MQTTString_initializer;
			      memcpy(topic,Topic,strlen(Topic));
			     topicString.cstring = (char*)topic;
			     //topicString.lenstring.len=4;
			     MQTTSerialize_subscribe(msgbuf, buflen, 0, msgid, 1, &topicString, 
			                             &req_qos);
			     return;
			 }
			 /*********拼接发布报文******************/
			 void make_pub_msg(char *Topic,unsigned char*msgbuf,int buflen,char*msg)
			 {
			     unsigned char topic[100];
			     int msglen = strlen(msg);
			     MQTTString topicString = MQTTString_initializer;
			     memset(topic,0,sizeof(topic));
			     memcpy(topic,Topic,strlen(Topic));
			     topicString.cstring = (char*)topic;
			     MQTTSerialize_publish(msgbuf, buflen, 0, 2, 0, 0, topicString, (
			                           unsigned char*)msg, msglen);
			     return;
			 }
			

此发布订阅的主题根据阿里云中设备管理的Topic列表设置


设备可以基于Topic列表中的Topic进行Pub/Sub通信,例如列表中有/TKKMt4nMF8U/MQTT1/mqtt,且设备拥有的权限是发布和订阅,这就意味着设备可以往这个Topic发布消息,同样设备可以从这个Topic订阅消息。


4、 简单测试:

把程序下载到测试板并连接,登陆阿里云,到添加的设备,开启测试板,状态显示在线,说明MQTT与阿里云已经初步连接上


通过设备的Topic列表,选择程序中设置的发布订阅的Topic进行发布消息的操作:


串口打印接收到的服务器端发送的消息:


同时可在日志服务中查询相关设备的相关消息:

此时MQTT协议通信成功。说明:在串口通信中会一直打印消息,是因为程序中设置了对MQTT的ping操作,防止MQTT离线。


注意

  1. 在MQTT与阿里云连接时,会出现离线的状态,在离线状态时重启测试板并手动刷新阿里云即可。因为状态不是实时的显示,会有一段时间的延迟,可耐心等待。
  2. MQTT CONNECT协议设置时的注意事项:

  3. 错误码





例程下载:【STM32+W5500_MQTT连阿里云_移植说明】
编译环境:keil V5.11
硬件要求:W5500EVB开发板