# 示例说明 ## 1、介绍 该示例提供 RTU / TCP 主机和从机的功能演示。 `WSL` 或 `Linux` 下使用 `gcc` 可直接 `make all` 编译出所有示例,在电脑上运行测试程序。 目录结构: | 名称 | 说明 | | ---- | ---- | | common | 公用源码 | | figures | 素材 | | rtu_master | RTU 主机示例 | | tcp_master | TCP 主机示例 | | slave | RTU + TCP 从机示例 | | rtu_p2p | RTU 点对点传输文件 | | rtu_broadcast | RTU 广播传输文件 (粘包处理示例) | ## 2、使用 需要准备的工具如下: - 虚拟串口软件 - Modbus Poll - Modbus Slave 命令行敲击 `make clean` 、 `make all` 。 ### 2.1、主机 - RTU (rtu_master) - 使用虚拟串口软件虚拟出一对串口 ![VirtualCom](./figures/VirtualCom.jpg) - 打开 `Modbus Slave` ,按下图设置 ![ModbusSlaveSetup](./figures/ModbusSlaveSetup.jpg) - `Modbus Slave` 连接,按下图设置 ![ModbusSlaveRTUConnection](./figures/ModbusSlaveRTUConnection.jpg) - 进入 `rtu_master` 目录,`./RtuMaster /dev/ttySX` 运行 `RTU` 主机示例,`ttySX` 为一对虚拟串口中的另一个 ![RTUMaster](./figures/RTUMaster.jpg) - TCP (tcp_master) - 打开 `Modbus Slave`,`SetUp` 设置同 `RTU` 一致 - `Modbus Slave` 连接,按下图设置 ![ModbusSlaveTCPConnection](./figures/ModbusSlaveTCPConnection.jpg) - 进入 `tcp_master` 目录,`./TcpMaster 127.0.0.1 502` 运行 `TCP` 主机示例 ![TCPMaster](./figures/TCPMaster.jpg) ### 2.2、从机 - 该示例 (slave) 同时提供 `RTU` 和 `TCP` 从机功能演示,控制的是同一片内存。`TCP` 最大可接入 5 个客户端,每个客户端无数据超时为 10s, 10s 后自动断开。 - 示例支持所有功能码(除了功能码 0x07)。 - 对 `bit`、`input_bit`、`register`、`input_register` 寄存器每个文件单独定义。 - 使用 `agile_modbus_slave_util_callback` 。 - 寄存器地址域: | 寄存器 | 地址范围 | | --- | --- | | 线圈寄存器 | 0x041A ~ 0x0423 (1050 ~ 1059) | | 离散量输入寄存器 | 0x041A ~ 0x0423 (1050 ~ 1059) | | 保持寄存器 | 0xFFF6 ~ 0xFFFF (65526 ~ 65535) | | 输入寄存器 | 0xFFF6 ~ 0xFFFF (65526 ~ 65535) | **注意**: 读写其他地址寄存器都能成功,但值都为 0。 使用: - 使用虚拟串口软件虚拟出一对串口 ![VirtualCom](./figures/VirtualCom.jpg) - 进入 `slave` 目录,`./ModbusSlave /dev/ttyS2 1025` 运行示例 /dev/ttySX: 虚拟串口中的一个 1025:监听端口号,如果不是 `root` 权限,端口号必须大于 `1024` - 打开 `Modbus Poll` 按下图设置和连接 RTU ![ModbusPollSetup](./figures/ModbusPollSetup.jpg) ![ModbusPollRTUConnection](./figures/ModbusPollRTUConnection.jpg) - 打开 5 个 `Modbus Poll` ,设置同 RTU,连接如下图 ![ModbusPollTCPConnection](./figures/ModbusPollTCPConnection.jpg) - 效果演示 ![ModbusSlaveShow](./figures/ModbusSlaveShow.jpg) - 超时断开演示 将 `Modbus Poll` 的 poll 界面关闭,看控制台打印可以看到 close 报文。 ![ModbusSlaveTimeoutShow](./figures/ModbusSlaveTimeoutShow.jpg) ### 2.3、RTU 传输文件 ![ModbusProtocol](./figures/ModbusProtocol.jpg) 使用 `0x50` 作为传输文件的特殊功能码。 分包传输文件数据,每包数据最大 1024 字节。 `Data` 字段协议定义: - 主机请求 | 命令 | 字节数 | 数据 | | ---- | ---- | ---- | | 2 Bytes | 2 Bytes | N Bytes | 命令: | 命令 | 说明 | 数据 | | ---- | ---- | ---- | | 0x0001 | 开始发送 | 文件大小(4 Bytes) + 文件名称(字符串) | | 0x0002 | 传输数据 | 标志(1 Byte) + 文件数据 | 标志: | 状态 | 说明 | | ---- | ---- | | 0x00 | 最后一包数据 | | 0x01 | 不是最后一包数据 | - 从机响应 | 命令 | 状态 | | ---- | ---- | | 2 Bytes | 1 Byte | 状态: | 状态 | 说明 | | ---- | ---- | | 0x00 | 失败 | | 0x01 | 成功 | - 使用虚拟串口软件虚拟出 3 个串口,组成串口群组 这里我使用的时 MX 虚拟串口 ![VirtualComGroup](./figures/VirtualComGroup.jpg) #### 2.3.1、点对点传输 - 进入 `rtu_p2p` 目录,打开 `Linux Shell`,演示效果如下 **注意**: - 传输的文件必须是一般文件,像可执行文件、目录等不支持 - 文件路径必须是 `Linux` 环境下的路径 - 从机接收到数据后修改文件名称 (从机地址_原文件名) 写入在当前目录。 ![rtu_p2p](./figures/rtu_p2p.gif) #### 2.3.2、广播传输 该例子主要演示 `agile_modbus_slave_handle` 中 `frame_length` 的用处。 `broadcast_master` 中,使用广播地址 0,周期 5ms 发送数据包。同时每包数据后都发送 100 字节的脏数据。 ```C int send_len = agile_modbus_serialize_raw_request(ctx, raw_req, raw_req_len); serial_send(_fd, ctx->send_buf, send_len); //脏数据 serial_send(_fd, _dirty_buf, sizeof(_dirty_buf)); ``` 在如此快速的数据流下,`broadcast_slave` 必须使用 `agile_modbus_slave_handle` 中 `frame_length` 参数来对粘包进行处理。 - 进入 `rtu_broadcast` 目录,打开 `Linux Shell`,演示效果如下 **注意**: - 传输的文件必须是一般文件,像可执行文件、目录等不支持 - 文件路径必须是 `Linux` 环境下的路径 - 从机接收到数据后修改文件名称 (从机地址_原文件名) 写入在当前目录。 ![rtu_broadcast](./figures/rtu_broadcast.gif)