logo资料库

STM32_W5500_MQTT_移植说明.pdf

第1页 / 共17页
第2页 / 共17页
第3页 / 共17页
第4页 / 共17页
第5页 / 共17页
第6页 / 共17页
第7页 / 共17页
第8页 / 共17页
资料共17页,剩余部分请下载后查看
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));
分享到:
收藏