一、目的
modbus 是常见的工业通讯协议,几乎被所有的设备支持。如果能在软件中增加 modbus通讯功能,无疑对于应用来说是个很吸引人的卖点。
通过这篇文章,可以了解到 modbus 的概念和调试环境的搭建,以及 libmodbus 的在 Qt 中 使用。
二、libmodbus 库
libmodbus 是一个免费的跨平台的支持 RTU 和 TCP 的 Modbus 库,遵循 LGPL v2.1+ 协议。官方网站 http://libmodbus.org/ ,现在稳定的版本是 v3.0.6 ,可以从 http://libmodbus.org/download/ 下载。
从 Github 可以获取最新版本:
三、在 Qt5 MinGW 中测试 libmodbus
由于第一次使用,参照别人的教程比较靠谱,参见 http://blog.csdn.net/zgrjkflmkyc/article/details/44855543
1. 使用 msys 生成 config.h 文件
这个需要使用 MinGW 这个工具。进入源码目录后运行 ./configure 会生成 config.h 文件
liangtao@RRCnSERLt /c/Users/liangtao/Downloads/libmodbus-3.0.6 $ ./configure checking for a BSD-compatible install... /bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking whether make supports nested variables... yes checking whether make supports nested variables... (cached) yes checking build system type... i686-pc-mingw32 checking host system type... i686-pc-mingw32 checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.exe checking for suffix of executables... .exe checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed checking whether gcc understands -c and -o together... yes checking for style of include used by make... GNU checking dependency style of gcc... gcc3 checking for g++... g++ checking whether we are using the GNU C++ compiler... yes checking whether g++ accepts -g... yes checking dependency style of g++... gcc3 checking whether make sets $(MAKE)... (cached) yes checking how to print strings... printf checking for a sed that does not truncate output... /bin/sed checking for grep that handles long lines and -e... /bin/grep checking for egrep... /bin/grep -E checking for fgrep... /bin/grep -F checking for ld used by gcc... /bin/ld checking if the linker (/bin/ld) is GNU ld... yes checking for BSD- or MS-compatible name lister (nm)... /bin/nm -B checking the name lister (/bin/nm -B) interface... BSD nm checking whether ln -s works... no, using cp -pR checking the maximum length of command line arguments... 8192 checking whether the shell understands some XSI constructs... yes checking whether the shell understands "+="... yes checking how to convert i686-pc-mingw32 file names to i686-pc-mingw32 format... func_convert_file_msys_to_w32 checking how to convert i686-pc-mingw32 file names to toolchain format... func_convert_file_msys_to_w32 checking for /bin/ld option to reload object files... -r checking for objdump... objdump checking how to recognize dependent libraries... file_magic ^x86 archive import|^x86 DLL checking for dlltool... dlltool checking how to associate runtime and link libraries... func_cygming_dll_for_implib checking for ar... ar checking for archiver @FILE support... @ checking for strip... strip checking for ranlib... ranlib checking command to parse /bin/nm -B output from gcc object... ok checking for sysroot... no checking for mt... no checking if : is a manifest tool... no checking how to run the C preprocessor... gcc -E checking for ANSI C header files... yes checking for sys/types.h... yes checking for sys/stat.h... yes checking for stdlib.h... yes checking for string.h... yes checking for memory.h... yes checking for strings.h... yes checking for inttypes.h... no checking for stdint.h... no checking for unistd.h... yes checking for dlfcn.h... yes checking for as... as checking for dlltool... (cached) dlltool checking for objdump... (cached) objdump checking for objdir... .libs checking if gcc supports -fno-rtti -fno-exceptions... no checking for gcc option to produce PIC... -DDLL_EXPORT -DPIC checking if gcc PIC flag -DDLL_EXPORT -DPIC works... yes checking if gcc static flag -static works... no checking if gcc supports -c -o file.o... yes checking if gcc supports -c -o file.o... (cached) yes checking whether the gcc linker (/bin/ld) supports shared libraries... yes checking whether -lc should be explicitly linked in... yes checking dynamic linker characteristics... Win32 ld.exe checking how to hardcode library paths into programs... immediate checking whether stripping libraries is possible... yes checking if libtool supports shared libraries... yes checking whether to build shared libraries... yes checking whether to build static libraries... no checking how to run the C++ preprocessor... g++ -E checking for ld used by g++... /bin/ld checking if the linker (/bin/ld) is GNU ld... yes checking whether the g++ linker (/bin/ld) supports shared libraries... yes checking for g++ option to produce PIC... -DDLL_EXPORT -DPIC checking if g++ PIC flag -DDLL_EXPORT -DPIC works... yes checking if g++ static flag -static works... no checking if g++ supports -c -o file.o... yes checking if g++ supports -c -o file.o... (cached) yes checking whether the g++ linker (/bin/ld) supports shared libraries... yes checking dynamic linker characteristics... Win32 ld.exe checking how to hardcode library paths into programs... immediate checking termios.h usability... yes checking termios.h presence... yes checking for termios.h... yes checking sys/time.h usability... yes checking sys/time.h presence... yes checking for sys/time.h... yes checking time.h usability... yes checking time.h presence... yes checking for time.h... yes checking for unistd.h... (cached) yes checking errno.h usability... yes checking errno.h presence... yes checking for errno.h... yes checking limits.h usability... yes checking limits.h presence... yes checking for limits.h... yes checking fcntl.h usability... yes checking fcntl.h presence... yes checking for fcntl.h... yes checking for sys/types.h... (cached) yes checking sys/socket.h usability... yes checking sys/socket.h presence... yes checking for sys/socket.h... yes checking sys/ioctl.h usability... yes checking sys/ioctl.h presence... yes checking for sys/ioctl.h... yes checking netinet/in.h usability... yes checking netinet/in.h presence... yes checking for netinet/in.h... yes checking netinet/tcp.h usability... yes checking netinet/tcp.h presence... yes checking for netinet/tcp.h... yes checking arpa/inet.h usability... yes checking arpa/inet.h presence... yes checking for arpa/inet.h... yes checking netdb.h usability... yes checking netdb.h presence... yes checking for netdb.h... yes checking linux/serial.h usability... no checking linux/serial.h presence... no checking for linux/serial.h... no checking for asciidoc... no checking for xmlto... no checking whether to build documentation... no checking whether to install manpages... yes checking for ANSI C header files... (cached) yes checking for an ANSI C-conforming const... yes checking for size_t... yes checking whether time.h and sys/time.h may both be included... yes checking whether __CYGWIN__ is declared... yes checking for pid_t... yes checking vfork.h usability... no checking vfork.h presence... no checking for vfork.h... no checking for fork... yes checking for vfork... yes checking for working fork... yes checking for working vfork... (cached) yes checking for gettimeofday... yes checking for inet_ntoa... yes checking for memset... yes checking for select... yes checking for socket... yes checking for strerror... yes checking for strlcpy... no checking for getaddrinfo... no checking winsock2.h usability... no checking winsock2.h presence... yes configure: WARNING: winsock2.h: present but cannot be compiled configure: WARNING: winsock2.h: check for missing prerequisite headers? configure: WARNING: winsock2.h: see the Autoconf documentation configure: WARNING: winsock2.h: section "Present But Cannot Be Compiled" configure: WARNING: winsock2.h: proceeding with the compiler's result configure: WARNING: ## ----------------------------------------------------------- ## configure: WARNING: ## Report this to https://github.com/stephane/libmodbus/issues ## configure: WARNING: ## ----------------------------------------------------------- ## checking for winsock2.h... no checking whether TIOCSRS485 is declared... no checking that generated files are newer than configure... done configure: creating ./config.status config.status: creating Makefile config.status: creating src/Makefile config.status: creating src/modbus-version.h config.status: creating doc/Makefile config.status: creating tests/Makefile config.status: creating libmodbus.pc config.status: creating libmodbus.spec config.status: creating config.h config.status: creating tests/unit-test.h config.status: executing depfiles commands config.status: executing libtool commands liangtao@RRCnSERLt /c/Users/liangtao/Downloads/libmodbus-3.0.6
这样,得到了生成的 config.h 文件。
2. 创建 Qt5 项目 test_libmodbus
建立一个新的 widget 项目,并在项目目录里建立一个文件夹,起名为 libmodbus, 将前面生成的 config.h 以及 src 目录里面的 .h .c 文件都拷贝到 libmodbus 里。再建立一个文件夹为 dll ,按照原po的说法,此库要调用 ws2_32.dll 文件,从 c:\Windows\System32\ 目录里搜索到此文件,并拷贝到文件夹 dll 里面,但似乎这个dll不拷贝也可以。
将 libmodbus 内的文件添加进项目,修改 modbus.c 文件第 30 行 #include <config.h> 为 #include <libmodbus/config.h>,修改 modbus-private.h 第31行 #include <config.h> 为 #include <libmodbus/config.h>。修改 .pro 文件,添加 LIBS += -Ldll -lws2_32。修改完成后,重新运行 qmake。
UI 上添加两个按钮,并且添加点击事件。(注意按钮上标的20个,但实际代码中只读取10个,20是 LT 参照原po时写的,后面改为了10个)
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QMessageBox> #include "libmodbus/modbus.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_clicked() { //RTU modbus_t *mb; uint16_t tab_reg[32]={0}; mb = modbus_new_rtu("COM3", 9600, 'N', 8, 1); //相同的端口只能同时打开一个 modbus_set_slave(mb, 1); //设置modbus从机地址 modbus_connect(mb); struct timeval t; t.tv_sec=0; t.tv_usec=1000000; //设置modbus超时时间为1000毫秒 modbus_set_response_timeout(mb, &t); int regs=modbus_read_registers(mb, 0, 10, tab_reg); QMessageBox::about(NULL, "报告", QString("RTU 读取寄存器的个数: %1").arg(regs)); modbus_close(mb); modbus_free(mb); } void MainWindow::on_pushButton_2_clicked() { //TCP modbus_t *mb; uint16_t tab_reg[32]={0}; mb = modbus_new_tcp("127.0.0.1", 502); //由于是tcp client连接,在同一个程序中相同的端口可以连接多次。 modbus_set_slave(mb, 1); //从机地址 modbus_connect(mb); struct timeval t; t.tv_sec=0; t.tv_usec=1000000; //设置modbus超时时间为1000毫秒,注意:经测试,如果没有成功建立tcp连接,则该设置无效。 modbus_set_response_timeout(mb, &t); int regs=modbus_read_registers(mb, 0, 10, tab_reg); QMessageBox::about(NULL, "报告", QString("TCP 读取寄存器的个数: %1").arg(regs)); modbus_close(mb); modbus_free(mb); }
其中
mb = modbus_new_rtu("COM3", 9600, 'N', 8, 1);
- 要根据实际的串口端口号来定,我这里采用 com3。
mb = modbus_new_tcp("127.0.0.1", 502);
- 这里填入从机的IP地址和端口号
int regs=modbus_read_registers(mb, 0, 10, tab_reg);
- 表示读取 10 个寄存器数值。
然后编译完成生成 test_libmodbus.exe ,并使用 windeployqt (参照 “Windows 平台发布 Qt5 应用程序”) 将需要的dll 补充完整。
3. 测试 modbus
前面得到的 test_libmodbus.exe 是一个 modbus 主机,需要一个从机来进行测试。使用 Modbus Slave 可以满足我们的需求, 可以从 http://www.modbustools.com/modbus_slave.html 下载到。
测试的时候,需要把 modbus slave 的监控功能打开,这样容易发现问题,LT才开始就是因为照抄原po的20,而 modbus slave中只设置了10,而测试结果错误!
a. 测试 RTU
先测试 RTU 功能,打开 modbus slave , connect setup 里设置 serial port ,并选择串口端口 com4,我这里使用 Virtual Serial Port Driver 建立一对虚拟串口,com4<->com3 互联。
运行 test_libmodbus.exe ,点击 RTU 按钮,测试结果 OK!,读到10个寄存器
b. 测试 TCP
再测试TCP功能,重新设置 modbus slave ,选择 TCP 方式
4. 源码下载
四、扩展阅读
- Simply Modbus
http://www.simplymodbus.ca/index.html - Modbus协议
http://blog.csdn.net/wangshunli/article/details/7542661 - QModMaster
http://sourceforge.net/projects/qmodmaster/ - MODBUS学习笔记——modbus tk modbus TCP主机实现
http://blog.csdn.net/xukai871105/article/details/21884065 - Modbus测试工具 :Modbus Poll,Modbus Slave
http://blog.chinaunix.net/uid-20620288-id-3207068.html - 我的Modbus Slave/Client开发历程(Rtu/AscII/Tcp)
http://blog.csdn.net/wangshunli/article/details/7530825 - MODBUS on the Pi Part 1, compiling a basic example
http://www.ostafichuk.com/raspberry-pi-projects/modbus-on-the-pi/modbus-on-the-pi-part-1-compiling-a-basic-example/ - qtLibModbus
https://github.com/Krzysztow/qtLibModbus - ModbusTCP Master/Client – EtherCAT Slave
http://www.anybus.com/products/abxmtcp.shtml
http://www.anybus.com/products/ethercat.shtml - 互联网与工业自动化总线的接口modbus
http://blog.lxztx.com/post/hu-lian-wang-yu-gong-ye-zi-dong-hua-zong-xian-de-jie-kou-modbus - Qt+MSVC使用libmodbus实现modbus主机功能
http://blog.csdn.net/zgrjkflmkyc/article/details/52745229