当前位置:首页>Linux>基于libmodbus库-在Linux及ARM平台上实现modbusTCP通信,附带完整代码

基于libmodbus库-在Linux及ARM平台上实现modbusTCP通信,附带完整代码

  • 2026-10-11 07:45:30
基于libmodbus库-在Linux及ARM平台上实现modbusTCP通信,附带完整代码
为什么要使用libmodbus驱动库?

我们知道,Modbus 是一种总线协议,可以基于串口或网口进行通信。以基于串口的 Modbus-RTU 为例,如果想要在 Windows 或 Linux 系统下实现一个上位机,它的主要功能是读取或写入 Modbus 接口传感器设备的数据,或是与单片机等从设备进行交互。

举个例子:假如需要向某个从机的寄存器写入一个值——比如,向地址为01的设备、保持寄存器 0x0105 写入一个数据 0x0190——那么就需要构建这样一个数据帧:

主机发送: 01 06 01 05 01 90 99 CB
01表示从机地址,06功能码表示写单个保持寄存器,0105表示寄存器地址,0190表示写入寄存器的数值,99 CB为CRC校验值。
如果从机正确的收到了数据,会回复一个数据帧:
从机回复: 01 06 01 05 01 90 99 CB

本文就来介绍如何使用 libmodbus 这个库,实现Modbus主机与从机的通信。

libmodbus简介

libmodbus 是一个快速、跨平台的 Modbus 通信库,支持 RTU 和 TCP 协议,广泛应用于工业自动化和物联网设备的通信。以下是 libmodbus 的主要功能和使用方法。

核心功能概述

libmodbus 提供了与 Modbus 协议兼容的设备进行数据发送和接收的能力。支持通过串口(RTU 模式)或网络(TCP 模式)进行通信。其主要特点包括:

  • 支持多种操作系统,如 Linux、Windows、MacOS 等。

  • 提供统一的 API 接口,便于开发者快速集成。

  • 支持主机(Master)和从机(Slave)模式

官方网站:https://libmodbus.org/
开源地址:https://github.com/stephane/libmodbus/
libmodbus支持如下功能:
  • 支持Modbus-RTU和Modbus-TCP
  • 支持常用功能码,如01/02/03/04/05/06/07/0F/10/11/16/17
  • 支持线圈类型读写、寄存器读写、离散量读取等
  • 支持广播地址0,从机地址1-247
  • 支持浮点数和整形数据转换,大端小端等多种模式
  • 参数根据ModbusApplicationProtocolV11b.pdf官方标准文档设计,比如最大读写线圈个数,最大读写寄存器个数等。
  • 源代码基于C编写,方便在各平台移植,只有11个文件。
libmodbus常用函数
libmodbus库函数非常简洁,读写操作函数对于RTU和TCP完全通用,RTU和TCP切换只需要修改一行代码就可以实现无缝切换。
modbus_t *mb;int ret;//创建一个modbus-rtu对象,指定串口号,波特率,校验位,数据位,停止位//成功返回指针,否则返回NULL, 会调用malloc申请内存mb = modbus_new_rtu("/dev/ttySP1", 115200, 'N', 8, 1);	//linuxmb = modbus_new_rtu("COM1", 115200, 'N', 8, 1);		//windows//创建modbus-tcp对象,指定IP地址和端口号mb = modbus_new_tcp("127.0.0.1", 502);	//TCP/IP//设置从机地址,成功返回0, 否则返回-1ret = modbus_set_slave(mb, slave);//连接Modbus主机,成功返回0, 否则返回-1ret = modbus_connect(mb);//设置响应超时时间1s,200msret = modbus_set_response_timeout(mb, 1, 200000);//读取寄存器数据,起始地址2, 数量5, 保存到table数组中//成功返回5, 否则返回-1uint16_t *table;ret = modbus_read_registers(mb, 2, 5, table);//modbus设备关闭和释放内存modbus_close(mb);modbus_free(mb);//写单个寄存器, 地址2写入56, 成功返回1,否则返回-1ret = modbus_write_register(mb, 2, 56);//写多个寄存器, 地址12起始,写入5个数据,成功返回5,否则返回-1uint16_t table[5] = {11, 22, 33, 44, 55};ret = modbus_write_registers(mb, 12, 5, table);//写单个线圈,线圈地址写入TRUE,成功返回1,否则返回-1ret = modbus_write_bit(mb, 11, TRUE);//查看错误信息char *err_str;err_str = modbus_strerror(errno);
Linux平台下libmodbus使用
Ubuntu下使用libmodbus和Windows几乎一样:
//1.解压tar -zxvf libmodbus-3.0.6.tar.gz//2.配置./configure//3.编译make//4.安装make install
Linux系统可以使用 tar -zxvf libmodbus-3.0.6.tar.gz 命令行解压:
文档的介绍
src文件夹:
是libmodbus库源文件和头文件,我们只需要把这些文件添加到工程中,然后包含头文件就可以直接使用了。
tests文件夹
包含libmodbus使用示例。
包括Modbus-RTU/TCP客户端和服务器单元测试,随机测试,效率测试,读写10万个线圈状态,10万个寄存器,记录消耗时间。
2.使用万能的配置公式:
在  Linux 操作系统中,编译使用 libmodbus 库的第一步都是运行 configure 配置工具。该工具会自动检测当前系统的硬件架构、编译器特性和依赖库情况,进而生成与平台匹配的 config.h 头文件以及用于编译的 Makefile 文件。具体操作是进入 libmodbus 库的根目录,在终端中执行韦东山老师说的万能公式:
./configure --host= arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
3.编写测试代码
ModbusTCP服务端的代码,可供参考:
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<errno.h>#include<modbus/modbus.h>intmain(){    modbus_t *ctx;    modbus_mapping_t *mb_mapping;    int server_socket;    int client_socket;    uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];    int rc;    // 创建 Modbus TCP 上下文(监听所有接口,端口502)    ctx = modbus_new_tcp(NULL, 502);    if (ctx == NULL) {        fprintf(stderr, "无法创建 Modbus 上下文\n");        return -1;    }    // 创建数据映射(保持寄存器100个,其他按需)    mb_mapping = modbus_mapping_new(100, 100, 100, 100);    if (mb_mapping == NULL) {        fprintf(stderr, "无法创建数据映射\n");        modbus_free(ctx);        return -1;    }    // 主循环:持续接受客户端连接    while (1) {        // 1. 开始监听        server_socket = modbus_tcp_listen(ctx, 1);        if (server_socket == -1) {            fprintf(stderr, "监听失败: %s\n", modbus_strerror(errno));            break;        }        printf("Modbus TCP 服务器正在监听...\n");        // 2. 阻塞等待客户端连接        client_socket = modbus_tcp_accept(ctx, &server_socket);        if (client_socket == -1) {            fprintf(stderr, "接受连接失败: %s\n", modbus_strerror(errno));            close(server_socket);            continue;  // 跳过本次,重新监听        }        printf("客户端已连接!\n");        // 3. 处理该客户端的请求,直到连接断开        while (1) {            rc = modbus_receive(ctx, query);            if (rc > 0) {                // 模拟数据变化                mb_mapping->tab_registers[0]++;                modbus_reply(ctx, query, rc, mb_mapping);                printf("已处理一个请求,保持寄存器[0] = %d\n", mb_mapping->tab_registers[0]);            } else if (rc == -1) {                // 连接关闭或错误                fprintf(stderr, "连接关闭或错误: %s\n", modbus_strerror(errno));                break;  // 退出内层循环,准备接受新连接            }        }        // 4. 关闭客户端连接,释放本次通信资源        close(client_socket);        // modbus_tcp_accept 已将 ctx 绑定到该连接,需要关闭 modbus 连接以清理内部状态        modbus_close(ctx);    }    // 清理    modbus_mapping_free(mb_mapping);    modbus_free(ctx);    return 0;}
ModbusTCP客户端
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<errno.h>#include<modbus.h>intmain(){    modbus_t *ctx;    uint16_t tab_reg[32];    int rc;    // 1. 创建 Modbus TCP 上下文,请将 IP 改为你的服务器 IP    ctx = modbus_new_tcp("192.168.5.9", 502);   // 根据你的网络实际 IP 修改    if (ctx == NULL) {        fprintf(stderr, "无法创建 Modbus 上下文\n");        return -1;    }    // 可选:设置从站 ID,如果服务器要求特定的 ID(默认为 1)    // modbus_set_slave(ctx, 1);    // 2. 连接服务器    if (modbus_connect(ctx) == -1) {        fprintf(stderr, "连接失败: %s\n", modbus_strerror(errno));        modbus_free(ctx);        return -1;    }    printf("已连接到服务器,开始循环读取寄存器...\n");    // 3. 无限循环,持续读取寄存器    while (1) {        rc = modbus_read_registers(ctx, 0, 10, tab_reg);        if (rc == -1) {            fprintf(stderr, "读取寄存器失败: %s\n", modbus_strerror(errno));            break;  // 连接断开则退出循环        }        // 打印读取到的数据        printf("当前寄存器值: ");        for (int i = 0; i < 10; i++) {            printf("%d ", tab_reg[i]);        }        printf("\n");        // 每隔 1 秒读取一次        sleep(1);    }    // 4. 清理    modbus_close(ctx);    modbus_free(ctx);    return 0;}

测试结果如下:

如果需要测试Modbus-RTU,只需要修改modbus设备创建函数:
//modbus-rtumb = modbus_new_rtu(port, 115200, 'N', 8, 1);//modbus-tcpmb = modbus_new_tcp("127.0.0.120", 502);	//指定IP地址
其他无需任何改动!
ARM平台下libmodbus使用
ARM开发板下使用libmodbus,需要使用交叉编译器进行交叉编译,生成so库文件。
//1.解压:tar -zxvf libmodbus-3.0.6.tar.gz//2.创建安装目录:mkdir install//3.配置编译选项:./configure --host=arm-fsl-linux-gnueabi --enable-static --prefix=[安装路径]/install///4.编译:make//5.安装:make install
在install目录会生成3个文件夹:include lib share
进入install/lib目录,执行`file libmodbus*`,出现如下打印信息,信息中有“ARM”说明libmodbus库移植成功。
libmodbus.a:        current ar archivelibmodbus.la:       libtool library file, libmodbus.so:       symbolic link to `libmodbus.so.5.0.5'libmodbus.so.5:     symbolic link to `libmodbus.so.5.0.5'libmodbus.so.5.0.5: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, not stripped
将libmodbus.so、libmodbus.so.5、libmodbus.so.5.0.5复制到ARM开发板中的/usr/lib目录下
执行 
cp libmodbus.so* /usr/lib
如果出现无法创建的问题(cannot create ‘/usr/lib/libmodbus.so*’: Read-only file system)。
可以执行 
wr cp libmodbus* /usr/lib
测试与使用,和Windows一样,对测试文件使用ARM交叉编译器进行编译。
libmodbus 从机地址限制的问题
libmodbus支持1-247从机地址,0为广播地址,但是有些非标准的Modbus传感器,并不是采用0作为广播地址,而是0xfe作为广播地址:
所以使用libmodbus会出现报错终止运行的问题,这是因为libmodbus源代码中限制了从机地址1-247,我们只需要修改源代码即可。
modbus-rtu.c文件95行:
modbus-tcp.c文件80行:
只需要修改这两个数值就可以取消从机地址限制的问题。

最新文章

随机文章