mqtt 移植
Qt 开发 MQTT 程序有两种方式,一个是 Qt 官方提供的基于 MQTT 的封装,一个是第三方(EMQ)开发的用于 Qt 调用 MQTT 的接口,二者使用方法大同小异,并且均提供了源码。那么,这里来介绍第一种,如基于 Qt 官方提供的封装来使用 MQTT。
-
下载
Qt官方在 github 上提供了源代码,地址:
https://github.com/qt/qtmqtt
要下载与板子上安装的 qt 一样的版本
-
下载好后用 Qt Creator 打开 qtmqtt.pro
-
如果没有 perl,要先安装 perl
-
然后编译,然后开始处理报错
-
一般会提示找不到头文件,将头文件用绝对路径包含,改到没有报错,一般不会有其他错误
替换为源码下 src 下的 mqtt 里面的文件
用 linux 下的 Kits 编译,将头文件改掉后就没有报错了,并且可以将里面的例程执行(mqtt 源码下的 example)
用 arm 的交叉编译工具链的 Kits 编译时,example 下的头文件就会报错,此时可以不编译 example 下的代码,即去掉下面文件的中的代码(红框中),就可以编译了
-
-
编译完成后,就会生成工程文件:
-
搭建工程
-
创建一个 qt 工程
-
将上面编译出来的工程文件中的 include 文件夹复制到 mqtt 源码目录下的 src/mqtt 下
-
将上面复制好的 mqtt 文件夹 和 编译出来的工程文件夹中的 lib 文件夹复制到新创建 qt 工程的目录下
-
qt 工程添加外部库
-
添加头文件目录
-
接下来就可以编译工程啦
-
-
在 arm 板上运行
单纯将可执行文件传到板子上运行会缺少库
将 编译出来的库文件都复制到板子的 /lib 目录下,用打包的形式,在板子上解压
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jehm4WW9-1662952832139)(D:\TyporaNote\1 Linux\Linux\mqtt.assets\image-20220912101218560.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ND4z8xGN-1662952832139)(D:\TyporaNote\1 Linux\Linux\mqtt.assets\image-20220912101425139.png)]
实例
ubuntu 上运行
要用 linux 的 Kits 编译
如上配置好工程就可以运行
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <mqtt/qmqttclient.h>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void setClientPort(int p);
private slots:
void on_buttonConnect_clicked();
void on_buttonQuit_clicked();
void updateLogStateChange();
void brokerDisconnected();
void on_buttonPublish_clicked();
void on_buttonSubscribe_clicked();
void on_buttonPing_clicked();
private:
Ui::MainWindow *ui;
QMqttClient *m_client;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtCore/QDateTime>
#include <mqtt/qmqttclient.h>
#include <QtWidgets/QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_client = new QMqttClient(this);
m_client->setHostname(ui->lineEditHost->text());
m_client->setPort(ui->spinBoxPort->value());
connect(m_client, &QMqttClient::stateChanged, this, &MainWindow::updateLogStateChange);
connect(m_client, &QMqttClient::disconnected, this, &MainWindow::brokerDisconnected);
connect(m_client, &QMqttClient::messageReceived, this, [this](const QByteArray &message, const QMqttTopicName &topic) {
const QString content = QDateTime::currentDateTime().toString()
+ QLatin1String(" Received Topic: ")
+ topic.name()
+ QLatin1String(" Message: ")
+ message
+ QLatin1Char('\n');
ui->editLog->insertPlainText(content);
});
connect(m_client, &QMqttClient::pingResponseReceived, this, [this]() {
ui->buttonPing->setEnabled(true);
const QString content = QDateTime::currentDateTime().toString()
+ QLatin1String(" PingResponse")
+ QLatin1Char('\n');
ui->editLog->insertPlainText(content);
});
connect(ui->lineEditHost, &QLineEdit::textChanged, m_client, &QMqttClient::setHostname);
connect(ui->spinBoxPort, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::setClientPort);
updateLogStateChange();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_buttonConnect_clicked()
{
if (m_client->state() == QMqttClient::Disconnected) {
ui->lineEditHost->setEnabled(false);
ui->spinBoxPort->setEnabled(false);
ui->buttonConnect->setText(tr("Disconnect"));
m_client->connectToHost();
} else {
ui->lineEditHost->setEnabled(true);
ui->spinBoxPort->setEnabled(true);
ui->buttonConnect->setText(tr("Connect"));
m_client->disconnectFromHost();
}
}
void MainWindow::on_buttonQuit_clicked()
{
QApplication::quit();
}
void MainWindow::updateLogStateChange()
{
const QString content = QDateTime::currentDateTime().toString()
+ QLatin1String(": State Change")
+ QString::number(m_client->state())
+ QLatin1Char('\n');
ui->editLog->insertPlainText(content);
}
void MainWindow::brokerDisconnected()
{
ui->lineEditHost->setEnabled(true);
ui->spinBoxPort->setEnabled(true);
ui->buttonConnect->setText(tr("Connect"));
}
void MainWindow::setClientPort(int p)
{
m_client->setPort(p);
}
void MainWindow::on_buttonPublish_clicked()
{
if (m_client->publish(ui->lineEditTopic->text(), ui->lineEditMessage->text().toUtf8()) == -1)
QMessageBox::critical(this, QLatin1String("Error"), QLatin1String("Could not publish message"));
}
void MainWindow::on_buttonSubscribe_clicked()
{
auto subscription = m_client->subscribe(ui->lineEditTopic->text());
if (!subscription) {
QMessageBox::critical(this, QLatin1String("Error"), QLatin1String("Could not subscribe. Is there a valid connection?"));
return;
}
}
void MainWindow::on_buttonPing_clicked()
{
ui->buttonPing->setEnabled(false);
m_client->requestPing();
}
mainwindow.ui
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wTc1JxSO-1662952832140)(D:\TyporaNote\1 Linux\Linux\mqtt.assets\image-20220912102526035.png)]
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>768</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Host:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEditHost">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spinBoxPort">
<property name="maximum">
<number>99999</number>
</property>
<property name="value">
<number>1883</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonConnect">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="lineEditTopic">
<property name="text">
<string>qtmqtt/topic1</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Topic:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Message:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="buttonPublish">
<property name="text">
<string>Publish</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="buttonSubscribe">
<property name="text">
<string>Subscribe</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditMessage">
<property name="text">
<string>This is a test message</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="buttonPing">
<property name="text">
<string>Ping</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Log Messages</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPlainTextEdit" name="editLog"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonQuit">
<property name="text">
<string>Quit</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionQuit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
运行
保存 ubuntu 可以上网
broke.hivemq.com 是 mqtt 的测试网站
arm 上运行
需保证 arm 可以上网,即可以 ping 通外网(配置好 wifi 驱动)
要用 arm 的 Kits 编译
如上配置好工程就可以运行
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <mqtt/qmqttclient.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void initUI();
private slots:
void on_btnConnect_clicked();
void on_btnDisconnect_clicked();
void on_btnPublish_clicked();
void updateLogStateChange();
void brokerDisconnected();
void on_btnPing_clicked();
void on_btnSubscribe_clicked();
private:
Ui::MainWindow *ui;
QMqttClient *m_client;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtCore/QDateTime>
#include <QMessageBox>
#include <mqtt/qmqttsubscription.h>
#define HOSTNAME "35.156.5.219"
#define MQTTPORT 1883
#define TOPIC "qtmqtt/topic1"
#define MQTTNAME "test"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_client = new QMqttClient(this);
m_client->setHostname(HOSTNAME);
m_client->setPort(MQTTPORT);
connect(m_client, &QMqttClient::stateChanged,this, &MainWindow::updateLogStateChange);
connect(m_client, &QMqttClient::messageReceived, this,
[this](const QByteArray & message, const QMqttTopicName &topic){
const QString content = QDateTime::currentDateTime().toString()
+ QLatin1String(" Received Topic: ")
+ topic.name()
+ QLatin1String(" Message: ")
+ message
+ QLatin1Char('\n');
ui->plainTextEdit->insertPlainText(content);
});
connect(m_client, &QMqttClient::pingResponseReceived, this, [this](){
ui->btnPing->setEnabled(true);
const QString content = QDateTime::currentDateTime().toString()
+ QLatin1String(" PingResponse")
+ QLatin1Char('\n');
ui->plainTextEdit->insertPlainText(content);
});
connect(m_client, &QMqttClient::disconnected, this, &MainWindow::brokerDisconnected);
initUI();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initUI()
{
ui->lineEditHost->setText(HOSTNAME);
ui->lineEditPort->setText(QString(MQTTPORT));
ui->lineEditName->setText(MQTTNAME);
}
void MainWindow::on_btnConnect_clicked()
{
if (m_client->state() == QMqttClient::Disconnected) {
ui->btnConnect->setEnabled(false);
ui->btnDisconnect->setEnabled(true);
m_client->connectToHost();
}
}
void MainWindow::on_btnDisconnect_clicked()
{
if(m_client->state() == QMqttClient::Connected){
ui->btnDisconnect->setEnabled(false);
ui->btnConnect->setEnabled(true);
m_client->disconnectFromHost();
}
}
void MainWindow::on_btnPublish_clicked()
{
QMqttTopicName topicName;
topicName.setName(TOPIC);
QString message("hello");
if (m_client->publish(topicName, message.toUtf8()) == -1)
QMessageBox::critical(this, QLatin1String("Error"), QLatin1String("Could not publish message"));
}
void MainWindow::updateLogStateChange()
{
const QString content = QDateTime::currentDateTime().toString()
+ QLatin1String(": State Change")
+ QString::number(m_client->state())
+ QLatin1Char('\n');
ui->plainTextEdit->insertPlainText(content);
}
void MainWindow::brokerDisconnected()
{
ui->btnConnect->setEnabled(true);
ui->btnDisconnect->setEnabled(false);
}
void MainWindow::on_btnPing_clicked()
{
ui->btnPing->setEnabled(false);
if(m_client->requestPing())
QMessageBox::about(NULL, "ping", "ping succuss");
else
QMessageBox::about(NULL, "ping", "ping failse");
}
void MainWindow::on_btnSubscribe_clicked()
{
QMqttTopicFilter topicFilter;
topicFilter.setFilter(TOPIC);
auto subscription = m_client->subscribe(topicFilter);
if (!subscription) {
QMessageBox::critical(this, QLatin1String("Error"), QLatin1String("Could not subscribe. Is there a valid connection?"));
return;
}
}
mainwindow.ui
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k78IHjnK-1662952832142)(D:\TyporaNote\1 Linux\Linux\mqtt.assets\image-20220912103931133.png)]
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>set</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Client ID</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Host</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="lineEditName">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditClientId"/>
</item>
<item>
<widget class="QLineEdit" name="lineEditHost"/>
</item>
<item>
<widget class="QLineEdit" name="lineEditPort"/>
</item>
<item>
<widget class="QLineEdit" name="lineEditUsername"/>
</item>
<item>
<widget class="QLineEdit" name="lineEditPassword"/>
</item>
</layout>
</item>
<item row="0" column="2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="btnConnect">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnPing">
<property name="text">
<string>Ping</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSubscribe">
<property name="text">
<string>Subscribe</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDisconnect">
<property name="text">
<string>disconnect</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnQuit">
<property name="text">
<string>quit</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>log</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QPlainTextEdit" name="plainTextEdit"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Message</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QPlainTextEdit" name="plainTextEditMessage"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnPublish">
<property name="text">
<string>Publish</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections>
<connection>
<sender>btnQuit</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>959</x>
<y>208</y>
</hint>
<hint type="destinationlabel">
<x>511</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>
运行
保存 arm 可以上网
broke.hivemq.com 是 mqtt 的测试网站
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AvMWt9kZ-1662952832142)(D:\TyporaNote\1 Linux\Linux\mqtt.assets\image-20220912103143327.png)]
由于板子上还不能 ping 域名,说以改用 直接的网址(ping 域名后会出现网址,用这个)