简介:在Qt5.10.1环境下,使用minGW5.10.1编译器,集成MQTT协议并与阿里云物联网平台对接的完整工程,适合需要为桌面应用添加IoT能力的Qt开发者,也适合学习QMQTT库移植与协议封装的中级程序员。资源共28个文件,以20个头文件为主,提供核心API声明;另含2个dll与2个静态库,封装了可直接调用的QMQTT实现;其余cpp、ui、pro文件构成示例工程,便于编译与修改。压缩包仅1.91MB。已有2114人学习下载。通过该工程可快速掌握Connect、Publish、Subscribe、Unsubscribe等核心调用流程,并理解阿里云物联网平台接入时的QoS设置、主题订阅与消息发布参数配置;由于内置已编译的Qt5Qmqtt库,导入pro后即可运行,省去自行编译第三方库的环节,适合作为模板进行二次开发与协议调试。 QT5.10.1+MQTT+minGW5.10.1,这个组合一看就是从某个嵌入式或者物联网项目里打包出来的。我拿到这个压缩包的第一反应是:这是一套给Windows平台用的上位机开发环境,而且大概率是配合ESP8266、STM32这类硬件设备做数据采集和远程控制用的。QT提供界面,MQTT负责和设备或者云平台通信,minGW5.10.1则是把代码变成可执行文件的编译器。三者缺一不可,而且版本号都定死,说明踩过不少坑之后才固定下来的一套组合。
今天就把这套工具链的来龙去脉、部署步骤和实际开发中的关键细节一次性讲清楚,给正在折腾QT上位机的朋友一个参考。
1. 这套工具链是什么,为什么这么组合
1.1 QT5.10.1:稳定和兼容的平衡点
很多人好奇为什么不用最新的QT6,而是用QT5.10.1这个“老掉牙”的版本。这里面的逻辑很务实:5.10.1是Qt5时代一个非常稳定的版本,对Windows 7到Windows 10都有良好的兼容性,而且很多第三方库(比如串口通信的qextserialport、图表绘制的QCustomPlot)在这个版本下都有现成的编译好的库,不用自己折腾源码。对于做工业控制和物联网设备的人来说,稳定压倒一切,没必要追新。
另外,5.10.1对电脑配置的要求也不高,一台普通的工控机、甚至虚拟机里跑都毫无压力。我见过不少现场的上位机还在用这个版本跑了好几年没出过问题。如果你也是做设备配套的软件,应该能理解这种“够用就行”的思路。
1.2 minGW5.10.1和MSVC到底选哪个
这个话题几乎每个用QT的人都会纠结。MSVC是微软的编译器,需要装Visual Studio,编译出的程序在Windows上性能好一点,但有个致命问题:发布的时候需要带一大堆VC运行库,而且不同版本的运行库还有冲突的可能。minGW是基于GCC的编译器,最大的优势是开源、免安装、绿色,编译出来的exe直接拷贝就能在别的Windows机器上跑,对于做设备配套软件这种需要频繁部署的场景来说太重要了。
用minGW还有一个隐性好处:如果你开发的是开源项目,用了GPL协议的库,编译器也是GCC系的,法律和许可证上更干净,不会有意无意的触碰到商业许可的边界。而且minGW版本的QT程序打个压缩包也就二三十兆,拷到设备现场就完事了,不用在用户电脑上装环境。
1.3 MQTT在项目里的角色
MQTT是一种基于发布/订阅模式的轻量级消息传输协议,非常适合物联网场景。比如你的设备通过ESP8266连接WiFi,然后把温湿度数据通过MQTT发布到服务器上,你的QT上位机只需要订阅对应的主题,就能实时收到数据。反过来,上位机也可以通过MQTT发指令,比如控制继电器的开关。
为什么不用HTTP?因为HTTP是请求/响应模式,设备端得不停地主动询问服务器有没有新指令,效率低还费流量。MQTT是长连接,服务器有消息会主动推给客户端,延迟低、省带宽,而且有QoS服务质量等级来保证消息不丢。如果你的设备经常掉线重连,MQTT的会话保持机制还能帮你把离线期间的消息保留下来,这点HTTP是做不到的。
2. 环境搭建和部署
2.1 解压后先检查目录结构
正常的minGW版本的QT目录结构是这样的:
- QT5.10.1文件夹(主程序)
- Tools(包含mingw530_32编译器)
- 5.10.1(QT库文件,里面是mingw53_32子目录)
拿到压缩包后先别急着用,按照这个结构核对一遍。如果解压后没有Tools目录,或者5.10.1下面没有mingw53_32子目录,那这个包很可能是用MSVC编译的,或者版本不匹配,勉强混用会导致后面编译报各种奇怪错误
2.2 编译QMqtt库,最关键的步骤
QT5.10.1官方安装包默认不包含MQTT模块,需要自己编译源码。如果不先编译这个库,你在工程里写#include <QMqttClient>的时候会直接报“找不到文件”。
具体步骤如下:
- 从QT官方仓库或者其他可信来源下载qtmqtt源码,注意选和QT5.10.1匹配的版本分支。
- 打开minGW提供的命令行工具。在开始菜单里找到“Qt 5.10.1 for Desktop (MinGW 5.3.0 32-bit)”,打开这个终端。
- 进入源码目录,依次执行:
qmake mingw32-make mingw32-make install这里的qmake和mingw32-make会自动匹配你当前使用的QT版本和编译器,不会搞混。编译过程大概要几分钟,等到命令行提示完成,MQTT模块就装好了,默认会装到QT的安装目录里
提示:不要在Windows自带的CMD里执行这些命令,必须用QT自带的minGW终端才能识别qmake和编译器路径。
2.3 验证环境是否就绪
装完之后别急着写代码,先建一个空白工程,在pro文件里加上QT += mqtt,然后写一行测试代码编译一下看看能不能过:
#include <QMqttClient> int main() { QMqttClient client; return 0; }如果编译通过,说明环境没问题。我见过很多朋友卡在这一步,问题往往出在编译器版本不匹配——比如QT是32位的minGW编译的,但你用的库是64位的,那链接的时候就找不到符号,报错内容还很绕,容易让人一头雾水。
3. 基于这套环境开发MQTT客户端
3.1 工程文件配置
新建QT Widgets Application工程,修改pro文件,加上MQTT模块和QT的网络模块:
QT += core gui widgets network mqtt TARGET = MqttClientDemo TEMPLATE = app CONFIG += c++11 SOURCES += main.cpp\ mainwindow.cpp HEADERS += mainwindow.h注意这里QT += mqtt是关键,如果没有提前编译好QMqtt库,这行会导致工程直接无法解析
3.2 最简连接、订阅和发布
mainwindow.h里声明一个QMqttClient对象,以及三个槽函数:
private slots: void onConnected(); void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic); void onStateChanged(QMqttClient::ClientState state);mainwindow.cpp里写连接逻辑:
#include "mainwindow.h" #include <QMqttClient> #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { m_client = new QMqttClient(this); m_client->setHostname("broker.emqx.io"); // 这里换成你的MQTT服务器地址 m_client->setPort(1883); m_client->setClientId("qt_client_001"); connect(m_client, &QMqttClient::connected, this, &MainWindow::onConnected); connect(m_client, &QMqttClient::messageReceived, this, &MainWindow::onMessageReceived); connect(m_client, &QMqttClient::stateChanged, this, &MainWindow::onStateChanged); m_client->connectToHost(); } void MainWindow::onConnected() { qDebug() << "连接成功"; m_client->subscribe(QMqttTopicFilter("sensor/data")); } void MainWindow::onMessageReceived(const QByteArray &message, const QMqttTopicName &topic) { qDebug() << "收到消息,主题:" << topic.name() << ",内容:" << message; } void MainWindow::onStateChanged(QMqttClient::ClientState state) { qDebug() << "客户端状态变化:" << state; }这是最核心的骨架代码了。连接、订阅、收消息都有了,如果要发布消息,调用m_client->publish(QMqttTopicName("control/cmd"), "ON")就行。
3.3 断线重连和心跳保持
实际项目里MQTT连接断开是家常便饭,尤其设备端用WiFi的话网络抖动更频繁。如果断线之后不重连,那这个上位机就变成“聋子”了。我在代码里加了定时器做重连,效果很稳定:
#include <QTimer> // 在构造函数里初始化定时器 m_reconnectTimer = new QTimer(this); m_reconnectTimer->setInterval(5000); // 5秒检测一次 connect(m_reconnectTimer, &QTimer::timeout, this, [this]() { if (m_client->state() == QMqttClient::Disconnected) { qDebug() << "检测到断开,正在重连..."; m_client->connectToHost(); } }); m_reconnectTimer->start();另外MQTT的PINGREQ心跳包机制可以保证连接不被服务器断开,如果我方没有消息发送,协议栈会自动发送PINGREQ维持连接。有时候服务器配置了较短的保活时间,就需要在connectToHost之前设置:
m_client->setKeepAlive(30); // 30秒心跳4. 常见问题和排查技巧
4.1 windows no qt platform plugin could be initialized
这个报错几乎每个用QT的都会遇到,最常见的原因是没有找到platforms插件目录里的qwindows.dll。解决方案有两种:
- 把QT安装目录下的
plugins\platforms文件夹整个拷贝到exe旁边,保证路径关系正确。 - 用官方自带的部署工具,命令行进入exe目录后执行:
C:\Qt\Qt5.10.1\5.10.1\mingw53_32\bin\windeployqt.exe 你的程序名.exe
执行完它会自动把需要的Qt库、插件、编译器运行库都拷过来,非常省心。注意一定要是用和你编译时同一个编译器对应的windeployqt,否则照样报错
4.2 编译时提示找不到QMqttClient文件
这个问题的根源就是MQTT库没编译好,或者编译了但路径不对。检查一下你的QT安装目录下的include文件夹里有没有QMqttClient目录,如果没有,说明编译没成功。另外看一下你用的编译器是32位还是64位,MQTT库必须和编译器位数一致
4.3 MQTT连接不稳定,收不到消息
先排除网络问题,再用MQTTX这个客户端工具连一下同一个服务器和主题,确认服务器本身是通的。如果别的客户端能收到,说明问题在自己的代码里。优先级排查顺序:
- 订阅的主题是否和发布端一致,写错一个字符都收不到
- QoS级别是否匹配,发布端用QoS2订阅端用QoS0会丢消息
- 是否在connected信号触发后才订阅,没连上就点subscribe是无效的
4.4 常见问题速查表
| 症状 | 原因 | 解决办法 |
|---|---|---|
| 代码编译不过 | MQTT模块没安装 | 重新编译qtmqtt并install |
| exe双击没反应 | 缺少QT运行库 | 用windeployqt部署 |
| 连接不上服务器 | 端口或地址错误 | 用MQTTX验证服务器地址 |
| 连一下就断开 | 心跳包时间太长 | 减小setKeepAlive数值 |
| 中文乱码 | 编译器和源码编码不一致 | 在pro文件加QMAKE_CXXFLAGS += -finput-charset=utf-8 -fexec-charset=gbk |
5. 一些实际操作中的心得
用这套组合做了两个月的项目,最大的体会是“版本固定”这四个字分量很重。QT5.10.1+MQTT+minGW5.10.1是一套完全能自洽的东西,你不用关心QT6的新特性、不用管MSVC的运行时库、不用担心mqtt库和编译器对不上,只需要专注于业务逻辑本身。
另外建议把编译好的MQTT库备份一下,下次换电脑直接拷过来就能用,不用重新编译。还有一点就是工程文件里的QT模块声明一定要和实际用到的库一致,多写了没关系,少写了一定报错。
如果项目里还要处理波形显示、时域频域转换,这套环境同样能很好支持。配合QCustomPlot和kissfft库,把接收到的传感器时域数据转成频谱图也很方便,等到做设备信号分析时再展开写。
最后再分享一个调试技巧:写MQTT程序时先用命令行版的mosquitto_sub订阅所有主题,然后运行你的上位机,看看它到底有没有发出正确的订阅/发布请求。有时候我们自己写的代码逻辑看着没问题,但实际上因为编码或者字符串格式的坑导致服务器根本没收到数据,这种情况下调试协议数据包往往能快速定位问题。
本文还有配套的精品资源,点击获取