上海皕科电子有限公司

Shanghai Bitconn Electronic Co.,Ltd.

公司介绍
上海皕科电子有限公司是一家专业的芯片代理商和方案提供商。总部设在上海,是一家专业为中国电子企业提供单片机,无线RF,以太网IC及外围器件、开发工具和相关应用解决方案的高新技术企业。公司的主要代理品牌有Digi、Wiznet、Xinnova,以及华虹MCU等。
  公司拥有优秀的销售团队和专业的研发部门,不但在品牌、价格、供货、服务等方面领先业界,而且可为客户提供及时、可行的技术支持和整体设计服务,满足不同客户多层次需求。

W5500通过MQTT连接阿里云平台

发表时间:2019-5-4  浏览次数:96  
字体大小: 【小】 【中】 【大】

1、简介

1.1 开发环境与连接平台

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

软件环境:Windows

  • 硬件环境:STM32F103+W5500
  • 开发工具:Keil uVision5
  • 调试工具:Wireshark、串口调试助手
  • 连接平台:阿里云-华东2节点()
  • 1.2 MQTT简介:

MQTT官网地址:()

  • 1.2.1 MQTT协议特点

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

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

MQTT协议运行在TCP/IP或其他网络协议,提供有序、无损、双向连接。其特点包括:

  1. 使用的发布/订阅消息模式,它提供了一对多消息分发,以实现与应用程序的解耦。
  2. 对负载内容屏蔽的消息传输机制。
  3. 对传输消息有三种服务质量(QoS):
  • 最多一次,这一级别会发生消息丢失或重复,消息发布依赖于底层TCP/IP网络。即:<=1
  • 至多一次,这一级别会确保消息到达,但消息可能会重复。即:>=1
  • 只有一次,确保消息只有一次到达。即:=1。在一些要求比较严格的计费系统中,可以使用此级别

数据传输和协议交换的最小化(协议头部只有2字节),以减少网络流量

通知机制,异常中断时通知传输双方

  • MQTT协议原理及实现方式

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

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

MQTT传输的消息分为:主题(Topic)和消息的内容(payload)两部分

Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)

payload,可以理解为消息的内容,是指订阅者具体要使用的内容

  • 连接
  1. 阿里云连接步骤:
  • 以aliyun账号直接进入,如果还没有开通阿里云物联网套件服务,则 申请开通
  • 接入引导

(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配置》部分

  1. MQTT移植步骤:

MQTT代码源码下载地址:()

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配置
  • MQTT连接参数说明

举例:

  • MQTT与阿里云连接函数:

参考阿里云内MQTT设备接入手册,计算出设备连接的各项参数,例如下列程序中框中的部分为本例程MQTT与阿里云连接的参数的配置,详细内容如下:

clientId = 192.168.207.115

deviceName = MQTT1

productKey = TKKMt4nMF8U

timestamp = 789(毫秒值)

signmethod = hmacsha1(算法类型)

deviceSecret = secret

那么使用tcp方式提交给mqtt参数分别如下:

  • mqttClientId:clientId+"|securemode=3,signmethod=hmacsha1,timestamp=789|"
  • clientId=192.168.207.115|securemode=3,signmethod=hmacsha1,timestamp=789|
  • keepalive时间需要设置超过60秒以上,否则会拒绝连接。
  • Cleansession为1;
  • mqttUsername: deviceName+"&"+productKey
  • username = "MQTT1&TKKMt4nMF8U"
  • password=hmacsha1("secret","clientId168.207.115deviceNameMQTT1productKeyTKKMt4nMF8Utimestamp789").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连接过程:
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服务器发出连接请求