/* * myModbus.h — libmodbus 统一 API 头文件 * * 本文件整合了 libmodbus 的全部公开接口,包括 RTU 和 TCP 后端。 * 用户只需包含这一个头文件即可使用 libmodbus 的所有功能。 * * 原始代码版权 © Stéphane Raimbault * 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 #endif #include /* ================================================================ * 版本信息宏 * * 版本号格式: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 */