QT使用MQTT协议对接华为IOT平台
最近想使用QT通过MQTT协议接入华为的IOT平台,实现数据的上报以及命令的接收,经过查找资料,可以使用QT的MQTT协议来完成这样的功能,下面是此次实验过程中的记录。
一、环境说明
- 开发环境:Ubuntu16.04 x64
- 软件:Qt 5.5.1 for Embedded
- 交叉编译工具链:arm-Linux-guneabihf
- 硬件平台:正点原子ALPHA Linux开发板
二、QMQTT源码编译
目前Qt5.11中已经提供了类似TCP或者UDP的MQTT类,但是想要在低于此版本的Qt中使用MQTT协议,就需要自行编译开源MQTT消息服务EMQTT为Qt提供的QMQTT源码。下面具体介绍在Ubuntu下使用该源码的方式。
- 下载QMQTT源码,在Linux终端中使用git 下载源码
git clone https://github.com/emqtt/qmqtt
- 网上有网友将QMQTT的源码编译成了.so或者dll的库文件,我在使用交叉编译工具链编译的时候出现了问题,没有解决,就采用了一位网友简单粗暴的方式,将所有的.cpp和.h文件copy到工程目录下并添加的到工程,编译出来的可执行文件大约7M。打开下载好的文件进入src目录下,将mqtt文件夹复制到工程目录下,并添加到工程中。
- QT工程的修改
(1)pro文件的修改
(2)在qt直接使用就可以了,包含头文件#include <mqtt/qmqtt.h>
三、对接华为IoT平台
- 华为设备接入开发文档
在MQTT设备快速接入这篇文章中,详细说明了如何通过设备id以及设备密钥生成接入信息的步骤。下面将一步步的演示这些步骤。 - 创建产品并注册设备,这里直接借用华为文档中的图片。
- 生成连接信息,这部分也是文档中的一部分,点击文档中的 连接信息生成工具 即可下载。
下载完成后,打开填入自己的设备ID和设备密钥,点击Generate生成,会在Message框中显示需要的ClientID、username、password
。(注:这个工具需要安装JDK,在华为云文档中有链接)。
- QT工程中使用,完成上述步骤后,在QT中就可以直接编写一个MQTT Client来实现对接到华为云的功能。
(1)初始化MQTT
//MqttClient.h
#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H
#include <QObject>
#include <macroinclude.h>
#include <mqtt/qmqtt.h>
class MqttClient : public QObject
{
Q_OBJECT
public:
explicit MqttClient(QObject *parent = 0);
void mqttInit(QString domainName, quint16 Port);
signals:
public slots:
void onMQTT_Received(QMQTT::Message message);
void connectTOHuaWeiIOT(QString domainName, quint16 Port);
void disConnectTOHuaWeiIOT();
void MQTT_SendMessage(QMQTT::Message msg);
private:
QMQTT::Client mqttclient;
};
#endif // MQTTCLIENT_H
//MqttClient.pp
#include "MqttClient.h"
MqttClient::MqttClient(QObject *parent) : QObject(parent)
{
}
//mqtt 初始化
void MqttClient::mqttInit(QString domainName, quint16 Port)
{
QHostInfo info = QHostInfo::fromName(domainName);
QString host = info.addresses().first().toString(); // 代理服务器 IP
qDebug() << host;
// mqttclient = new QMQTT::Client(QHostAddress(host),Port);
mqttclient.setKeepAlive(120);
mqttclient.setHost(QHostAddress(host));
mqttclient.setPort(Port);
mqttclient.setClientId(CLIENTID);
mqttclient.setUsername(USERNAME);
mqttclient.setPassword(PASSWORD);
mqttclient.cleanSession();
mqttclient.setVersion(QMQTT::MQTTVersion::V3_1_1); // 设置mqtt版本
connect(&mqttclient,SIGNAL(received(QMQTT::Message)),this,SLOT(onMQTT_Received(QMQTT::Message)));
}
//链接到华为物联网平台
void MqttClient::connectTOHuaWeiIOT(QString domainName, quint16 Port)
{
//初始化 MQTT
mqttInit(domainName,Port);
mqttclient.connectToHost();
qDebug()<<"connect to host success!!";
}
//断开与平台的链接
void MqttClient::disConnectTOHuaWeiIOT()
{
mqttclient.disconnectFromHost();
qDebug()<<"disconnect huaweiIOT!!";
}
//接收消息的槽函数
void MqttClient::onMQTT_Received(QMQTT::Message message)
{
QString str(message.payload());
qDebug()<<"onMQTT_Received: "<<str;
QMQTT::Message message();
}
//发送消息的槽函数
void MqttClient::MQTT_SendMessage(QMQTT::Message msg)
{
mqttclient.publish(msg);
}
(2)Widget.cpp中使用,主要是连接按钮和断开连接按钮以及一个发送按钮信号槽的编写,这边直接展示槽函数,信号由Button触发。
//构造函数中
MqttClient *MQTTClient;
MQTTClient = new MqttClient(this);
//连接华为IOT
void Widget::connectToHuaweiIoT_slot(QString domainName,quint16 port)
{
MQTTClient->connectTOHuaWeiIOT(domainName,port);
}
//断开连接华为Iot
void Widget::disconnectTOHuaweiIoT_slot()
{
MQTTClient->disConnectTOHuaWeiIOT();
}
//设备属性上报
connect(ui->toolButton,&QToolButton::clicked,[this](){
QString topic = EQUIPMENT_REPORT_TOPIC;
qDebug()<<topic;
QByteArray array = getEquipmentReportJson();
qDebug()<<array;
QMQTT::Message msg(0,topic,array);
MQTTClient->MQTT_SendMessage(msg);
});
其中在设备属性上报中,EQUIPMENT_REPORT_TOPIC
是华为IOT平台提供的一个topic,MQTT实质是一种发布订阅模式,此topic是设备上报属性时用的topic,其他topic在华为设备接入文档API参考中有详细介绍。直接附图。
根据开发文档给的示例格式,我们需要封装一个这样的JSON数据包,在QT中有相关的JSON类,例如 JSONObject,JSONArray,JSONValue,JSONDocument
等等,下面是我封装的一个设备属性上报的JSON数据包,并使用QJSONDocument
转换为QByteArray
(原因下面介绍)。
#include "JsonUtils.h"
//设备属性上报JSON封装
/*
{
"service_id": "Battery",
"properties": {
"batteryLevel": 80
},
"event_time": "20201212T121212Z"
}
*/
QByteArray getEquipmentReportJson()
{
//构建 JSON 对象 properties
QJsonObject propertiesObj;
propertiesObj.insert("batteryLevel",80);
//构建 JSON 对象
QJsonObject jsonObj;
jsonObj.insert("service_id","Battery");
jsonObj.insert("event_time",getSystemTime());
jsonObj.insert("properties",QJsonValue(propertiesObj));
//添加到JSON数组 services
QJsonArray jsonarray;
jsonarray.push_back(jsonObj);
//构建 JSON 总体对象
QJsonObject json;
json.insert("services",QJsonValue(jsonarray));
//构建 JSON 文档
QJsonDocument document;
document.setObject(json);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
return byteArray;
}
//获取当前系统时间 IOT平台那边接受的是格林威治时间,GMT+8.00
QString getSystemTime()
{
QDateTime currentTime = QDateTime::currentDateTime();
QString currentTimeStr = currentTime.toString("yyyyMMddThhmmssZ");
QTime current_time =QTime::currentTime();
int hour_int = current_time.hour();
if(hour_int >= 8) hour_int -= 8;
else hour_int = 24 + hour_int - 8;
QString hour = QString::number(hour_int);//当前的小时
QString result = currentTimeStr.mid(0,9)+hour+currentTimeStr.mid(11);
return result;
}
这边的一个getSystemTime()
是本人编写的一个获取系统时间的函数(写的有点复杂,好在能用),由于华为IOT平台那边使用的GMT+8.00,会在我们上报的时间基础上再加上8小时,所以我们上报时,要将上报时间减去八小时。
通过getEquipmentReportJson()
这个函数就输出了我们需要上报的数据,为什么要使用QByteArray这样的数据呢,这个是由QMQTT::Message
的构造函数决定。我们上报数据使用的是
quint16 QMQTT::Client::publish(const Message& message);
而QMQTT::Message
的构造函数如下:
//qmqtt_message.cpp
Message::Message(const quint16 id, const QString &topic, const QByteArray &payload,
const quint8 qos, const bool retain, const bool dup)
: d(new MessagePrivate(id, topic, payload, qos, retain, dup))
{
}
//qmqtt_message.h
explicit Message(const quint16 id, const QString &topic, const QByteArray &payload,
const quint8 qos = 0, const bool retain = false, const bool dup = false);
Message的构造函数有六个参数,其中后三个参数都是有默认值的,第一个参数为id,第二个为发布的topic(由物联网平台或者用户自己订阅),第三个是要发送的数据,是一个QByteArray
类型,因此就有了通过JSON来生成QByteArray
这样的一个步骤。
四、效果验证
这边可以看到在2020/08/09 23:54:18 GMT+08:00
我上报的最新的消息为80.
本文地址:https://blog.csdn.net/qq_33685823/article/details/107908533
上一篇: 前端页面点击图片放大功能(viewerjs插件的简单而强大)
下一篇: 卡被吞了吧