资讯详情

资讯详情

Windows下Libmodbus与Qt5 MinGW编译配置全攻略

简介基于 libmodbus 的 Windows 平台 Qt5 MinGW 测试代码包面向需要在 Qt 环境中调试 Modbus 通信协议、验证主站/从站交互的开发者尤其适合初次接触 libmodbus 移植与调用的工程师。工程参照经典 CSDN 教程整理包含可直接打开编译的 test_libmodbus 工程覆盖主程序入口、主窗口界面、库引用与构建配置并额外提供工程配置动态链接库及第三方依赖引用方式。资源共 21 个文件以 .h 头文件、.c 库源码、.cpp 业务逻辑、.ui 界面文件为主另含 .pro 工程文件、在线文档链接与说明文本压缩包仅 39KB轻量便于快速部署与二次修改。已有 978 人学习下载适合作为 libmodbus 在 MinGW 环境下的入门测试模板。通过该资源可掌握 Qt5 中加载第三方 C 库、配置动态链接库以及构建 Modbus 从站测试界面的基本方法同时了解下载资源中 dll 动态库与 url 快捷方式的使用意图节省自行搭建工程的时间。1. 我在 Windows 上编译 libmodbus Qt5 MinGW 踩过的坑别说你能一次过libmodbus 这个库做工业上位机的人基本都躲不开——Modbus RTU 走串口、Modbus TCP 走网口它都给你封装好了。但问题是官方文档和网上教程大多默认你在 Linux 上玩到了 Windows 平台尤其是配合 Qt5 的 MinGW 编译器画风就开始不对劲了。我见过有人卡在“找不到 libmodbus.dll”有人卡在“undefined reference to modbus_new_tcp”还有人干脆放弃 MinGW 转头去装 MSVC结果整个 Qt 工程跟着重来一遍。其实这套环境没你想得那么玄乎只要选对 libmodbus 的版本、搞懂 MinGW 的库命名规则、把搜索路径和链接顺序理顺半小时内就能把测试工程跑起来。这篇文章就是给你按着这个顺序拆开的装什么、怎么装、配什么参数、坏了怎么救。2. 前置准备version 选择比安装动作更决定成败2.1 libmodbus 版本差异为什么我推荐 v3.1.10 而不是 master 分支libmodbus 官方仓库的 master 分支一直在动最近几年主要在改 cmake 构建和修一些边角 bug但 Windows MinGW 场景下稳定压倒一切。我一般固定从 tag 拉版本常用的是 v3.1.10 和 v3.1.6。如果你项目里用的 Qt 是 5.15 系列MinGW 是 7.3 或 8.1v3.1.10 的 CMakeLists 已经写得比较干净能省掉不少手动改编译脚本的时间。如果是更早的 Qt 5.12、MinGW 5.3那老老实实选 v3.1.6 更稳。提示下载源码时别直接下 zip 后解压到带中文或空格的路径下比如C:\Users\某开发者\Desktop\新建文件夹\libmodbus-3.1.10。MinGW 的 make 和 cmake 碰到中文路径很容易报奇怪错误我踩过一次之后所有第三方库都统一放D:\libs\。版本选型的另一个维度是协议特性。v3.1.10 支持 Modbus RTU over TCP即MODBUS_RTU通过 TCP 传输但这个特性依赖用户自己封装调度库本身只提供基础帧解析。如果你的测试项目只用到基础的 TCP 读写保持寄存器那么 v3.1.10 和 v3.1.6 没有本质差别如果用到modbus_mapping_new_start_address这类接口v3.1.10 的参数更友好比如起始地址和数量分开传避免老版本那种总在地址偏移上算错的尴尬。2.2 Qt5 MinGW 环境核对你的编译器是 32 位还是 64 位这是最容易被忽略的一步。Qt 官方安装包里MinGW 版本分为mingw73_32和mingw73_64或者 Qt 5.15 带的mingw81_32/64。libmodbus 编译出来的库必须跟你的 Qt 编译器位数一致否则链接阶段直接报file not recognized。确认方法很简单g --version看输出里的Target: x86_64-w64-mingw32就是 64 位i686-w64-mingw32就是 32 位。然后在 Qt Creator 里检查“工具链”页面确认你选择的 Kit 对应的编译器路径和上面的g是同一个。我见过有人装了 32 位 Qt但命令行里 PATH 指向的是 64 位 MinGW结果 cmake 检测出 64 位 libmodbusQt 工程却用 32 位链接器最后折腾了整整一下午。另一个环境核对项是 CMake 版本。libmodbus v3.1.10 的 CMakeLists 要求 cmake 3.13 以上最低版本写的 3.13但 Qt 5.15 自带的 cmake 通常是 3.21没什么问题。如果你用 Qt 5.12自带 cmake 可能是 3.12那就会报CMake 3.13 or higher is required。这时候不用急着升级 Qt去下载一个独立版 cmake安装后把它路径放在 Qt 自带 cmake 前面即可。2.3 直接能用的环境配置清单我习惯先在笔记里列一份环境对照表装之前逐项打勾避免装到一半发现版本错配。这里给你一份可直接抄的清单组件推荐版本说明Qt5.15.2 或 5.12.125.15.2 的 MinGW 8.1 编译 libmodbus 更顺5.12 需注意 cmake 版本编译器套件MinGW 8.1.0 64-bit随 Qt 安装必须与 Qt Kit 一致CMake3.21 及以上如果 Qt 自带版本过低单独装一个libmodbus 源码v3.1.10优先 tag不追 master构建目录D:\libs\libmodbus\build源码和构建分开方便清缓存重来这份清单的目的不是列一堆东西给你压力而是让你在开始前就确认所有变量的取值。变量越早确定后面排错范围越小。我每次在新机器上搭这套环境光核对编译器位数就花了十五分钟但之后基本一路顺畅。3. 编译 libmodbus三种方式我推荐第一种但你必须知道第二种3.1 方式一CMake MinGW Makefiles 编译动态库这是我最常用、也最省心的方式。打开 MinGW 自带的命令行环境注意不是 cmd是“Qt 5.15.2 (MinGW 8.1.0 64-bit)”这个快捷方式然后依次执行cd D:\libs\libmodbus-3.1.10 mkdir build cd build cmake .. -G MinGW Makefiles -DCMAKE_BUILD_TYPERelease -DBUILD_SHARED_LIBSON -DCMAKE_INSTALL_PREFIXD:/libs/libmodbus-install mingw32-make -j8 mingw32-make install解释一下参数-G MinGW Makefiles是告诉 CMake 生成 MinGW 兼容的 Makefile不要在 Windows 上默认生成 Visual Studio 工程-DBUILD_SHARED_LIBSON让 libmodbus 编译成 DLL因为 Qt 程序运行时想动态加载省去把静态库一起编进 exe 的麻烦-DCMAKE_INSTALL_PREFIX指定安装目录这样最后头文件和库文件会统一放到指定文件夹找起来方便。编译过程中如果看到undefined reference to WSAGetLastError0之类的链接错误别慌这是因为 libmodbus 的 TCP 部分依赖 Windows 的 Winsock2 库CMake 有些版本没有自动帮你加上ws2_32。解决方法是在 cmake 命令里追加一行-DCMAKE_C_FLAGS-lws2_32这个坑在 v3.1.6 上比较常见v3.1.10 已经修正了但环境千差万别万一碰上就知道是哪里出了问题。3.2 方式二用 Qt 的 qmake 直接编 libmodbus适合临时调试如果你不想单独装 CMake而 Qt 自带的环境里刚好有 qmake可以进到 libmodbus 源码目录里试一下。但注意官方 CMakeLists 并没有提供.pro文件qmake 只能编译 Qt 工程没法直接给纯 C 库做构建。所以这种方式其实是先构造一个临时.pro文件再让它调用源码里所有.c文件。操作如下cd D:\libs\libmodbus-3.1.10 echo TEMPLATE lib libmodbus_temp.pro echo CONFIG dll libmodbus_temp.pro echo TARGET modbus libmodbus_temp.pro echo SOURCES src/*.c libmodbus_temp.pro echo HEADERS src/*.h libmodbus_temp.pro qmake libmodbus_temp.pro mingw32-make这个方式能出库但生成的 DLL 可能缺少导出符号表链接时照样找不到函数。原因在于 libmodbus 源码里用了LIBMODBUS_API宏做导出声明而 qmake 默认的DEF_FILE没有生成.def文件MinGW 链接器在导出符号时全靠__declspec(dllexport)。如果你发现编出的 DLL 里没有modbus_new_tcp这些导出符号建议回头用 CMake 方式。我的实践里方式二只用来验证源码能不能在当前编译器下通过语法编译真正交付还是走方式一。3.3 静态库编译什么时候值得选项目里的程序如果要在客户机器上免安装跑那 DLL 拷贝麻烦不如直接静态链接。静态库编译只需要把上一节的-DBUILD_SHARED_LIBSON改成-DBUILD_SHARED_LIBSOFF或者干脆不传这个参数默认就是静态。但静态链接 libmodbus 有个坑它默认没有定义WIN32宏某些头文件里的结构体对齐方式会和 Qt 编译出的目标文件不一致偶尔会报莫名其妙的指针错位。我的经验是凡是静态链接都在 CMake 命令里增加-DCMAKE_C_FLAGS-DWIN32 -D_WIN32_WINNT0x0601_WIN32_WINNT0x0601是对应 Windows 7 及以上系统的 API 版本号保证 Winsock 函数声明正确。你如果目标平台是 Win10改成0x0A00也行但0x0601的兼容面更广。4. 配置 Qt5 工程链接 libmodbus 的三个关键点4.1 在 .pro 文件里写对 INCLUDEPATH 和 LIBS拿到编译好的 libmodbus 后在 Qt 工程里配置就简单了但细节却很致命。先看一段标准的.pro片段QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET modbus_test TEMPLATE app INCLUDEPATH D:/libs/libmodbus-install/include/modbus LIBS -LD:/libs/libmodbus-install/lib -lmodbus SOURCES main.cpp \ mainwindow.cpp这里INCLUDEPATH指向的路径必须具体到include/modbus因为 libmodbus 安装时头文件被放在这个子目录里。你 include 的时候写的是#include modbus.h但实际上文件全名是modbus/modbus.h编译器搜索头文件的逻辑是include路径 尖括号里的路径所以如果INCLUDEPATH指到include层就得写#include modbus/modbus.h。两种写法人人都能编译过但关键是别混用。LIBS这里的-lmodbus是让 MinGW 链接器去找libmodbus.dll.a动态导入库或libmodbus.a静态库。如果你之前 CMake 安装时生成的文件名是libmodbus.dll.a那-lmodbus能识别如果你的库名带了版本号比如libmodbus-5.dll.a那就要写-lmodbus-5。我建议在编译 libmodbus 之后进入安装目录的lib文件夹里看一眼实际文件名再决定-l后面写什么。这一步花不了半分钟但填错的话链接报错信息会让你怀疑人生。4.2 运行时 DLL 拷贝别拖到程序发布时才想起Qt Creator 里直接运行时它会通过 PATH 找modbus.dll但默认 PATH 里并没有你的 libmodbus 安装目录。运行报错长这样程序无法启动因为计算机中丢失 modbus.dll。解决办法有两个我同时用先测试阶段把D:/libs/libmodbus-install/bin/modbus.dll拷贝到 Qt Creator 构建输出目录通常是build-xxx-Desktop_Qt_5_15_2_MinGW_64_bit-Debug下和 exe 放一起。然后在项目里启用 shadow build 时注意构建目录不是固定不变的我习惯在.pro里写一条自定义命令QMAKE_POST_LINK $$quote(copy /Y D:\\libs\\libmodbus-install\\bin\\modbus.dll $$OUT_PWD\\modbus.dll)这行的意思是编译成功后自动把 DLL 复制到当前构建目录。注意$$quote保留了反斜杠的转义直接写普通字符串的话大概率路径解析出错。部署时用 Qt 自带的windeployqt工具自动收集 Qt 依赖而 libmodbus 的 DLL 仍然需要手动拷贝或者写进安装脚本。4.3 链接顺序谁说 C 库可以随便放在 qmake 的.pro文件里LIBS顺序一般不需要过度讲究因为 qmake 会自动把库放到目标文件后面。但如果你在代码里自定义了类封装了 libmodbus而且这个类又依赖另一个库那链接顺序就可能出错。我遇到一个场景某个模拟项目X里自定义的ModbusWorker类调用了 libmodbus 函数而ModbusWorker本身被放在一个单独的静态库里结果链接时报undefined reference to modbus_new_tcp原因是ModbusWorker所在库位于-lmodbus前面而老版本 MinGW 链接器是单遍扫描。解决办法是把-lmodbus提到所有自定义库之后或者用-Wl,--start-group和-Wl,--end-group包住所有库LIBS -Wl,--start-group -lmodbus -lModbusWorker -Wl,--end-group这类问题在新版 MinGW 里很少见但只要你的测试工程引入了自定义静态库就要留个心眼。我一般在LIBS配置后面加注释写明每个库的依赖关系防止自己过两周忘了。5. 编译与运行常见问题排查我踩过的四个最坑记录5.1 现象fatal error: modbus.h: No such file or directory头文件找不到的原因通常有两种一是INCLUDEPATH路径写错二是头文件安装目录和你预期不一致。我之前把D:/libs/libmodbus-install/include/modbus错写成了D:/libs/libmodbus-install/include结果编译器找的是include/modbus.h而实际文件在include/modbus/modbus.h。解决方式是先打开安装目录看一眼ls D:/libs/libmodbus-install/include/modbus如果里面是modbus.h、modbus-version.h等文件说明INCLUDEPATH要指向这个目录然后在代码里写#include modbus.h如果安装目录长的是include/modbus/modbus.h那INCLUDEPATH指到include层代码里写#include modbus/modbus.h。两个方案选一个别混。5.2 现象undefined reference to modbus_xxx并伴随大量__imp_前缀这一看就是链接器找到了库文件但没找到对应的导出符号。原因通常是库编译时用的是 32 位模式而你的 Qt Kit 是 64 位符号名不匹配。用nm工具看一下导入库nm D:/libs/libmodbus-install/lib/libmodbus.dll.a | grep modbus_new_tcp如果输出的是T modbus_new_tcp却没有任何__imp_前缀说明这是静态符号表不是导入符号表说明你编译出来的 DLL 导出符号缺失。解决方式最简单的是重新用 CMake 编译确保BUILD_SHARED_LIBSON且没有手动覆盖CMAKE_C_FLAGS里的-DBUILD_SHARED_LIBS宏。还有一种情况是libmodbus.dll.a已经被某个杀毒软件隔离了路径上明明有文件但大小为 0链接器读不到任何符号此时重新编译并关闭实时防护就能解决。5.3 现象程序启动后modbus_connect返回 -1errno是WSAECONNREFUSED这个不算编译期问题但我测试时至少碰到三次。Qt 里初始化 TCP 连接时libmodbus 内部用 Winsock 的connect函数。如果目标 Modbus 从站没启动或者端口不对返回 -1 是正常的但排查时容易怀疑是不是协议栈的问题。我的习惯是在代码里把errno打印出来查strerrorint rc modbus_connect(ctx); if (rc -1) { qDebug() connect failed, errno: errno strerror(errno); }如果是WSAECONNREFUSED说明 TCP 端口根本没服务不是你库的问题如果是WSAETIMEDOUT说明网络不通或防火墙挡了如果errno是 0那反而要怀疑是不是modbus_new_tcp返回的 ctx 本身就是空指针导致后续所有操作都无效。5.4 现象Qt 程序里调用modbus_read_registers时结构体对齐导致读出的数据全是 0这个问题藏得很深我一开始完全没往编译器选项上想。libmodbus 内部定义了一些 packing 的结构体比如modbus_tcp的私有数据在默认对齐方式下某些字段会错位。MinGW 默认对齐方式与 MSVC 不同所以同一个库在 MSVC 下编出的目标文件能工作MinGW 下可能就不正常。解决方式是在编译 libmodbus 的 CMake 命令里添加对齐参数-DCMAKE_C_FLAGS-fpack-struct8-fpack-struct8让结构体紧凑对齐到 8 字节边界和 Windows API 常用声明的匹配度更高。不过加了它之后libmodbus 与 Qt 之间传递的自定义结构体也需要保持一致的编译选项否则又可能引入新错位。我的建议是如果测试程序只是通过modbus_read_registers读uint16_t数组不用传结构体那别折腾这个参数如果你自定义了从站回调处理大量字节 buffer再考虑对齐问题。6. 最后一并验证用 Qt 自带串口工具和模拟从站跑通一个测试场景环境全配好后我再检查一遍。首先验证库能链接建一个最简控制台工程只有main.cpp代码就做三件事创建 TCP 上下文、连接、读寄存器。然后打印结果。下列代码的核心部分#include QCoreApplication #include modbus.h #include cstdio int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); modbus_t *ctx modbus_new_tcp(127.0.0.1, 502); if (ctx nullptr) { qCritical(modbus_new_tcp failed); return 1; } if (modbus_connect(ctx) -1) { qCritical(modbus_connect failed, errno%d, errno); modbus_free(ctx); return 1; } uint16_t regs[10] {0}; int rc modbus_read_registers(ctx, 0, 10, regs); if (rc -1) { qCritical(read failed, errno%d, errno); modbus_close(ctx); modbus_free(ctx); return 1; } printf(read %d registers\n, rc); for (int i 0; i rc; i) { printf(reg[%d]0x%04X\n, i, regs[i]); } modbus_close(ctx); modbus_free(ctx); return 0; }这段代码里用到的modbus_new_tcp(127.0.0.1, 502)第一个参数是 IP 字符串第二个是端口modbus_read_registers三个参数分别是从站起始地址、寄存器数量、目标 buffer。编译前确保.pro里没有把modbus.h的路径写错。要跑通这个验证你还得有个 Modbus 从站模拟器。我用的还是那个模拟项目X里的 python 脚本但更轻量的办法是用pymodbus起一个调试服务。命令行下跑python -m pymodbus.server --port 502 --unit-id 1或者如果端口被占用换 1502然后把程序里的端口改成 1502。跑起来后再执行你的 Qt 程序如果输出read 10 registers并打印出非 0 数据说明全套环境没白搭。验证过程中还有一个心得如果你在 Qt Creator 里直接点运行右下角“应用程序输出”窗口如果出现Press RETURN to close this window说明程序是正常退出的如果什么输出都没有就闪退就去“编译输出”看完整消息通常最后一行就是原因。从那以后我每次新装完一套 libmodbus Qt 环境都强制走一遍这个流程先编译库里自带的unit-test示例再跑自己这个最小读取程序全部通过才开始写业务代码。这个习惯让我少踩了很多“以为环境没问题最后发现是库少编了一次”的坑。希望帮到你按这个流程走Windows 上的 libmodbus 测试环境理论上半小时内能跑通。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →