mqtt移植及arm端Qt使用

时间:2024-03-14 14:08:29

一、简述

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。

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

二、移植 

       要移植mqtt,首先需要先移植openssl,因为在编译mqtt时会用到openssl的lib库

       2.1 openssl移植到ARM Linux

      OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法、常用的**和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。

      备注:开发环境:Ubuntu 18.04  交叉编译工具:aarch64-linux-gnu-gcc version 7.3.0。开发环境不一定是我这配置,只要是linux操作系统,和ARM对应的交叉工具链就可以。

     首先从OpenSSL官网下载 源码,建议下载最新源码低一个版本的

     官网链接:https://www.openssl.org/source/

  1. 执行下面命名解压缩:

     tar zxvf openssl-1.0.2l.tar.gz

     2.进入刚解压的目录cd openssl-1.0.2l/,执行下面指令,做相应的配置:

     ./config no-asm shared --prefix=$(pwd)/__install

     备注:

  1. no-asm: 是在交叉编译过程中不使用汇编代码代码加速编译过程,原因是它的汇编代码是对arm格式不支持的。
  2. shared :生成动态连接库。
  3. --prefix :指定make install后生成目录的路径,不修改此项则默认为OPENSSLDIR目录(/usr/local/ssl)。
  4. 建议不指定pwd具体路径
  5. 建议将CFLAG中的-m64删掉,同时找到SHARED_LDFLAGS=-m64这一句,将-m64删除

     3.修改Makefile:

     mqtt移植及arm端Qt使用

    以上为修改后的内容

    make

    make install

   成功后查看如图所示目录下是否有相同文件夹

   include下文件在编译程序的时候需要指定include的路径。而lib下在程序运行时会用到,需要将lib下文件拷贝到开发板中。

  2.2 交叉编译mqtt

  1.下载源码    源码地址:http://www.eclipse.org/paho/downloads.php

  下载源码后,将压缩包拷贝到Ubuntu中,解压 tar xvf 文件名

   进入解压后的目录,找到Makefile文件,打开

   找到在101行 添加CC ?= 处,然后修改为如下图所示

mqtt移植及arm端Qt使用

make

不出意外则可以编译成功,使用使用可以直接修改src/sample中的例子

三、arm端使用

首先将编译好的生成的output文件整个copy到arm中,然后将output中的所有库文件copy到arm板的lib目录下,然后可以运行sample中的可执行程序,查看效果。过程较为简单,就不贴图了。

3.1Qt端调用mqtt

因Qt5.10之前都没有官方支持的mqtt库。所以只能下载mqtt的源码自行编译,但多次测试只能在Windows和linux下用Qt编译mqtt源码,生成动态库。但用交叉编译链移植无法编译,如果有大神编译成功,请不吝赐教,感激不尽。

所以之前交叉编译的是基于c语言的mqtt源码包;

1.发布

首先修改sample中的mqtt移植及arm端Qt使用

1.修改宏定义,如图所示mqtt移植及arm端Qt使用

2.在main函数中重新定义一个PAYLOAD来接收从命令行输入的发布内容,建议变量名不要修改,就用原有的命名,这样就不需要修改其他函数了。mqtt移植及arm端Qt使用

至此发布就可以自己手动输入要发布的内容了。

 

2.订阅

订阅的话也是同样的要先修改宏定义mqtt移植及arm端Qt使用,修改方式和发布是一样的。

然后就可以正常订阅服务器发出的消息了。

为了方便Qt端访问将订阅的消息存成到一个临时文件中,在Qt端调用时就可以直接打开文件读取

mqtt移植及arm端Qt使用

3.Qt调用

在arm端使用Qt程序来编写mqtt的客户端的话,首先需要了解Qt的QProcess进程类

QProcess
可用于完成启动外部程序,并与之交互通信。启动时注意需要带上执行文件的路径
一、启动外部程序的两种方式
  1)一体式:void QProcess::start(const QString & program,const QStringList &arguments,OpenMode mode = ReadWrite)
    外部程序启动后,将随主程序的退出而退出。
  2)分离式:void QProcess::startDetached(const QString & program,const QStringList & arguments,const QString&workingDirectory=QString(),qint64 *pid =0)
    外部程序启动后,当主程序退出时并不退出,而是继续运行。
二、启动之前需要做的工作:
  启动一个外部程序,需要传递外部程序的路径和执行参数,参数用QStringList来带入。
 QString str_exe;
    str_exe = "./pubsync"; //相对于Qt执行程序的相对路径,也可以改成绝对路径
    QStringList args;
    args << "dasdaefgedfg" << "asfwefewfrew"; //传递的参数,可以改成实时变化的
    process->start(str_exe,args);

   if(!process->waitForStarted())  //等待程序启动,会阻塞,建议使用多线程调用
    {
        ui->Edit_tip->append("process call failed");
    }
    ui->Edit_tip->append("process call sucessed!");

在启动前建议调用QProcess的信号和槽机制来对程序进行检测

connect(process,SIGNAL(readyReadStandardError()),this,SLOT(startReadErrOutput()));
connect(process,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(finishProcess(int,QProcess::ExitStatus)));

void MainWindow::finishProcess(int, QProcess::ExitStatus exitStatus)
{
    if(exitStatus == QProcess::NormalExit)
    {
        ui->Edit_tip->append("process exit normal");
    }
    else
    {
        ui->Edit_tip->append("process exit crash");
    }
    ui->Edit_tip->append("process finished");
}

同时需要重新一下closeEvent(QCloseEvent *event)这个虚函数,来回收进程

    process->kill();
    if(!process->waitForFinished(100))
        return;
    event->accept();

 

订阅同样也是需要创建一个QProcess *recv_process;

       QString str_exe;
        str_exe = "./subasync";
        recv_process->start(str_exe);
        if(!recv_process->waitForStarted())
        {
            ui->Edit_tip->append("recv_process call failed");
        }
        else
        {
            ui->Edit_tip->append("recv_process call sucessed!");
        }

启动程序后就可以读取文件了

 QFile file("./temp");
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;
    QByteArray  ba = file.readAll();
    ui->Edit_recvBrowser->append(QString::fromLocal8Bit(ba));

    file.close(); //建议每次读取后将文件清空

读取完后同样也需要对进程进行同上的处理。至此Qt调用完成。

对于订阅时通过read直接读取外部进程中接收的内容,经常测试无法访问,推测是因为此进程一直处于阻塞状态,导致无法读取,所以采用读取文件的方式直接读取。