libmodbus 在 Windows 平台上 Qt5 MinGW 中的测试

目录 Content
[hide]

一、目的

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 文件

libmodbus_msys_configure这是我的屏幕输出

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个

libmodbus_exe_1主要程序代码如下面所示,几乎就是照搬原po的:

#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,而测试结果错误!

mdslave_communication_traffic

a. 测试 RTU

先测试 RTU 功能,打开 modbus slave , connect setup 里设置  serial port ,并选择串口端口 com4,我这里使用 Virtual Serial Port Driver 建立一对虚拟串口,com4<->com3 互联。

msslave_RTU运行 test_libmodbus.exe ,点击 RTU 按钮,测试结果 OK!,读到10个寄存器

msslave_RTU_OK

b. 测试 TCP

再测试TCP功能,重新设置 modbus slave ,选择 TCP 方式

msslave_TCP同样测试OK!
msslave_TCP_OK

4. 源码下载

四、扩展阅读

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.