Linkit 7688 DUO(六) 加入MQTT物联网协议

来源:互联网 发布:极简图床的源码 编辑:程序博客网 时间:2024/05/06 10:00

Linkit 系列博文:

联发科Linkit 7688 (一) 上手及在Mac下搭建OpenWrt交叉编译环境,C语言编译Hello,World

联发科Linkit 7688 (二)GPIO基本操作与C语言编程

联发科Linkit 7688 DUO(三): 通过 Arduino 控制外设和传感器

Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇

Linkit 7688 DUO(六) 加入MQTT物联网协议


本篇介绍 将 MQTT物联网协议加载到 Linkit 7688 DUO开发板上 ( 其中的源码和方法也适用于所有的Linux系统、也适合于Windows系统)

MQTT是IBM开发的一种物联网即时通信协议,该协议支持所有平台,几乎可以把所有联网物品、电脑APP、手机APP、网页等连接起来,让它们随时相互交换信息。

有了MQTT协议,Linkit 7688开发板就可以称得上物联网开发板了,可以实现很多场景,比如:手机远程控制、网页远程控制、服务器监测物体状态等等。


一、MQTT协议简介

1,MQTT是一种基于TCP的网络消息协议,用于发送和接收消息。 

在一个MQTT网络中,有一个服务器和多个客户端。 每个客户端以TCP Socket与服务器连接,并保持为长连接。

客户端可以是一个联网物体、电脑软件、手机APP、甚至是网页。如下图:



2,  消息传输采取发布/订阅(publish/subscribe)模式。

  客户端可以发布(publish)消息, 每个消息由一个主题(topic)和一个消息内容组成。比如:某温度传感器发布一个消息: topic = "sensor1/temperature", message="25“

  客户端可以订阅(subscribe)一个或多个主题的消息。 当网络中有人发布了这些主题的消息,则客户端将收到这条消息。比如: 手机App订阅了 “sensor1/temperature"消息,则上述消息将被收到, 手机App就知道sensor1的温度值了。

  由于客户端与服务器保持着Socket长连接,消息将立即实时推送到客户端,也就是说:PUSH到客户端。

  MQTT服务器的主要作用是接收-转发:接收消息、判断哪些客户端订阅了该主题的消息,PUSH给相应客户端。

  这种机制,可以实现一对一发送消息,也可以实现一对多发送消息(群发)。

应用场景举例:

场景1:  手机APP发布查温度的消息, 各个传感器装置收到消息后,发布温度消息。则手机就可以收集到各个传感器的温度。

场景2:  当火警传感器装置检测到起火后,发布消息。 则订阅了该类型消息的手机、电脑等均可立即收到火警。


3,MQTT的消息包传输数据量很小(固定长度的头部仅为 2 字节),能充分降低网络流量,非常适合于低带宽、不可靠连接、嵌入式设备。 

同时非常适合于手机等移动通信环境,可以省流量、省电。因此,有人用MQTT作为手机PUSH使用。


4, 为保障消息有效率到达,MQTT定义了三种消息发布服务质量(Qos, Quality Of Service):

“至多一次”(At most once),开销最小,消息发布完全依赖底层 TCP/IP 网络,会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
“至少一次”(At least once),确保消息到达,但消息重复可能会发生。
“只有一次”(Exactly once),确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。常用于精确控制。

每一条消息都可以有自己的Qos定义。每一个客户端订阅某主题时,也可以指定Qos.


5, MQTT协议由IBM公司于1999年提出,经过多年发展已比较成熟,目前版本是3.1.1。服务器和客户端均有多个开源实现,支持各类操作系统和开发语言。


二、适合嵌入式设备的MQTT协议客户端(C语言实现)

对于Linkit 7688开发板,我选取 “Eclipse Paho C” 的MQTT 客户端开源代码库 。其主页在这:https://www.eclipse.org/paho/clients/c/

这个代码库是IBM公司提供的,Eclipse项目组维护的,可以称为官方的。

我的使用方式是: 将它作为一个函数库, 在Linkit 7688上编写c语言程序,实现MQTT消息收发。

这个库有一点点复杂,需要对协议比较了解。如果你要完全搞懂它,需要先读它的文档(http://www.eclipse.org/paho/files/mqttdoc/Cclient/index.html)

我们一般总是喜欢把复杂的事情简单化,因此,我对这个库进行了封装,  变成几个函数, 不需要懂太多就可以用.

库和例程在我的资源中下载:  MQTT客户端Paho C代码

解压后,其中mqtt目录是Paho C所有的库文件( 原有的文件都没改,我增加了 mqtt_client.c ,  mqtt_client.h 两个文件),使用时将mqtt目录复制到你的项目文件夹中即可。


在使用前,需要有一个测试用的MQTT服务器, 我用的是 IBM提供的测试服务器:  messagesight.demos.ibm.com, 端口是1883 (1883是MQTT的默认端口)

同时需要有一个测试用的MQTT客户端作为对端,我用的是IBM提供的网页版MQTT客户端:http://m2m.demos.ibm.com/mqttclient/


1, 在Linkit 7688上编一个发送MQTT消息的程序:

  用Eclipse 建立一个交叉编译项目 ( 开发环境搭建请见:联发科Linkit 7688 (一) 上手及在Mac下搭建OpenWrt交叉编译环境,C语言编译Hello,World)

  将mqtt目录复制到你的项目文件夹下。

  创建一个 mqtt_publish.c文件, 编写主程序如下:

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <errno.h>  
  4. #include "mqtt/mqtt_client.h"//这个是我对mqtt_client封装后的头文件  
  5.   
  6.   
  7. int publish(int argc, char ** argv) {  
  8.   
  9.     mqtt_client *m; //mqtt_client 对象指针  
  10.     int ret; //返回值  
  11.     char *host = "messagesight.demos.ibm.com:1883";//测试服务器  
  12.     char *topic = "test_topic"//主题  
  13.     char *client_id = "clientid33883";//客户端ID; 对测试服务器,可以随便写  
  14.     char *username = NULL;//用户名,用于验证身份。对测试服务器,无。  
  15.     char *password = NULL;//密码,用于验证身份。对测试服务器,无。  
  16.     int Qos; //Quality of Service  
  17.   
  18.     //create new mqtt client object  
  19.     m = mqtt_new(host, MQTT_PORT, client_id); //创建对象,MQTT_PORT = 1883  
  20.     if ( m == NULL ) {  
  21.         printf("mqtt client create failure, return  code = %d\n", errno);  
  22.         return 1;  
  23.     } else {  
  24.         printf("mqtt client created\n");  
  25.     }  
  26.   
  27.     //connect to server  
  28.     ret = mqtt_connect(m, username, password); //连接服务器  
  29.     if (ret != MQTT_SUCCESS ) {  
  30.         printf("mqtt client connect failure, return code = %d\n", ret);  
  31.         return 1;  
  32.     } else {  
  33.         printf("mqtt client connect\n");  
  34.     }  
  35.   
  36.     //publish message  
  37.     Qos = QOS_EXACTLY_ONCE; //Qos  
  38.     ret = mqtt_publish(m, topic, "hello from Linkit 7688", Qos);//发布消息  
  39.     printf("mqtt client publish,  return code = %d\n", ret);  
  40.   
  41.     mqtt_disconnect(m); //disconnect  
  42.     mqtt_delete(m);  //delete mqtt client object  
  43.     return 0;  
  44. }  


主程序分为三个步骤:

1, 调用 mqtt_new()创建 客户端对象

2, 调用 mqtt_connect() 连接服务器

3, 调用 mqtt_publish() 发布消息


用Eclipse编译项目

如果出现错误提示:

undefined reference to `pthread_create’
undefined reference to `pthread_mutexattr_init’

这是因为没有在编译连接时包含pthread库, 解决办法: 需在GCC中添加 -lpthread 参数。

如果出现错误提示:

undefined reference to `dlclose’
undefined reference to `dlopen’
undefined reference to `dlsym
这是因为没有在编译连接时包含dl库, 解决办法: 需在GCC中添加 -ldl 参数

在eclipse中的操作是:   项目Property =>  C/C++ Build => Settings, 在"Tools Setting"页,选Cross GCC linker, 在 "Linker flags"编译框中填入:  -lpthread -ldl

OK, 编译成功了。


用scp将 mqtt_publish 程序上传到 linkit 7688:   scp mqtt_publish root@mylinkit.local:/root


先准备接收消息的网页客户端: 用浏览器打开 http://m2m.demos.ibm.com/mqttclient/

在Connect栏中, 点Connect.

在Subscribe栏中,将topic设为 test_topic, 按subscribe

OK, 网页客户端准备好接收主题(topic)为 test_topic的消息了。


用SSH登录入Linkit 7688,  运行 mqtt_publish 程序。

运行结果, 显示mqtt 创建、连接、发布消息的过程, return code >=0 表示成功

mqtt client created

mqtt client connect

mqtt client publish,  return code = 1


这时,可以看到浏览器中的MQTT网页客户端收到了Linkit 7688发布的MQTT消息


 

成功了: Linkit 7688发布MQTT消息到服务器, 网页客户端实时收到服务器推送来的消息。



2, 在Linkit 7688上编一个接收MQTT消息的程序:

  用Eclipse 建立一个交叉编译项目 ( 开发环境搭建请见:联发科Linkit 7688 (一) 上手及在Mac下搭建OpenWrt交叉编译环境,C语言编译Hello,World)

  将mqtt目录复制到你的项目文件夹下。

  创建一个 mqtt_subscribe.c文件, 编写主程序如下:

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <errno.h>  
  4. #include <signal.h>  
  5. #include "mqtt/mqtt_client.h"//这个是我对mqtt_client封装后的头文件  
  6.   
  7. int running = 1;  
  8.   
  9. void stop_running(int sig)  
  10. {  
  11.     signal(SIGINT, NULL);  
  12.     running = 0;  
  13. }  
  14.   
  15.   
  16. int main(int argc, char ** argv) {  
  17.   
  18.     mqtt_client *m; //mqtt_client 对象指针  
  19.     int ret; //返回值  
  20.     char *host = "messagesight.demos.ibm.com:1883";//测试服务器  
  21.     char *topic = "test_topic"//主题  
  22.     char *client_id = "clientid33883";//客户端ID; 对测试服务器,可以随便写  
  23.     char *username = NULL;//用户名,用于验证身份。对测试服务器,无。  
  24.     char *password = NULL;//密码,用于验证身份。对测试服务器,无。  
  25.     int Qos; //Quality of Service  
  26.   
  27.     //create new mqtt client object  
  28.     m = mqtt_new(host, MQTT_PORT, client_id); //创建对象,MQTT_PORT = 1883  
  29.     if ( m == NULL ) {  
  30.         printf("mqtt client create failure, return  code = %d\n", errno);  
  31.         return 1;  
  32.     } else {  
  33.         printf("mqtt client created\n");  
  34.     }  
  35.   
  36.     //connect to server  
  37.     ret = mqtt_connect(m, username, password); //连接服务器  
  38.     if (ret != MQTT_SUCCESS ) {  
  39.         printf("mqtt client connect failure, return code = %d\n", ret);  
  40.         return 1;  
  41.     } else {  
  42.         printf("mqtt client connect\n");  
  43.     }  
  44.   
  45.     //subscribe  
  46.     Qos = QOS_EXACTLY_ONCE;  
  47.     ret = mqtt_subscribe(m, topic, Qos);//订阅消息  
  48.     printf("mqtt client subscribe %s,  return code = %d\n", topic, ret);  
  49.   
  50.     signal(SIGINT, stop_running);  
  51.     signal(SIGTERM, stop_running);  
  52.   
  53.     printf("wait for message of topic: %s ...\n", topic);  
  54.   
  55.     //loop: waiting message, 循环  
  56.     while (running) {  
  57.         int timeout = 200;  
  58.         if ( mqtt_receive(m, timeout) == MQTT_SUCCESS ) { //recieve message,接收消息  
  59.             printf("received Topic=%s, Message=%s\n", m->received_topic, m->received_message);  
  60.         }  
  61.         mqtt_sleep(200); //sleep a while  
  62.     }  
  63.   
  64.     mqtt_disconnect(m); //disconnect  
  65.     printf("mqtt client disconnect");  
  66.     mqtt_delete(m);  //delete mqtt client object  
  67.     return 0;  
  68. }  

主程序分为几个步骤:

1, 调用 mqtt_new()创建 客户端对象

2, 调用 mqtt_connect() 连接服务器

3, 调用 mqtt_subscribe() 订阅消息

4, 进入循环:不断用 mqtt_receive()检测有否新消息,如有,则打印出来。

用Eclipse编译项目 (记得必须在GCC中加入连接选项:  -lpthread -ldl )

OK, 编译成功了。

用scp将 mqtt_subscribe 程序上传到 linkit 7688:   scp mqtt_subscribe root@mylinkit.local:/root


用SSH登录入Linkit 7688,  运行 mqtt_subscribe 程序。则此时出现:

mqtt client created

mqtt client connect

mqtt client subscribe,  return code = 0

wait for message of topic: test_topic ...


显示程序在等待消息到来


准备发送消息的网页客户端: 用浏览器打开 http://m2m.demos.ibm.com/mqttclient/

在Connect栏中, 点Connect.

在Publish栏中,将topic设为 test_topic, 将Message设为 “Say hello to linkit 7688” 按publish, 则发出一条MQTT消息。


这时, 看Linkit 7688 SSH客户端,可以看到,Linkit7688立即收到了这条MQTT消息

mqtt client created

mqtt client connect

mqtt client subscribe,  return code = 0

wait for message of topic: test_topic ...

received Topic=test_topic, Message=Say hello to linkit 7688


成功了: Linkit 7688 实时收到了网页客户端发来的消息。

mqtt_subscribe 程序是死循环,一直在接收消息。 可按CTRL+C退出。



通用性: Eclipse Paho C 的 MQTT客户端代码,可以运行在linux、Windows、Mac上。所以,上述的代码可以用于Linux、Windows、Mac操作系统,编写各种App。

对于Android手机, Eclipse Paho另提供了Java的代码库。