---
title: "Porting MQTT protocol to STM32 and W5500, Ali Cloud"
url: "https://maker.wiznet.io/gemma/projects/porting-mqtt-protocol-to-stm32-and-w5500%2C-ali-cloud/"
markdown_url: "https://maker.wiznet.io/gemma/projects/porting-mqtt-protocol-to-stm32-and-w5500%2C-ali-cloud/md"
type: "UCC: User Created Content"
author: "乌冬Nymo"
author_url: "https://blog.csdn.net/qq_41655062/article/details/105181398"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "乌冬Nymo"
original_url: "https://blog.csdn.net/qq_41655062/article/details/105181398"
published: "2022-08-30"
language: "en"
hardware: ["WIZnet W5500"]
likes: 4
views: 1008
comments: 1
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# Porting MQTT protocol to STM32 and W5500, Ali Cloud

> STM32上移植MQTT协议使用W5500以太网芯片连接阿 The MQTT protocol is transplanted on the STM32 and the W5500 Ethernet chip is used to connect to Alibaba Cloud

Original author: 乌冬Nymo (source: https://blog.csdn.net/qq_41655062/article/details/105181398)

## Components

- **WIZnet W5500** x 1 ([docs](https://docs.wiznet.io/Product/Chip/Ethernet/W5500))
- Software: **Eclipse Paho MQTT** ([docs](https://www.eclipse.org/paho/))

WIZnet parts: W5500 ([Datasheet](https://docs.wiznet.io/Product/Chip/Ethernet/W5500/datasheet?utm_source=maker&utm_medium=project&utm_campaign=w5500), [product hub](https://maker.wiznet.io/products/w5500/))

## Article

## STM32上移植MQTT协议使用W5500以太网芯片连接阿里云

**前言**
有机会做这次项目很开心，学到了很多东西，但刚开始的时候是真的从零开始，网上找的资料都大相径庭，很多代码都有问题，而且把别人做的模块一个个移植实在是很麻烦，为了不让后来需要做类似这个物联网项目的大家走歪路，我尽可能的把我这次做项目的核心事务说清楚，个人能力有限，有错的地方麻烦轻喷和指教，谢谢各位。

**正题**
首先先把要做什么搞清楚，顾名思义，本次项目内容即：
①把MQTT协议移植到stm32上；
②通过W5500以太网芯片，用网线连接至本地路由；
③由stm32本地网口连接到阿里云

使用到的工具：Keil5、pahoMqtt包、stm32f103RCT6、W5500、网线*1
调试工具：485通信usart口串口通信、串口调试助手、通信猫、阿里云、MQTTfx、MQTTBOX（当然如果不使用串口，单步Debug也是可以，我当时就这样，搞的我快崩溃了，最后还是弄了串口方便很多，也很简单）

学习资料:这边安利和推广一下B站上一个叫橙子的up主，他做的视频挺好的，很适合萌新上手，视频看看能明白很多东西；
友情链接：stm32 mqtt移植
如果有违权的地方麻烦友情提醒一下，方便我删除。

进入正题，MQTT协议，硬件部分不多说。
首先是下载Mqtt包，可以上github上下载，很多其他mqtt移植的博文都有链接，不过这里也贴一下；
友情链接：PahoMqtt-Github下载
下载后解压，把\paho.mqtt.embedded-c-master\MQTTPacket\src下的所有源文件导入到你的工程项目中，然后导入src所有的头文件路径，最后再把/sample下的transport.c、.h也导入；
这样首先第一步移植就成功了，很多人说transport.c里面有很多函数是Linux下编译的，所以用不了，那必然是用不了的2333，我们要做的事情就是重写transport.c和transport.h文件里的函数。主要有

transport_sendPacketBuffer //发送数据包到服务器
transport_getdata //从服务器获取数据
transport_open // 创建socket，绑定、连接等
transport_close // 关闭socket
这四个函数；
后续要Connect、Pub、Sub、Disconnect，都是基于这四个函数，以及一些MQTT_Package里头自带的一些不需要你写的函数；
最最重要的是搞清楚MQTT协议的工作方式，才能把功能实现，无论你是用一些常用的比如SIM800C、8266、Lora、W5500之类的通信途径，都是大同小异的；
工作方式：具体的几个名词不细讲。本地作为客户端连接到服务器，服务器作为Broker，你可以向Broker订阅话题，也可以向Broker发布话题，同样Broker作为服务器也可以对你Pub和Sub，宏观来看，是对等的；
首先你要有服务器的域名或者IP，只有域名就得做DNS解析，有IP直接IP连，端口号视你要连的服务器规定，阿里云就是固定的1883，通信猫18831之类的，在连接阿里云之前，建议把连接通信猫作为实验调通了作为练手；
连接时，有以下几个参数：
data.clientID.cstring // 客户端ID
data.keepAliveInterval //心跳时长，所谓心跳就是等待时间，阿里云规定要//大于60，否则拒绝连接
data.cleansession// 清除位，默认1
data.username.cstring//用户名
data.password.cstring//密码
data.MQTTVersion //规定版本号，比如Viersion4，连接阿里云，透传
关于Qos不细说；
连接阿里云的具体过程：发出连接报文>收到CONNACK(云确认到你的连接的回执）>订阅>收到SUBACK，返回Qos>不断Read做轮询，该干什么干什么，比如Pub、心跳以及等待云端Pub；
阿里云规定Pub和Sub的报文格式为Json格式，因此在Pub和在Sub解析的时候都要注意Json格式是否符合，网上有很多在线检测
很重要的一点，自带的Socket库里头的read都是阻塞的，如果你需要同时（近似）Pub和Sub，需修改成非阻塞的；

核心代码
下面贴上核心代码。
————————————————
版权声明：本文为CSDN博主「乌冬Nymo」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
原文链接：https://blog.csdn.net/qq_41655062/article/details/105181398

```plaintext
/**
*@Todo: Send data to the server via tcp
*/
int transport_sendPacketBuffer(unsigned char* buf, int buflen)
{
  return send(SOCK_TCPS,buf,buflen);
}

/**
 *@Todo: Receiving data sent by tcp server in blocking mode
*/
int transport_getdata(unsigned char* buf, int count)
{
  return recv(SOCK_TCPS,buf,count);
}

/**
 *@Todo: Open a socket and connect to the server
 *@retval less than means fail
*/
int transport_open(void){
	int32_t ret;
	//New socket
	ret = socket(1, Sn_MR_TCP,2001, 0x00);
	 if(ret != SOCK_TCPS){
    printf("%d:Socket Error\r\n",SOCK_TCPS);
    while(1);
  }else{
    printf("%d:Opened\r\n",SOCK_TCPS);
  }
	 //Connect to TCP server
	ret = connect(SOCK_TCPS,domain_ip,1883); 
	if(ret != SOCK_OK){
    printf("%d:Socket Connect Error\r\n",SOCK_TCPS);
    //while(1);
  }else{
    printf("%d:Connected\r\n",SOCK_TCPS);
  }		
	return 0;
}

/**
 *@Todo: Close Socket
*/
int transport_close(void)
{
  disconnect(SOCK_TCPS);
  close(SOCK_TCPS);
  return 0;
}
/***************************The above are the basic four functions***************** *******/


	while(1){
		timeh++;
			if(connect_flag == 1&&timeh==1000)
		  {  // Json format, data is released according to variables defined in Ailiyun, payload is the payload
				Ua = 56.7 -rand()%10+1;
				Ub = 34.8 -rand()%10+1;
				Uc = 69.2 -rand()%10+1;
	      sprintf((char*)payload_out,"{\"params\":{\"nUa\":+%0.1f,\"Ub\":+%0.1f,\"Uc\":+%0.1f},\"method\":\"thing.event.property.post\"}",Ua,Ub,Uc);
				payload_out_len = strlen((char*)payload_out);
				topicString.cstring = topic_pub_set_info;		//Publish push topic
				len = MQTTSerialize_publish(buf, buflen, 0, req_qos, retained, msgid, topicString, payload_out, payload_out_len);
				rc = transport_sendPacketBuffer(buf, len);
				if(rc == len)															//
					printf("send PUBLISH Successfully\r\n"); // The length of the sent out and the received back are the same, which means the release is ok
		  }
			if(timeh>=1000)
				timeh = 0;
			switch(msgtypes)
		{

			case CONNECT:	len = MQTTSerialize_connect(buf, buflen, &data); 						//Send connection information
							rc = transport_sendPacketBuffer(buf, len);		//Send connection information
							if(rc == len){															//
								printf("send CONNECT Successfully\r\n");
							}
							else{
								msgtypes =  CONNECT; 
								continue;
							}
							break;

			case CONNACK:   if(MQTTDeserialize_connack(&sessionPresent, &connack_rc,buf, buflen))	//Retract the execution
							{
								printf("MQTT is conncet OK!\r\n");									//Receipt of receipt, connection is successful
								connect_flag = 1;
								msgtypes = SUBSCRIBE;													//Subscription operation
							}
							else
							{
								printf("Unable to connect, return code %d\r\n", connack_rc);		//failure
							}
							break;
			case SUBSCRIBE: topicString.cstring = topic_sub_set_info;
							len = MQTTSerialize_subscribe(buf, buflen, 0, msgid, 1, &topicString, &req_qos);
							rc = transport_sendPacketBuffer(buf, len);
							if(rc == len){
								goto tt;
							}
							else
							{
								continue;
							}
							break;
			case SUBACK: 	rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos,buf, buflen);	//Subscription is successful QoS                                                     
							printf("granted qos is %d\r\n", granted_qos);       							 
							if (granted_qos != 0)
								//goto exit;
							printf("Get qos");
	
							break;
			case PUBLISH:	rc = MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic,&payload_in, &payloadlen_in, (unsigned char*)buf, buflen);	//Receive server message
							printf("message arrived : %s\r\n", payload_in);
							json = cJSON_Parse((char *)payload_in);			//Analyze the data packet
							if (!json)  
							{  
								printf("Error before: [%s]\r\n",cJSON_GetErrorPtr());  
							} 
							else
							{
								json_id = cJSON_GetObjectItem(json , "id"); 
								if(json_id->type == cJSON_String)
								{
									printf("id:%s\r\n", json_id->valuestring);  
								}
								json_params = cJSON_GetObjectItem(json , "params");
								if(json_params)  
								{  
									
									if(cJSON_GetObjectItem(json_params, "LEDSwitch"))
									{
                      
									}
									if(cJSON_GetObjectItem(json_params, "p2"))
									{
										
										}  
									}
								} 
							cJSON_Delete(json);   //Free up space
							if(qos == 1)
							{
								printf("publish qos is 1,send publish ack.\r\n");							//
								memset(buf,0,buflen);
								len = MQTTSerialize_ack(buf,buflen,PUBACK,dup,msgid);   					//publish ack                       
								rc = transport_sendPacketBuffer(buf, len);			
								if(rc == len)
									printf("send PUBACK Successfully\r\n");
								else
									printf("send PUBACK failed\r\n");                                       
							}
							break;
			case PUBACK:    printf("PUBACK!\r\n");	
							break;

			case PUBREC:    printf("PUBREC!\r\n");     				//just for qos2
							USART2_sendbyte(12);
							break;
							
			case PUBREL:    printf("PUBREL!\r\n");        			//just for qos2
							break;
			case PUBCOMP:   printf("PUBCOMP!\r\n");        			//just for qos2
							break;
			case PINGREQ:   len = MQTTSerialize_pingreq(buf, buflen);							
							rc = transport_sendPacketBuffer(buf, len);
							break;
			case PINGRESP:	printf("mqtt server Pong\r\n");  			   
 							msgtypes = PINGREQ;
							break;
			default:
							break;
		}
		
		if(msgtypes!=SUBSCRIBE){
  tt:			
			    rc=MQTTPacket_read(buf, buflen, transport_getdata);       	       Polling, corresponding processing according to the received receipt
			    if(rc >0)													//If you receive the correct, existing receipt, then enter the corresponding state
				  {
				  	msgtypes = rc;
				  }else msgtypes=PINGREQ;
		  }

	}
	exit:
	transport_close();
```

MQTT通信部分使用了以上的状态机，其实大部分代码网上都有，但是都不是完全正确，而且根据每个人情况不同，代码的理解也不同。

结论
写着写着感觉文字有点多…不太会写文章，大家有问题还是问吧，如果有看见会回答的，我个人认为这个项目大家没必要去花钱买资料看，网上可以白嫖的很多的，从0开始确实很艰辛…我也是花了几天时间去搞明白，最重要的是要理解，盲目的肝代码莫得用处（对自己说的…）
————————————————
版权声明：本文为CSDN博主「乌冬Nymo」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
原文链接：https://blog.csdn.net/qq_41655062/article/details/105181398

---

Source: https://maker.wiznet.io/gemma/projects/porting-mqtt-protocol-to-stm32-and-w5500%2C-ali-cloud/
