735 lines
28 KiB
C
735 lines
28 KiB
C
/*
|
||
* myModbus.h — libmodbus 统一 API 头文件
|
||
*
|
||
* 本文件整合了 libmodbus 的全部公开接口,包括 RTU 和 TCP 后端。
|
||
* 用户只需包含这一个头文件即可使用 libmodbus 的所有功能。
|
||
*
|
||
* 原始代码版权 © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||
*
|
||
* 库版本:3.1.12
|
||
* 官网:http://libmodbus.org
|
||
*/
|
||
|
||
#ifndef MY_MODBUS_H
|
||
#define MY_MODBUS_H
|
||
|
||
// clang-format off
|
||
/* 添加此宏用于定义了 Unix 风格的系统 */
|
||
#if (defined(__unix__) || defined(unix)) && !defined(USG)
|
||
# include <sys/param.h>
|
||
#endif
|
||
|
||
#include <stdint.h>
|
||
|
||
/* ================================================================
|
||
* 版本信息宏
|
||
*
|
||
* 版本号格式:LIBMODBUS_VERSION_MAJOR.MINOR.MICRO
|
||
* 当前版本:3.1.12
|
||
* ================================================================ */
|
||
#define LIBMODBUS_VERSION_MAJOR 3
|
||
#define LIBMODBUS_VERSION_MINOR 1
|
||
#define LIBMODBUS_VERSION_MICRO 12
|
||
|
||
/* 完整版本号字符串 */
|
||
#define LIBMODBUS_VERSION 3.1.12
|
||
#define LIBMODBUS_VERSION_STRING "3.1.12"
|
||
|
||
/* 数值编码的版本号,例如 v1.2.3 编码为 0x010203 */
|
||
#define LIBMODBUS_VERSION_HEX \
|
||
((LIBMODBUS_VERSION_MAJOR << 16) | (LIBMODBUS_VERSION_MINOR << 8) | \
|
||
(LIBMODBUS_VERSION_MICRO << 0))
|
||
|
||
/* 版本检查宏:当库版本大于等于指定版本时返回真
|
||
* @param major 主版本号
|
||
* @param minor 次版本号
|
||
* @param micro 修订版本号
|
||
*/
|
||
#define LIBMODBUS_VERSION_CHECK(major, minor, micro) \
|
||
(LIBMODBUS_VERSION_MAJOR > (major) || \
|
||
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR > (minor)) || \
|
||
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR == (minor) && \
|
||
LIBMODBUS_VERSION_MICRO >= (micro)))
|
||
|
||
#define MODBUS_API
|
||
|
||
#ifdef __cplusplus
|
||
# define MODBUS_BEGIN_DECLS extern "C" {
|
||
# define MODBUS_END_DECLS }
|
||
#else
|
||
# define MODBUS_BEGIN_DECLS
|
||
# define MODBUS_END_DECLS
|
||
#endif
|
||
// clang-format on
|
||
|
||
MODBUS_BEGIN_DECLS
|
||
|
||
#ifndef FALSE
|
||
#define FALSE 0
|
||
#endif
|
||
|
||
#ifndef TRUE
|
||
#define TRUE 1
|
||
#endif
|
||
|
||
#ifndef OFF
|
||
#define OFF 0
|
||
#endif
|
||
|
||
#ifndef ON
|
||
#define ON 1
|
||
#endif
|
||
|
||
/* ================================================================
|
||
* 一、Modbus 功能码定义
|
||
* ================================================================ */
|
||
|
||
/* 读取线圈状态 */
|
||
#define MODBUS_FC_READ_COILS 0x01
|
||
/* 读取离散输入状态 */
|
||
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
|
||
/* 读取保持寄存器 */
|
||
#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03
|
||
/* 读取输入寄存器 */
|
||
#define MODBUS_FC_READ_INPUT_REGISTERS 0x04
|
||
/* 写单个线圈 */
|
||
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
|
||
/* 写单个寄存器 */
|
||
#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06
|
||
/* 读取异常状态 */
|
||
#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07
|
||
/* 写多个线圈 */
|
||
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
|
||
/* 写多个寄存器 */
|
||
#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10
|
||
/* 报告从站ID */
|
||
#define MODBUS_FC_REPORT_SLAVE_ID 0x11
|
||
/* 掩码写寄存器 */
|
||
#define MODBUS_FC_MASK_WRITE_REGISTER 0x16
|
||
/* 读写多个寄存器 */
|
||
#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
|
||
|
||
/* 广播地址(仅 RTU 模式有效) */
|
||
#define MODBUS_BROADCAST_ADDRESS 0
|
||
|
||
/* ================================================================
|
||
* 二、数据量限制
|
||
*
|
||
* 参考 Modbus_Application_Protocol_V1_1b.pdf
|
||
* ================================================================ */
|
||
|
||
/* 位操作限制:
|
||
* 单次读取最多 2000 个位(线圈/离散输入)
|
||
* 单次写入最多 1968 个位(线圈)
|
||
*/
|
||
#define MODBUS_MAX_READ_BITS 2000
|
||
#define MODBUS_MAX_WRITE_BITS 1968
|
||
|
||
/* 寄存器操作限制:
|
||
* 单次读取最多 125 个寄存器
|
||
* 单次写入最多 123 个寄存器
|
||
* 读写操作中写入最多 121 个、读取最多 125 个寄存器
|
||
*/
|
||
#define MODBUS_MAX_READ_REGISTERS 125
|
||
#define MODBUS_MAX_WRITE_REGISTERS 123
|
||
#define MODBUS_MAX_WR_WRITE_REGISTERS 121
|
||
#define MODBUS_MAX_WR_READ_REGISTERS 125
|
||
|
||
/* ================================================================
|
||
* 三、ADU/PDU 长度定义
|
||
*
|
||
* Modbus PDU 的大小受限于最早 Modbus 实现的尺寸约束:
|
||
* 最大 RS485 ADU = 256 字节。
|
||
* 因此串行线路通信的 Modbus PDU = 256 - 从站地址(1) - CRC(2) = 253 字节。
|
||
*
|
||
* 由此可得各后端最大 ADU:
|
||
* RTU: 253 + 从站地址(1) + CRC(2) = 256 字节
|
||
* TCP: 253 + MBAP头(7) = 260 字节
|
||
* 统一取 260 字节保证兼容所有后端。
|
||
* ================================================================ */
|
||
#define MODBUS_MAX_PDU_LENGTH 253
|
||
#define MODBUS_MAX_ADU_LENGTH 260
|
||
|
||
/* 用一个任意数作为 errno 基准值,避免与系统 errno 冲突 */
|
||
#define MODBUS_ENOBASE 112345678
|
||
|
||
/* ================================================================
|
||
* 四、Modbus 协议异常码
|
||
*
|
||
* 当服务端无法处理请求时,在功能码上设置 0x80 并返回异常码。
|
||
* ================================================================ */
|
||
|
||
enum {
|
||
MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01, /* 非法功能码 */
|
||
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, /* 非法数据地址 */
|
||
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, /* 非法数据值 */
|
||
MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE, /* 从站/服务端故障 */
|
||
MODBUS_EXCEPTION_ACKNOWLEDGE, /* 确认(处理中) */
|
||
MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY, /* 从站/服务端忙 */
|
||
MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE, /* 否定确认 */
|
||
MODBUS_EXCEPTION_MEMORY_PARITY, /* 存储器奇偶校验错误 */
|
||
MODBUS_EXCEPTION_NOT_DEFINED, /* 未定义 */
|
||
MODBUS_EXCEPTION_GATEWAY_PATH, /* 网关路径不可用 */
|
||
MODBUS_EXCEPTION_GATEWAY_TARGET, /* 网关目标设备无响应 */
|
||
MODBUS_EXCEPTION_MAX /* 异常码上限 */
|
||
};
|
||
|
||
/* 异常 errno 数值:基值 + 异常码 */
|
||
#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION)
|
||
#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS)
|
||
#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE)
|
||
#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE)
|
||
#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE)
|
||
#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY)
|
||
#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE)
|
||
#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY)
|
||
#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH)
|
||
#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET)
|
||
|
||
/* libmodbus 本地错误码(非协议定义) */
|
||
#define EMBBADCRC (EMBXGTAR + 1) /* CRC 校验错误 */
|
||
#define EMBBADDATA (EMBXGTAR + 2) /* 无效数据 */
|
||
#define EMBBADEXC (EMBXGTAR + 3) /* 无效异常码 */
|
||
#define EMBUNKEXC (EMBXGTAR + 4) /* 未知异常码 */
|
||
#define EMBMDATA (EMBXGTAR + 5) /* 数据过多 */
|
||
#define EMBBADSLAVE (EMBXGTAR + 6) /* 响应并非来自请求的从站 */
|
||
|
||
/* ================================================================
|
||
* 五、版本信息全局变量
|
||
* ================================================================ */
|
||
|
||
extern const unsigned int libmodbus_version_major;
|
||
extern const unsigned int libmodbus_version_minor;
|
||
extern const unsigned int libmodbus_version_micro;
|
||
|
||
/* ================================================================
|
||
* 六、核心数据类型
|
||
* ================================================================ */
|
||
|
||
/* Modbus 上下文(不透明类型,所有 API 操作的基础句柄) */
|
||
typedef struct _modbus modbus_t;
|
||
|
||
/*!
|
||
* Modbus 数据映射表(服务端数据模型)
|
||
*
|
||
* 四种数据区与 Modbus 协议的对应关系:
|
||
* Coils (0X) — tab_bits — 可读写位
|
||
* Discrete Inputs (1X) — tab_input_bits — 只读位
|
||
* Holding Registers (4X) — tab_registers — 可读写 16 位寄存器
|
||
* Input Registers (3X) — tab_input_registers — 只读 16 位寄存器
|
||
*
|
||
* tab_xxx 数组中的内存布局采用处理器字节序。
|
||
* 接收 Modbus 数据时会自动转换为处理器字节序,参见 read_registers()。
|
||
*/
|
||
typedef struct _modbus_mapping_t {
|
||
int nb_bits; /* 线圈数量 */
|
||
int start_bits; /* 线圈起始地址 */
|
||
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; /* 线圈数据数组 */
|
||
uint8_t *tab_input_bits; /* 离散输入数据数组 */
|
||
uint16_t *tab_input_registers; /* 输入寄存器数据数组 */
|
||
uint16_t *tab_registers; /* 保持寄存器数据数组 */
|
||
} modbus_mapping_t;
|
||
|
||
/*!
|
||
* 错误恢复模式(可按位组合)
|
||
*
|
||
* MODBUS_ERROR_RECOVERY_LINK — 链路级恢复:send() 失败时自动关闭连接、
|
||
* 等待超时后重连
|
||
* MODBUS_ERROR_RECOVERY_PROTOCOL — 协议级恢复:收到无效回复时自动休眠刷新
|
||
*/
|
||
typedef enum {
|
||
MODBUS_ERROR_RECOVERY_NONE = 0,
|
||
MODBUS_ERROR_RECOVERY_LINK = (1 << 1),
|
||
MODBUS_ERROR_RECOVERY_PROTOCOL = (1 << 2)
|
||
} modbus_error_recovery_mode;
|
||
|
||
/*!
|
||
* Quirks 行为开关(用于启用以非标准方式工作的设备的兼容模式)
|
||
*
|
||
* MODBUS_QUIRK_MAX_SLAVE — 允许从站地址扩展到 255(而非标准 247)
|
||
* MODBUS_QUIRK_REPLY_TO_BROADCAST — 允许服务器回复广播请求
|
||
*/
|
||
typedef enum {
|
||
MODBUS_QUIRK_NONE = 0,
|
||
MODBUS_QUIRK_MAX_SLAVE = (1 << 1),
|
||
MODBUS_QUIRK_REPLY_TO_BROADCAST = (1 << 2),
|
||
MODBUS_QUIRK_ALL = 0xFF
|
||
} modbus_quirks;
|
||
|
||
/* ================================================================
|
||
* 七、通用 API(RTU 和 TCP 均适用)
|
||
* ================================================================ */
|
||
|
||
/* ---- 从站地址 ---- */
|
||
/* 设置从站地址(RTU: 0~247, TCP: 默认 0xFF)
|
||
* @param ctx Modbus 上下文
|
||
* @param slave 从站地址
|
||
* @return 成功返回 0,失败返回 -1 并设置 errno
|
||
*/
|
||
MODBUS_API int modbus_set_slave(modbus_t *ctx, int slave);
|
||
|
||
/* 获取当前从站地址
|
||
* @param ctx Modbus 上下文
|
||
* @return 从站地址,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_get_slave(modbus_t *ctx);
|
||
|
||
/* ---- 错误恢复 ---- */
|
||
MODBUS_API int modbus_set_error_recovery(modbus_t *ctx,
|
||
modbus_error_recovery_mode error_recovery);
|
||
|
||
/* ---- Socket 描述符 ---- */
|
||
/* 设置自定义 socket 文件描述符
|
||
* @param ctx Modbus 上下文
|
||
* @param s 文件描述符
|
||
*/
|
||
MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
|
||
|
||
/* 获取当前 socket 文件描述符 */
|
||
MODBUS_API int modbus_get_socket(modbus_t *ctx);
|
||
|
||
/* ---- 超时配置 ---- */
|
||
/* 获取/设置响应超时(客户端等待服务端响应的最长时间)
|
||
* @param to_sec 秒
|
||
* @param to_usec 微秒(0~999999)
|
||
* 默认:500000 微秒(0.5 秒)
|
||
*/
|
||
MODBUS_API int
|
||
modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||
MODBUS_API int
|
||
modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||
|
||
/* 获取/设置字节间超时(接收时两个连续字节间的最大间隔)
|
||
* 可设置为 0 禁用字节间超时检查
|
||
*/
|
||
MODBUS_API int
|
||
modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||
MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||
|
||
/* 获取/设置指示超时(服务端等待客户端请求的最大时间)
|
||
* 默认为 0(无限等待/阻塞模式)
|
||
*/
|
||
MODBUS_API int
|
||
modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||
MODBUS_API int
|
||
modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||
|
||
/* 获取帧头长度(RTU=1, TCP=7) */
|
||
MODBUS_API int modbus_get_header_length(modbus_t *ctx);
|
||
|
||
/* ---- 连接管理 ---- */
|
||
/* 建立 Modbus 连接
|
||
* TCP: 连接指定 IP 和端口
|
||
* RTU: 打开串口并配置参数
|
||
*/
|
||
MODBUS_API int modbus_connect(modbus_t *ctx);
|
||
|
||
/* 关闭连接(TCP: 关闭 socket, RTU: 关闭串口并恢复设置) */
|
||
MODBUS_API void modbus_close(modbus_t *ctx);
|
||
|
||
/* 释放 Modbus 上下文占用的所有内存 */
|
||
MODBUS_API void modbus_free(modbus_t *ctx);
|
||
|
||
/* 清空收发缓冲区 */
|
||
MODBUS_API int modbus_flush(modbus_t *ctx);
|
||
|
||
/* 开启/关闭调试输出(将通信数据打印到 stderr/stdout) */
|
||
MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag);
|
||
|
||
/* ---- 错误信息 ---- */
|
||
/* 将 errno 错误码转换为可读的字符串描述 */
|
||
MODBUS_API const char *modbus_strerror(int errnum);
|
||
|
||
/* ================================================================
|
||
* 八、客户端读写 API
|
||
*
|
||
* 以下函数用于 Modbus 主站(Master/Client)向从站发起读写请求。
|
||
* 成功时返回读取/写入的数据个数,失败时返回 -1 并设置 errno。
|
||
* ================================================================ */
|
||
|
||
/* ---- 位操作 ---- */
|
||
|
||
/* 读取线圈状态 (FC 0x01)
|
||
* @param addr 起始地址
|
||
* @param nb 数量(1~2000)
|
||
* @param dest 输出缓冲区(每个元素为 TRUE 或 FALSE)
|
||
* @return 成功返回 nb,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||
|
||
/* 读取离散输入状态 (FC 0x02) */
|
||
MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||
|
||
/* 读取保持寄存器 (FC 0x03)
|
||
* @param addr 起始地址
|
||
* @param nb 数量(1~125)
|
||
* @param dest 输出缓冲区
|
||
* @return 成功返回 nb,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||
|
||
/* 读取输入寄存器 (FC 0x04) */
|
||
MODBUS_API int
|
||
modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||
|
||
/* 写单个线圈 (FC 0x05)
|
||
* @param coil_addr 线圈地址
|
||
* @param status ON(1) 或 OFF(0)
|
||
* @return 成功返回 1,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
|
||
|
||
/* 写单个寄存器 (FC 0x06) */
|
||
MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);
|
||
|
||
/* 写多个线圈 (FC 0x0F)
|
||
* @param addr 起始地址
|
||
* @param nb 数量(1~1968)
|
||
* @param data 数据源数组(每个元素为 TRUE 或 FALSE)
|
||
* @return 成功返回 nb,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
|
||
|
||
/* 写多个寄存器 (FC 0x10)
|
||
* @param addr 起始地址
|
||
* @param nb 数量(1~123)
|
||
* @param data 数据源数组
|
||
* @return 成功返回 nb,失败返回 -1
|
||
*/
|
||
MODBUS_API int
|
||
modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
|
||
|
||
/* 掩码写寄存器 (FC 0x16) — 原子读-修改-写操作
|
||
* 操作:result = (current & and_mask) | (or_mask & ~and_mask)
|
||
*/
|
||
MODBUS_API int
|
||
modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
|
||
|
||
/* 写并读多个寄存器 (FC 0x17) — 在单次请求中完成写入和读取
|
||
* @param write_addr 写入起始地址
|
||
* @param write_nb 写入数量(1~121)
|
||
* @param src 写入数据源
|
||
* @param read_addr 读取起始地址
|
||
* @param read_nb 读取数量(1~125)
|
||
* @param dest 读取数据目标
|
||
* @return 成功返回 read_nb,失败返回 -1
|
||
*/
|
||
MODBUS_API 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);
|
||
|
||
/* 读取从站 ID 信息 (FC 0x11) */
|
||
MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
|
||
|
||
/* ================================================================
|
||
* 九、服务端 API
|
||
*
|
||
* 以下函数用于 Modbus 从站(Slave/Server)处理请求和生成响应。
|
||
* ================================================================ */
|
||
|
||
/* ---- 数据映射管理 ---- */
|
||
|
||
/* 创建数据映射(指定起始地址,推荐使用,灵活)
|
||
* @param start_bits 线圈起始地址
|
||
* @param nb_bits 线圈数量
|
||
* @param start_input_bits 离散输入起始地址
|
||
* @param nb_input_bits 离散输入数量
|
||
* @param start_registers 保持寄存器起始地址
|
||
* @param nb_registers 保持寄存器数量
|
||
* @param start_input_registers 输入寄存器起始地址
|
||
* @param nb_input_registers 输入寄存器数量
|
||
* @return 成功返回映射指针,失败返回 NULL
|
||
*/
|
||
MODBUS_API 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);
|
||
|
||
/* 创建数据映射(起始地址均为 0) */
|
||
MODBUS_API modbus_mapping_t *modbus_mapping_new(int nb_bits,
|
||
int nb_input_bits,
|
||
int nb_registers,
|
||
int nb_input_registers);
|
||
|
||
/* 释放数据映射占用的内存 */
|
||
MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);
|
||
|
||
/* ---- 原始请求发送 ---- */
|
||
|
||
/* 发送原始 Modbus 请求帧(不含后端帧头/校验,库会自动添加)
|
||
* @param ctx Modbus 上下文
|
||
* @param raw_req 原始请求数据(格式:[slave][function][data...])
|
||
* @param raw_req_length 请求长度
|
||
* @return 成功返回已发送字节数,失败返回 -1
|
||
*/
|
||
MODBUS_API int
|
||
modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length);
|
||
|
||
/* 同上,但可指定事务 ID(TCP 模式下有意义) */
|
||
MODBUS_API int modbus_send_raw_request_tid(modbus_t *ctx,
|
||
const uint8_t *raw_req,
|
||
int raw_req_length,
|
||
int tid);
|
||
|
||
/* ---- 请求接收 ---- */
|
||
|
||
/* 接收 Modbus 请求(服务端使用)
|
||
* @param ctx Modbus 上下文
|
||
* @param req 接收缓冲区
|
||
* @return >0 请求长度, 0 请求已过滤, -1 错误
|
||
*/
|
||
MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req);
|
||
|
||
/* 接收确认响应(客户端使用)
|
||
* @param ctx Modbus 上下文
|
||
* @param rsp 接收缓冲区
|
||
* @return >0 响应长度, -1 错误
|
||
*/
|
||
MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
|
||
|
||
/* ---- 请求处理 ---- */
|
||
|
||
/* 自动解析请求并根据数据映射生成响应
|
||
* @param ctx Modbus 上下文
|
||
* @param req 请求数据
|
||
* @param req_length 请求长度
|
||
* @param mb_mapping 数据映射表
|
||
* @return 成功返回响应长度,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_reply(modbus_t *ctx,
|
||
const uint8_t *req,
|
||
int req_length,
|
||
modbus_mapping_t *mb_mapping);
|
||
|
||
/* 手动返回异常响应
|
||
* @param ctx Modbus 上下文
|
||
* @param req 原始请求数据
|
||
* @param exception_code 异常码(MODBUS_EXCEPTION_xxx)
|
||
* @return 成功返回响应长度,失败返回 -1
|
||
*/
|
||
MODBUS_API int
|
||
modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code);
|
||
|
||
/* ---- Quirks 管理 ---- */
|
||
|
||
/* 启用 quirks 特性(可按位组合) */
|
||
MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask);
|
||
|
||
/* 禁用 quirks 特性 */
|
||
MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask);
|
||
|
||
/* ================================================================
|
||
* 十、数据工具函数
|
||
* ================================================================ */
|
||
|
||
/* ---- 字节/位转换宏 ---- */
|
||
|
||
#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
|
||
#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
|
||
|
||
/* 从 4 个连续的 16 位寄存器中组合出 64 位整数 */
|
||
#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \
|
||
(((int64_t) tab_int16[(index)] << 48) | ((int64_t) tab_int16[(index) + 1] << 32) | \
|
||
((int64_t) tab_int16[(index) + 2] << 16) | (int64_t) tab_int16[(index) + 3])
|
||
|
||
/* 从 2 个连续的 16 位寄存器中组合出 32 位整数 */
|
||
#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \
|
||
(((int32_t) tab_int16[(index)] << 16) | (int32_t) tab_int16[(index) + 1])
|
||
|
||
/* 从 2 个连续的 8 位值中组合出 16 位整数 */
|
||
#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \
|
||
(((int16_t) tab_int8[(index)] << 8) | (int16_t) tab_int8[(index) + 1])
|
||
|
||
/* 将 16 位整数拆分写入 2 个连续的 8 位值 */
|
||
#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \
|
||
do { \
|
||
((int8_t *) (tab_int8))[(index)] = (int8_t) ((value) >> 8); \
|
||
((int8_t *) (tab_int8))[(index) + 1] = (int8_t) (value); \
|
||
} while (0)
|
||
|
||
/* 将 32 位整数拆分写入 2 个连续的 16 位寄存器 */
|
||
#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \
|
||
do { \
|
||
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 16); \
|
||
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) (value); \
|
||
} while (0)
|
||
|
||
/* 将 64 位整数拆分写入 4 个连续的 16 位寄存器 */
|
||
#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \
|
||
do { \
|
||
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 48); \
|
||
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 32); \
|
||
((int16_t *) (tab_int16))[(index) + 2] = (int16_t) ((value) >> 16); \
|
||
((int16_t *) (tab_int16))[(index) + 3] = (int16_t) (value); \
|
||
} while (0)
|
||
|
||
/* ---- 位数组工具函数 ---- */
|
||
|
||
/* 从单字节设置 8 个位 */
|
||
MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value);
|
||
|
||
/* 从字节数组设置多个位 */
|
||
MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest,
|
||
int idx,
|
||
unsigned int nb_bits,
|
||
const uint8_t *tab_byte);
|
||
|
||
/* 从位数组获取一个字节 */
|
||
MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src,
|
||
int idx,
|
||
unsigned int nb_bits);
|
||
|
||
/* ---- 浮点数转换(IEEE 754 单精度 ↔ 2 个 Modbus 寄存器) ---- */
|
||
|
||
/* 已弃用:默认使用 CDAB(字交换)字节序 */
|
||
MODBUS_API float modbus_get_float(const uint16_t *src);
|
||
|
||
/* ABCD 字节序(大端):高位字节在前 */
|
||
MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
|
||
|
||
/* DCBA 字节序(小端):低位字节在前 */
|
||
MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
|
||
|
||
/* BADC 字节序:字内字节交换 */
|
||
MODBUS_API float modbus_get_float_badc(const uint16_t *src);
|
||
|
||
/* CDAB 字节序:字间交换 */
|
||
MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
|
||
|
||
/* 已弃用:默认使用 CDAB(字交换)字节序 */
|
||
MODBUS_API void modbus_set_float(float f, uint16_t *dest);
|
||
|
||
MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest);
|
||
MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest);
|
||
MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest);
|
||
MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest);
|
||
|
||
/* ================================================================
|
||
* 十一、RTU 后端 API(串口通信)
|
||
*
|
||
* 仅 RTU 后端可用,TCP 后端调用将返回错误。
|
||
* ================================================================ */
|
||
|
||
/* RS232 / RS485 ADU = 253 + 从站地址(1) + CRC(2) = 256 字节 */
|
||
#define MODBUS_RTU_MAX_ADU_LENGTH 256
|
||
|
||
/* 创建 RTU 上下文
|
||
* @param device 串口设备路径(如 "/dev/ttyUSB0")
|
||
* @param baud 波特率(9600, 115200 等)
|
||
* @param parity 校验位:'N' 无校验, 'E' 偶校验, 'O' 奇校验
|
||
* @param data_bit 数据位:5, 6, 7, 8
|
||
* @param stop_bit 停止位:1 或 2
|
||
* @return 成功返回 Modbus 上下文指针,失败返回 NULL
|
||
*/
|
||
MODBUS_API modbus_t *
|
||
modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
|
||
|
||
/* RS232/RS485 模式切换
|
||
* MODBUS_RTU_RS232 — RS232 模式
|
||
* MODBUS_RTU_RS485 — RS485 模式
|
||
*/
|
||
#define MODBUS_RTU_RS232 0
|
||
#define MODBUS_RTU_RS485 1
|
||
|
||
/* 设置/获取串行通信模式(RS232 或 RS485)
|
||
* 注意:需要平台支持 TIOCSRS485 ioctl
|
||
*/
|
||
MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
|
||
MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);
|
||
|
||
/* ---- RTS 引脚控制(半双工 RS485 自动收发切换) ---- */
|
||
|
||
/* RTS 控制模式 */
|
||
#define MODBUS_RTU_RTS_NONE 0 /* 不使用 RTS */
|
||
#define MODBUS_RTU_RTS_UP 1 /* 发送前置 RTS 为高 */
|
||
#define MODBUS_RTU_RTS_DOWN 2 /* 发送前置 RTS 为低 */
|
||
|
||
/* 设置/获取 RTS 控制模式 */
|
||
MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
|
||
MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);
|
||
|
||
/* 设置自定义 RTS 回调函数
|
||
* @param set_rts 回调函数指针:void callback(modbus_t *ctx, int on)
|
||
*/
|
||
MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx,
|
||
void (*set_rts)(modbus_t *ctx, int on));
|
||
|
||
/* 设置/获取 RTS 切换延时(微秒)
|
||
* 在发送前 RTS 信号有效后等待的延时,以及发送后 RTS 释放前的延时。
|
||
* 默认值根据波特率自动计算。
|
||
*/
|
||
MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us);
|
||
MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx);
|
||
|
||
/* ================================================================
|
||
* 十二、TCP 后端 API(以太网通信)
|
||
* ================================================================ */
|
||
|
||
|
||
/* Modbus TCP 标准端口(IANA 注册) */
|
||
#define MODBUS_TCP_DEFAULT_PORT 502
|
||
|
||
/* TCP 模式下默认的单元标识符(表示由 TCP/IP 层寻址) */
|
||
#define MODBUS_TCP_SLAVE 0xFF
|
||
|
||
/* TCP MODBUS ADU = 253 字节数据 + MBAP 报文头(7) = 260 字节 */
|
||
#define MODBUS_TCP_MAX_ADU_LENGTH 260
|
||
|
||
/* ---- TCP IPv4 接口 ---- */
|
||
|
||
/* 创建 TCP 上下文(IPv4)
|
||
* @param ip_address 服务器 IP 地址(如 "192.168.1.100")
|
||
* @param port 端口号(默认 502)
|
||
* @return 成功返回 Modbus 上下文指针,失败返回 NULL
|
||
*/
|
||
MODBUS_API modbus_t *modbus_new_tcp(const char *ip_address, int port);
|
||
|
||
/* TCP 服务端监听
|
||
* @param ctx Modbus 上下文
|
||
* @param nb_connection 最大等待连接数
|
||
* @return 成功返回监听 socket 描述符,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection);
|
||
|
||
/* TCP 服务端接受客户端连接
|
||
* @param ctx Modbus 上下文
|
||
* @param s 监听 socket 描述符(由 modbus_tcp_listen 返回)
|
||
* @return 成功返回客户端 socket,失败返回 -1
|
||
*/
|
||
MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s);
|
||
|
||
/* ---- TCP PI 接口(协议无关,同时支持 IPv4/IPv6) ---- */
|
||
|
||
/* 创建 TCP PI 上下文
|
||
* @param node 主机名或 IP 地址(NULL 表示监听所有地址)
|
||
* @param service 端口号或服务名(如 "502")
|
||
* @return 成功返回 Modbus 上下文指针,失败返回 NULL
|
||
*/
|
||
MODBUS_API modbus_t *modbus_new_tcp_pi(const char *node, const char *service);
|
||
|
||
/* TCP PI 服务端监听 */
|
||
MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection);
|
||
|
||
/* TCP PI 服务端接受客户端连接 */
|
||
MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s);
|
||
|
||
MODBUS_END_DECLS
|
||
|
||
#endif /* MY_MODBUS_H */
|