W5500 如何通过  MQTT 协议连接阿里云 
一、 简介: 
1、  开发环境与连接平台: 
本文主要介绍 W5500 如何通过 MQTT 协议将设备连接到阿里云 IoT,并通过 MQTT 协议实
现通信。MQTT 协议是基于 TCP 的协议,所以我们只需要在单片机端实现 TCP 客户端代码之
后就很容易移植 MQTT 了,  +W5500 实现 TCP 客户端的代码我们以前已经实现过,程序下载
地址为(http://www.w5500.com/) 
软件环境:Windows 
硬件环境:STM32F103+W5500 
开发工具:Keil uVision5 
调试工具:Wireshark、串口调试助手 
连接平台:阿里云-华东 2 节点(https://www.aliyun.com) 
2、  MQTT 简介: 
MQTT 官网地址:(http://mqtt.org/) 
1)  MQTT 协议特点 
MQTT 是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT 协议是轻量、简单、
开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,
如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号
的医疗设备、智能家居、及一些小型化设备中已广泛使用。 
MQTT 协议当前版本为,2014 年发布的 MQTT v3.1.1。除标准版外,还有一个简化版 MQTT-
SN,该协议主要针对嵌入式设备,这些设备一般工作于百 TCP/IP 网络,如:ZigBee。 
MQTT 协议运行在 TCP/IP 或其他网络协议,提供有序、无损、双向连接。其特点包括: 
使用的发布/订阅消息模式,它提供了一对多消息分发,以实现与应用程序的解耦。 
对负载内容屏蔽的消息传输机制。 
对传输消息有三种服务质量(QoS):  
  最多一次,这一级别会发生消息丢失或重复,消息发布依赖于底层 TCP/IP 网络。
即:<=1 
  至多一次,这一级别会确保消息到达,但消息可能会重复。即:>=1 
  只有一次,确保消息只有一次到达。即:=1。在一些要求比较严格的计费系统中,
可以使用此级别 
数据传输和协议交换的最小化(协议头部只有 2 字节),以减少网络流量 
通知机制,异常中断时通知传输双方 
2)  MQTT 协议原理及实现方式 
实现 MQTT 协议需要:客户端和服务器端 
MQTT 协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者
(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者
可以同时是订阅者。 
MQTT 传输的消息分为:主题(Topic)和消息的内容(payload)两部分  
Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息
内容(payload) 
payload,可以理解为消息的内容,是指订阅者具体要使用的内容 
二、 连接 
1.  阿里云连接步骤: 
1)  以 aliyun 账号直接进入 IoT 控制台,如果还没有开通阿里云物联网套件服务,则
  申请开通 
2)  接入引导 
(1)、创建产品 
(2)、添加设备 
(3)、获取设备的 Topic 
  创建产品 
初步进入控制台后,需要创建产品。点击创建产品。产品相当于某一类设备的集合,用
户可以根据产品管理其设备等。 
  产品名称:对产品命名,例如可以填写产品型号。产品名称在账号内保持唯一。 
  productKey:阿里云 IoT 为产品颁发的全局唯一标识符 
 
  添加设备: 
创建完产品之后,可以为该产品添加设备。进入产品管理页面下的设备管理,点击添加
设备。 
  说明:用户可以自定义设备名称(即 deviceName),这个名称即可作为设备唯一标
识符,用户可以基于该设备名称与 IoT Hub 进行通信,需要指出的是,用户需要保
证 deviceName 产品内唯一。 
 
 
  设备证书:添加设备之后,物联网套件为设备颁发的唯一标识符,设备证书用于设
备认证以及设备通信,详细的请参考设备接入文档。 
  deviceName:用户自定义设备唯一标识符,用于设备认证以及设备通信,用户保证
产品维度内唯一。 
  deviceSecret:物联网套件为设备颁发的设备秘钥,用于认证加密,与 deviceName
或者 deviceId 成对出现。 
  获取设备的 Topic 
添加设备之后,可以获取设备的 Topic。点击 Topic 列表 
  说明:创建产品之后,物联网套件都会为产品默认定义三个 Topic 类。那么,在添
加设备之后,每个设备都会默认有三个 Topic,即图中所示。如果想要增加、修改、
删除 Topic,请到消息通信重新定义 Topic 类。 
  设 备 可 以 基 于 Topic 列 表 中 的 Topic 进 行 Pub/Sub 通 信 , 例 如 列 表 中 有
/1000118502/test9/update,且设备拥有的权限是发布,这就意味着设备可以往这
个 Topic 发布消息;同样,列表中/1000118502/test9/get,权限是订阅,这就意味
着设备可以从这个 Topic 订阅消息。 
 
 
 
  设备接入 
获得 productKey、设备证书以及设备的 Topic 这些参数,就可以基于 aliyun IoT device 
SDK for C 将设备连接上 IoT Hub 并进行通信,具体请参考《MQTT 配置》部分 
 
2.  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 客户端的程序,那么这几个函数的封装也是非常
简单的,程序代码如下所示: 
 
1 /** 
 2 * @brief  通过 TCP 方式发送数据到 TCP 服务器 
 3 * @param  buf 数据首地址 
 4 * @param  buflen 数据长度 
 5 * @retval 小于 0 表示发送失败 
 6 */ 
 7  
 8 /*订阅消息*/ 
9 int Subscribe_sendPacketBuffer(unsigned char* buf, int buflen) 
10 { 
11     return send(SOCK_TCPS,buf,buflen); 
12 } 
13  
14 /*发布消息*/ 
15 int Published_sendPacketBuffer(unsigned char* buf, int buflen) 
16 { 
17     return send(SOCK_TCPC,buf,buflen); 
18 } 
19   
20 /** 
21 * @brief  阻塞方式接收 TCP 服务器发送的数据 
22 * @param  buf 数据存储首地址· 
23 * @param  count 数据缓冲区长度 
24 * @retval 小于 0 表示接收数据失败 
25 */ 
26 int Subscribe_getdata(unsigned char* buf, int count) 
27 { 
28  
29     return recv(SOCK_TCPS,buf,count); 
30  
31 } 
32  
33 int Published_getdata(unsigned char* buf, int count) 
34 { 
35     return recv(SOCK_TCPC,buf,count); 
36  
37 } 
38  
39 /** 
40 * @brief  打开一个 socket 并连接到服务器 
41 * @param  无 
42 * @retval 小于 0 表示打开失败 
43 */ 
44 int Subscribe_open(void) 
45 { 
46     int32_t ret; 
47     //新建一个 socket 并绑定本地端口 5000 
48 ret = socket(SOCK_TCPS,Sn_MR_TCP,50000,0x00); 
49     if (ret != 1) { 
50         printf("%d:Socket Error\r\n",SOCK_TCPS); 
51         while (1); 
52     } else { 
53         printf("%d:Opened\r\n",SOCK_TCPS); 
54     } 
55  
56  
57     while (getSn_SR(SOCK_TCPS)!=SOCK_ESTABLISHED) { 
58         printf("connecting\r\n"); 
60       //连接 TCP 服务器÷ 
61      ret = connect(SOCK_TCPS,server_ip,1883); 
62            //端口必须为 1883 
63   } 
63 if (ret != 1) { 
64         printf("%d:Socket Connect Error\r\n",SOCK_TCPS); 
65         while (1); 
66     } else { 
67         printf("%d:Connected\r\n",SOCK_TCPS); 
68     } 
69     return 0; 
70 } 
71  
72 int Published_open(void) 
73 { 
74     int32_t ret; 
75  
76     ret = socket(SOCK_TCPC,Sn_MR_TCP,5001,0x00); 
77  
78     if (ret != 1) { 
79         printf("%d:Socket1 Error1\r\n",SOCK_TCPC); 
80         while (1); 
81     } else { 
82         printf("%d:socket1 Opened\r\n",SOCK_TCPC); 
83     } 
84  
85  
86     while (getSn_SR(SOCK_TCPC)!=SOCK_ESTABLISHED) { 
87         ret = connect(SOCK_TCPC,server_ip,1883); 
88            //端口必须为 1883 
89 } 
90     if (ret != 1) { 
91         printf("%d:Socket Connect1 Error\r\n",SOCK_TCPC); 
92         while (1); 
93     } else { 
94         printf("%d:Connected1\r\n",SOCK_TCPC); 
95     } 
96     return 0; 
97 } 
98  
99 } 
100  
101 /** 
102 * @brief  关闭 socket 
103 * @param  无 
104 * @retval 小于 0 表示关闭失败 
105 */ 
106 int Subscribe_close(void) 
107 { 
108     disconnect(SOCK_TCPS); 
109     printf("close0\n\r"); 
110  
111     while (getSn_SR(SOCK_TCPC)!=SOCK_CLOSED) { 
112         ; 
113     } 
114     return 0; 
115 } 
116  
117  
118 int Published_close(void) 
119 { 
120     disconnect(SOCK_TCPC); 
121     printf("close1\n\r"); 
122  
123     while (getSn_SR(SOCK_TCPC)!=SOCK_CLOSED) { 
124         ; 
125     } 
126     return 0; 
127 } 
3、 
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.115deviceNameMQTT1productKe
yTKKMt4nMF8Utimestamp789").toHexString();  
最 后 是 二 进 制 转 16 制 字 符 串 大 小 写 不 敏 感 。 这 个 例 子 结 果 为 
9076b0ebc04dba8a8ebba1f0003552dbc862c9b9 
 
 
MQTT 连接函数原型,tcp_client.c 文件中的 MQTT_CON_ALI 函数中调用 make_con_msg
函数并通过阿里云设备的参数,设置 MQTT 连接阿里云函数的参数: 
 
 
 
1 void make_con_msg(char* clientID,int keepalive, uint8 cleansession, 
 2                   char*username,char* password,unsigned char*buf,int  
 3                   buflen) 
 4 { 
 5     int32_t len,rc; 
 6     MQTTPacket_connectData data = MQTTPacket_connectData_initializer; 
 7     data.clientID.cstring = clientID; 
 8     data.keepAliveInterval = keepalive; 
 9     data.cleansession = cleansession; 
10     data.username.cstring = username; 
11     data.password.cstring = password; 
12     len = MQTTSerialize_connect(buf, buflen, &data); 
13           //构造链接报文 
 14     return; 
15 } 
 
 
MQTT 连接过程: 
1 void MQTT_CON_ALI(void) 
 2 { 
 3     int len; 
 4     int type; 
 5     switch (getSn_SR(0)) {                   
 6             //获取 socket0 的状态 
 7     case SOCK_INIT:                       
 8     //Socket 处于初始化完成(打开)状态 
 9         connect(0, server_ip,server_port);     
10                 //配置 Sn_CR 为 CONNECT,并向 TCP 服务器发出连接请求¢ 
11    
12  
13         break; 
14     case SOCK_ESTABLISHED:                //  
15     Socket 处于连接建立状态 
16         if (getSn_IR(0) & Sn_IR_CON) { 
17             setSn_IR(0, Sn_IR_CON);       //  
18                      Sn_IR 的 CON 位置 1,通知 W5500 连接已建立 
19               
20         } 
21         memset(msgbuf,0,sizeof(msgbuf)); 
22         if ((len=getSn_RX_RSR(0))==0) { 
23             if (1==CONNECT_FLAG) { 
24                 printf("send connect\r\n"); 
25  
26                 /*MQTTÆ 拼接连接报文 
27                 *根据阿里云平台 MQTT 设备接入手册配置 
28            
29                 */ 
30  
31                 //void make_con_msg(char* clientID,int keepalive,  
32                                     uint8 cleansession,char*username, 
33                                     char* password,unsigned char*buf, 
34                                     int buflen) 
35                 make_con_msg("192.168.207.115|securemode=3, 
36                              signmethod=hmacsha1,timestamp=789|",180,  
37                              1,"MQTT1&TKKMt4nMF8U", 
38                              "9076b0ebc04dba8a8ebba1f0003552dbc862c9b9" 
39                              ,msgbuf,sizeof(msgbuf)); 
40  
41  
42                 //printf(" server_ip: %d.%d.%d.%d\r\n", server_ip[0], 
43                          server_ip[1],server_ip[2],server_ip[3]); 
44                 //printf("connect ALY\r\n"); 
45                 CONNECT_FLAG = 0; 
46                 send(0,msgbuf,sizeof(msgbuf)); 
47                 Delay_s(2); 
48                 while ((len=getSn_RX_RSR(0))==0) { 
49                     Delay_s(2); 
50                     send(0,msgbuf,sizeof(msgbuf)); 
51                 }; 
52                 recv(0,msgbuf,len); 
53                 while (mqtt_decode_msg(msgbuf)!=CONNACK) {  
54                        //判断是不是 CONNACK 
55                     printf("wait ack\r\n"); 
56                 } 
57             } else if (SUB_FLAG == 1) { 
58                 memset(msgbuf,0,sizeof(msgbuf)); 
59                 make_sub_msg(topic,msgbuf,sizeof(msgbuf)); 
60                 // make_pub_msg(topic,msgbuf,sizeof(msgbuf),"hello"); 
61                 send(0,msgbuf,sizeof(msgbuf));  
62                     // 接收到数据后再回给服务器,完成数据回环 
63                       
64                 SUB_FLAG = 0; 
65                 Delay_s(2); 
66                 while ((len=getSn_RX_RSR(0))==0) { 
67                     Delay_s(2); 
68                     send(0,msgbuf,sizeof(msgbuf)); 
69                 }; 
70                 recv(0,msgbuf,len); 
71                 while (mqtt_decode_msg(msgbuf)!=SUBACK) {  
72                        //判断是不是 SUBACK 
73                     printf("wait suback\r\n"); 
74                 } 
75                 TIM_Cmd(TIM2, ENABLE); 
76                 printf("send sub\r\n"); 
77  
78             } 
79 #if 1 
80             else { 
81                 //count++; 
82                 // Delay_s(2); 
83                 if (count>10000) { 
84                     count = 0; 
85                     make_ping_msg(msgbuf,sizeof(msgbuf));