QT使用MQTT协议对接华为IOT平台

2 篇文章 0 订阅
2 篇文章 0 订阅


    最近想使用QT通过MQTT协议接入华为的IOT平台,实现数据的上报以及命令的接收,经过查找资料,可以使用QT的MQTT协议来完成这样的功能,下面是此次实验过程中的记录。

一、环境说明

  1. 开发环境:Ubuntu16.04 x64
  2. 软件:Qt 5.5.1 for Embedded
  3. 交叉编译工具链:arm-Linux-guneabihf
  4. 硬件平台:正点原子ALPHA Linux开发板

二、QMQTT源码编译

    目前Qt5.11中已经提供了类似TCP或者UDP的MQTT类,但是想要在低于此版本的Qt中使用MQTT协议,就需要自行编译开源MQTT消息服务EMQTT为Qt提供的QMQTT源码。下面具体介绍在Ubuntu下使用该源码的方式。

  1. 下载QMQTT源码,在Linux终端中使用git 下载源码
git clone https://github.com/emqtt/qmqtt
  1. 网上有网友将QMQTT的源码编译成了.so或者dll的库文件,我在使用交叉编译工具链编译的时候出现了问题,没有解决,就采用了一位网友简单粗暴的方式,将所有的.cpp和.h文件copy到工程目录下并添加的到工程,编译出来的可执行文件大约7M。打开下载好的文件进入src目录下,将mqtt文件夹复制到工程目录下,并添加到工程中。
  2. QT工程的修改
    (1)pro文件的修改

    (2)在qt直接使用就可以了,包含头文件#include <mqtt/qmqtt.h>
    在这里插入图片描述

三、对接华为IoT平台

  1. 华为设备接入开发文档
    进入华为云官网,点击文档
    在这里插入图片描述
    在这里插入图片描述
        在MQTT设备快速接入这篇文章中,详细说明了如何通过设备id以及设备密钥生成接入信息的步骤。下面将一步步的演示这些步骤。
  2. 创建产品并注册设备,这里直接借用华为文档中的图片。
    在这里插入图片描述
  3. 生成连接信息,这部分也是文档中的一部分,点击文档中的 连接信息生成工具 即可下载。

        下载完成后,打开填入自己的设备ID和设备密钥,点击Generate生成,会在Message框中显示需要的 ClientID、username、password。(注:这个工具需要安装JDK,在华为云文档中有链接)。
    在这里插入图片描述
  4. 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.
在这里插入图片描述

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在C语言中实现MQTT协议通信华为云IOT可以使用MQTT C语言客户端库,以下是具体的实现步骤: 1. 首先需要在华为云IOT上创建设备并获取设备证书以及设备ID和物联网平台的服务地址。 2. 下载MQTT C语言客户端库,例如Eclipse Paho MQTT C库,该库可以在Linux、Windows和Mac OS等平台使用。下载地址:https://github.com/eclipse/paho.mqtt.c 3. 在代码中引入相关头文件: ```c #include "MQTTClient.h" ``` 4. 创建MQTT客户端并初始化MQTT连接参数: ```c MQTTClient client; MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; ``` 5. 设置连接参数,包括设备证书、设备ID、服务地址、连接超时时间等: ```c char* address = "ssl://XXXX.iot-mqtts.cn-north-4.myhuaweicloud.com:8883"; char* clientID = "XXXX"; char* username = "XXXX"; char* password = "XXXX"; char* root_ca = "XXXX.pem"; conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 1; conn_opts.username = username; conn_opts.password = password; conn_opts.ssl = &ssl_opts; ``` 其中,`ssl://XXXX.iot-mqtts.cn-north-4.myhuaweicloud.com:8883`为华为云IOT物联网平台的服务地址,`clientID`为设备ID,`username`和`password`为设备证书的用户名和密码,`root_ca`为根证书。 6. 创建MQTT连接: ```c MQTTClient_create(&client, address, clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); if (MQTTClient_connect(client, &conn_opts) != MQTTCLIENT_SUCCESS) { printf("Failed to connect\n"); return -1; } ``` 7. 发布消息: ```c MQTTClient_message pubmsg = MQTTClient_message_initializer; pubmsg.payload = "Hello world!"; pubmsg.payloadlen = strlen("Hello world!"); pubmsg.qos = QOS; MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token); ``` 其中,`TOPIC`为MQTT主题,`QOS`为消息发布的质量等级。 8. 订阅主题: ```c MQTTClient_subscribe(client, TOPIC, QOS); ``` 9. 接收消息: ```c void messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message) { printf("Message arrived\n"); printf(" topic: %s\n", topicName); printf(" message: "); fwrite(message->payload, 1, message->payloadlen, stdout); printf("\n"); MQTTClient_freeMessage(&message); MQTTClient_free(topicName); } ``` 10. 断开MQTT连接: ```c MQTTClient_disconnect(client, 10000); MQTTClient_destroy(&client); ``` 完整的代码示例如下: ```c #include "MQTTClient.h" #include <string.h> #define QOS 1 #define TIMEOUT 10000L void messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message) { printf("Message arrived\n"); printf(" topic: %s\n", topicName); printf(" message: "); fwrite(message->payload, 1, message->payloadlen, stdout); printf("\n"); MQTTClient_freeMessage(&message); MQTTClient_free(topicName); } int main(int argc, char* argv[]) { MQTTClient client; MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; MQTTClient_SSLOptions ssl_opts = MQTTClient_SSLOptions_initializer; char* address = "ssl://XXXX.iot-mqtts.cn-north-4.myhuaweicloud.com:8883"; char* clientID = "XXXX"; char* username = "XXXX"; char* password = "XXXX"; char* root_ca = "XXXX.pem"; char* topic = "XXXX"; MQTTClient_message pubmsg = MQTTClient_message_initializer; MQTTClient_deliveryToken token; int rc; MQTTClient_create(&client, address, clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 1; conn_opts.username = username; conn_opts.password = password; ssl_opts.enableServerCertAuth = 1; ssl_opts.trustStore = root_ca; conn_opts.ssl = &ssl_opts; if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) { printf("Failed to connect, return code %d\n", rc); return -1; } MQTTClient_subscribe(client, topic, QOS); MQTTClient_setCallbacks(client, NULL, NULL, messageArrived, NULL); pubmsg.payload = "Hello world!"; pubmsg.payloadlen = strlen("Hello world!"); pubmsg.qos = QOS; pubmsg.retained = 0; MQTTClient_publishMessage(client, topic, &pubmsg, &token); printf("Waiting for up to %d seconds for publication of %s\n" "on topic %s for client with ClientID: %s\n", (int)(TIMEOUT/1000), "Hello world!", topic, clientID); rc = MQTTClient_waitForCompletion(client, token, TIMEOUT); printf("Message with delivery token %d delivered\n", token); MQTTClient_disconnect(client, 10000); MQTTClient_destroy(&client); return rc; } ``` 注意,在使用该代码前需要将其中的`XXXX`替换为对应的设备证书、设备ID和华为云IOT服务地址等信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值