RTU_ALL_AI/docs/protocol/libmodbus/API-libmodbus.md

9.7 KiB
Raw Blame History

API-libmodbus — Modbus 协议栈模块

版本: v1.0
日期: 2026-07-08
来源: libmodbus v3.1.12LGPL-2.1-or-later
状态: 导入完成(开源库,未重构)
修改: 日志输出改为调用 myLog.h LOG_I/LOG_E


1. 模块概述

libmodbus 是工业标准 Modbus 协议的开源实现(libmodbus.org),支持:

功能 说明
Modbus RTU串行 RS-232 / RS-485CRC 校验
Modbus TCP以太网 TCP/IPMBAP 报文头
主站 / 从站 同一套 API 支持主站请求和从站响应
全功能码覆盖 读线圈/读离散量/读保持寄存器/读输入寄存器/写单/写多/掩码/读写组合

版本信息

libmodbus v3.1.12
Copyright © Stéphane Raimbault
SPDX-License-Identifier: LGPL-2.1-or-later

项目适配

原 RTU 项目对 libmodbus 做了两处适配:

  1. 日志系统:源码中 fprintf(stderr, ...)LOG_I / LOG_E(调用项目 liblog 模块)
  2. config.h:配置头文件,启用 HAVE_DECL_TIOCSRS485 等宏

2. 源文件结构

libmodbus/
├── inc/
│   ├── modbus.h              # 主接口 — API、错误码、功能码、数据结构
│   ├── modbus-rtu.h          # RTU 后端接口 — 创建/串口参数/RTS
│   ├── modbus-tcp.h          # TCP 后端接口 — 创建/listen/accept
│   ├── modbus-version.h      # 版本号宏
│   ├── modbus-private.h      # 内部数据结构(不对外暴露)
│   ├── modbus-rtu-private.h  # RTU 后端内部数据
│   ├── modbus-tcp-private.h  # TCP 后端内部数据
│   └── config.h              # 平台配置宏
└── src/
    ├── modbus.c              # 主协议逻辑 — 帧构建/解析/收发
    ├── modbus-data.c         # 位/寄存器数据转换工具
    ├── modbus-rtu.c          # RTU 后端 — 串口读写 + CRC
    └── modbus-tcp.c          # TCP 后端 — socket 读写 + MBAP

3. 核心数据结构

3.1 modbus_t不透明上下文句柄

typedef struct _modbus modbus_t;

通过 modbus_new_rtu()modbus_new_tcp() 创建,所有 API 的第一个参数。

3.2 数据映射表

typedef struct _modbus_mapping_t {
    int nb_bits;                  // Coil 数量
    int start_bits;               // Coil 起始地址
    int nb_input_bits;            // 离散输入数量
    int start_input_bits;         // 离散输入起始地址
    int nb_input_registers;       // 输入寄存器数量
    int start_input_registers;    // 输入寄存器起始地址
    int nb_registers;             // 保持寄存器数量
    int start_registers;          // 保持寄存器起始地址
    uint8_t  *tab_bits;           // Coil 数组
    uint8_t  *tab_input_bits;     // 离散输入数组
    uint16_t *tab_input_registers;// 输入寄存器数组
    uint16_t *tab_registers;      // 保持寄存器数组
} modbus_mapping_t;

3.3 功能码

说明
MODBUS_FC_READ_COILS 0x01 读线圈
MODBUS_FC_READ_DISCRETE_INPUTS 0x02 读离散输入
MODBUS_FC_READ_HOLDING_REGISTERS 0x03 读保持寄存器
MODBUS_FC_READ_INPUT_REGISTERS 0x04 读输入寄存器
MODBUS_FC_WRITE_SINGLE_COIL 0x05 写单线圈
MODBUS_FC_WRITE_SINGLE_REGISTER 0x06 写单寄存器
MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F 写多线圈
MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 写多寄存器
MODBUS_FC_MASK_WRITE_REGISTER 0x16 掩码写寄存器
MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17 读写多寄存器

3.4 协议异常码

错误宏 说明
EMBXILFUN 非法功能码
EMBXILADD 非法数据地址
EMBXILVAL 非法数据值
EMBXSFAIL 从站设备故障
EMBXACK 确认
EMBXSBUSY 从站繁忙
EMBMDATA 数据太多(本地错误)
EMBBADCRC CRC 校验失败

4. 公共 API 接口

4.1 创建与销毁

/* RTU 模式 */
modbus_t *modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);

/* TCP 模式 */
modbus_t *modbus_new_tcp(const char *ip_address, int port);

/* TCP PI协议无关支持 IPv4/IPv6 */
modbus_t *modbus_new_tcp_pi(const char *node, const char *service);

/* 释放资源 */
void modbus_free(modbus_t *ctx);

4.2 连接管理

/* 建立连接 */
int modbus_connect(modbus_t *ctx);

/* 关闭连接 */
void modbus_close(modbus_t *ctx);

/* 清空缓冲区 */
int modbus_flush(modbus_t *ctx);

/* TCP 服务器端: 监听 */
int modbus_tcp_listen(modbus_t *ctx, int nb_connection);

/* TCP 服务器端: accept */
int modbus_tcp_accept(modbus_t *ctx, int *s);

4.3 参数配置

/* 从站地址 */
int modbus_set_slave(modbus_t *ctx, int slave);
int modbus_get_slave(modbus_t *ctx);

/* 响应超时(秒 + 微秒) */
int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);

/* 字节间超时 */
int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);

/* 错误恢复模式 */
int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode mode);

/* 调试开关 */
int modbus_set_debug(modbus_t *ctx, int flag);

/* 错误消息 */
const char *modbus_strerror(int errnum);

4.4 数据读写(主站模式)

/* 读线圈 0x01 */
int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);

/* 读离散输入 0x02 */
int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);

/* 读保持寄存器 0x03 */
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);

/* 读输入寄存器 0x04 */
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);

/* 写单线圈 0x05 */
int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);

/* 写单寄存器 0x06 */
int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);

/* 写多线圈 0x0F */
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);

/* 写多寄存器 0x10 */
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);

/* 掩码写寄存器 0x16 */
int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);

/* 读写多寄存器 0x17 */
int modbus_write_and_read_registers(modbus_t *ctx,
    int write_addr, int write_nb, const uint16_t *src,
    int read_addr, int read_nb, uint16_t *dest);

4.5 从站模式

/* 创建数据映射表 */
modbus_mapping_t *modbus_mapping_new(int nb_bits, int nb_input_bits,
    int nb_registers, int nb_input_registers);

/* 创建带起始地址的映射表 */
modbus_mapping_t *modbus_mapping_new_start_address(
    unsigned int start_bits, unsigned int nb_bits,
    unsigned int start_input_bits, unsigned int nb_input_bits,
    unsigned int start_registers, unsigned int nb_registers,
    unsigned int start_input_registers, unsigned int nb_input_registers);

/* 释放映射表 */
void modbus_mapping_free(modbus_mapping_t *mb_mapping);

/* 接收请求(阻塞) */
int modbus_receive(modbus_t *ctx, uint8_t *req);

/* 从映射表自动回复 */
int modbus_reply(modbus_t *ctx, const uint8_t *req, int req_length, modbus_mapping_t *mb_mapping);

/* 手动回复异常 */
int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code);

4.6 原始帧操作

/* 发送原始请求帧 */
int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length);

/* 带事务 ID 的原始请求TCP 多主站) */
int modbus_send_raw_request_tid(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length, int tid);

5. 使用示例

5.1 Modbus TCP 客户端读取保持寄存器

#include "modbus.h"

modbus_t *ctx = modbus_new_tcp("192.168.1.100", 502);
if (ctx == NULL) { /* error */ }

modbus_set_slave(ctx, 1);

if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    return;
}

uint16_t tab_reg[32];
int rc = modbus_read_registers(ctx, 0, 10, tab_reg);
if (rc == -1) {
    fprintf(stderr, "Read failed: %s\n", modbus_strerror(errno));
} else {
    for (int i = 0; i < rc; i++) {
        printf("reg[%d]=%d\n", i, tab_reg[i]);
    }
}

modbus_close(ctx);
modbus_free(ctx);

5.2 Modbus RTU 从站

#include "modbus.h"

modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
modbus_set_slave(ctx, 1);

modbus_mapping_t *mb_mapping = modbus_mapping_new(100, 100, 100, 100);
if (mb_mapping == NULL) { /* error */ }

if (modbus_connect(ctx) == -1) { /* error */ }

uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
for (;;) {
    int rc = modbus_receive(ctx, query);
    if (rc > 0) {
        modbus_reply(ctx, query, rc, mb_mapping);
    }
}

modbus_mapping_free(mb_mapping);
modbus_close(ctx);
modbus_free(ctx);

6. 依赖关系

myLog.h → libmodbus
  (libmodbus 源码修改: fprintf → LOG_I/LOG_E)

libmodbus 是独立的开源库,仅依赖标准 POSIXsocket / termios / pthread和项目日志模块。


7. 代码审查要点

状态 说明
许可证 LGPL-2.1+ 兼容项目
版本 v3.1.12 稳定发布版
源码改动 2 处 日志宏替换fprintf → LOG_I/E
线程安全 每个 modbus_t 独立,不共享
内存管理 手动 modbus_free() / modbus_mapping_free()
RTU CRC 查表法 同 func_cal_crc16
TCP MBAP 7 字节头 事务 ID + 协议 ID + 长度 + 单元 ID