j2mod 是一个 Java 库,用于实现 Modbus 协议通信,支持 RTU(串行)和 TCP 模式。Modbus RTU 通常用于工业自动化设备,通过串行端口(如 RS-232 或 RS-485)进行数据传输。以下是如何使用 j2mod 读取 Modbus RTU 数据的逐步指南。整个过程包括配置串口、创建主站对象、读取寄存器数据等步骤。
步骤 1: 添加 Maven 依赖
您已经提供了 Maven 依赖项,确保在pom.xml中添加以下依赖(版本号可选,建议使用最新版):
<dependency> <groupId>com.ghgande</groupId> <artifactId>j2mod</artifactId> <version>3.2.1</version> <!-- 可使用最新版本 --> </dependency>步骤 2: 配置串口参数
Modbus RTU 通信需要配置串口参数,如端口名称、波特率、数据位等。使用SerialParameters类来设置这些参数。
import com.ghgande.j2mod.modbus.util.SerialParameters; // 创建串口参数对象 SerialParameters serialParams = new SerialParameters(); serialParams.setPortName("COM1"); // 串口名称,例如 "COM1" (Windows) 或 "/dev/ttyUSB0" (Linux) serialParams.setBaudRate(9600); // 波特率,常用值为 9600、19200 等 serialParams.setDatabits(8); // 数据位,通常为 8 serialParams.setStopbits(1); // 停止位,通常为 1 serialParams.setParity("None"); // 奇偶校验,可选 "None"、"Even"、"Odd"步骤 3: 创建 Modbus 主站对象
使用ModbusSerialMaster类创建主站对象,并传入串口参数。主站对象负责与 Modbus 从站(设备)通信。
import com.ghgande.j2mod.modbus.facade.ModbusSerialMaster; import com.ghgande.j2mod.modbus.ModbusException; // 创建 Modbus 主站 ModbusSerialMaster master = new ModbusSerialMaster(serialParams);步骤 4: 连接到串口
在读取数据前,需要建立串口连接。使用connect()方法打开连接。
try { master.connect(); // 打开串口连接 System.out.println("串口连接成功"); } catch (Exception e) { System.err.println("连接失败: " + e.getMessage()); }步骤 5: 读取数据
Modbus RTU 支持读取不同类型的寄存器,如保持寄存器(Holding Registers)或输入寄存器(Input Registers)。以下示例演示如何读取保持寄存器:
slaveId: 从站地址(设备地址),通常为 1-247。startAddr: 起始寄存器地址(0-based)。count: 要读取的寄存器数量。
import com.ghgande.j2mod.modbus.procimg.Register; import com.ghgande.j2mod.modbus.procimg.InputRegister; import com.ghgande.j2mod.modbus.ModbusException; try { int slaveId = 1; // 从站地址 int startAddr = 0; // 起始地址,例如 0 int count = 2; // 读取 2 个寄存器 // 读取保持寄存器 InputRegister[] registers = master.readMultipleRegisters(slaveId, startAddr, count); // 处理读取的数据:将寄存器值转换为整数 for (int i = 0; i < registers.length; i++) { int value = registers[i].getValue(); // 获取寄存器的整数值 System.out.println("寄存器地址 " + (startAddr + i) + " 的值: " + value); } } catch (ModbusException e) { System.err.println("读取错误: " + e.getMessage()); }步骤 6: 关闭连接
通信结束后,关闭串口连接以释放资源。
master.disconnect(); // 关闭连接 System.out.println("连接已关闭");完整代码示例
以下是一个完整的 Java 示例,整合了以上步骤:
import com.ghgande.j2mod.modbus.io.ModbusSerialTransaction; import com.ghgande.j2mod.modbus.msg.*; import com.ghgande.j2mod.modbus.net.SerialConnection; import com.ghgande.j2mod.modbus.util.BitVector; import com.ghgande.j2mod.modbus.util.SerialParameters; /** * j2mod Modbus RTU 从站数据读取示例 * 通过串口通信读取 Modbus RTU 从站数据 */ public class ModbusRtuSlaveReader { private String portName; private int baudRate; private int dataBits; private int stopBits; private int parity; private int slaveId; private SerialConnection connection; /** * 构造函数 - 使用默认串口参数 */ public ModbusRtuSlaveReader(String portName, int slaveId) { this(portName, 9600, 8, 1, com.fazecast.jSerialComm.SerialPort.NO_PARITY, slaveId); } /** * 构造函数 - 自定义串口参数 */ public ModbusRtuSlaveReader(String portName, int baudRate, int dataBits, int stopBits, int parity, int slaveId) { this.portName = portName; this.baudRate = baudRate; this.dataBits = dataBits; this.stopBits = stopBits; this.parity = parity; this.slaveId = slaveId; } /** * 连接到 Modbus RTU 从站 */ public boolean connect() { try { // 配置串口参数 SerialParameters parameters = new SerialParameters(); parameters.setPortName(portName); parameters.setBaudRate(baudRate); parameters.setDatabits(dataBits); parameters.setStopbits(stopBits); parameters.setParity(parity); // 创建串口连接 connection = new SerialConnection(parameters); connection.open(); System.out.println("成功连接到串口: " + portName); System.out.println("参数: " + baudRate + "bps, " + dataBits + "数据位, " + stopBits + "停止位, 校验位: " + getParityString(parity)); return true; } catch (Exception e) { System.err.println("连接串口失败: " + e.getMessage()); return false; } } /** * 断开连接 */ public void disconnect() { if (connection != null) { connection.close(); System.out.println("串口连接已关闭"); } } /** * 读取线圈状态 (功能码 01) * @param startAddress 起始地址 * @param count 读取数量 */ public void readCoils(int startAddress, int count) { ModbusSerialTransaction transaction = null; try { transaction = new ModbusSerialTransaction(connection); ReadCoilsRequest request = new ReadCoilsRequest(startAddress, count); request.setUnitID(slaveId); transaction.setRequest(request); transaction.execute(); ReadCoilsResponse response = (ReadCoilsResponse) transaction.getResponse(); BitVector coils = response.getCoils(); System.out.println("读取线圈状态 (地址 " + startAddress + " - " + (startAddress + count - 1) + "):"); for (int i = 0; i < count; i++) { System.out.println(" 地址 " + (startAddress + i) + ": " + (coils.getBit(i) ? "ON" : "OFF")); } } catch (Exception e) { System.err.println("读取线圈失败: " + e.getMessage()); } } /** * 读取离散输入 (功能码 02) * @param startAddress 起始地址 * @param count 读取数量 */ public void readDiscreteInputs(int startAddress, int count) { ModbusSerialTransaction transaction = null; try { transaction = new ModbusSerialTransaction(connection); ReadInputDiscretesRequest request = new ReadInputDiscretesRequest(startAddress, count); request.setUnitID(slaveId); transaction.setRequest(request); transaction.execute(); ReadInputDiscretesResponse response = (ReadInputDiscretesResponse) transaction.getResponse(); BitVector discreteInputs = response.getDiscretes(); System.out.println("读取离散输入 (地址 " + startAddress + " - " + (startAddress + count - 1) + "):"); for (int i = 0; i < count; i++) { System.out.println(" 地址 " + (startAddress + i) + ": " + (discreteInputs.getBit(i) ? "ON" : "OFF")); } } catch (Exception e) { System.err.println("读取离散输入失败: " + e.getMessage()); } } /** * 读取保持寄存器 (功能码 03) * @param startAddress 起始地址 * @param count 读取数量 */ public void readHoldingRegisters(int startAddress, int count) { ModbusSerialTransaction transaction = null; try { transaction = new ModbusSerialTransaction(connection); ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(startAddress, count); request.setUnitID(slaveId); transaction.setRequest(request); transaction.execute(); ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse(); System.out.println("读取保持寄存器 (地址 " + startAddress + " - " + (startAddress + count - 1) + "):"); for (int i = 0; i < response.getWordCount(); i++) { int registerValue = response.getRegisterValue(i); System.out.println(" 地址 " + (startAddress + i) + ": " + registerValue + " (0x" + Integer.toHexString(registerValue) + ")"); } } catch (Exception e) { System.err.println("读取保持寄存器失败: " + e.getMessage()); } } /** * 读取输入寄存器 (功能码 04) * @param startAddress 起始地址 * @param count 读取数量 */ public void readInputRegisters(int startAddress, int count) { ModbusSerialTransaction transaction = null; try { transaction = new ModbusSerialTransaction(connection); ReadInputRegistersRequest request = new ReadInputRegistersRequest(startAddress, count); request.setUnitID(slaveId); transaction.setRequest(request); transaction.execute(); ReadInputRegistersResponse response = (ReadInputRegistersResponse) transaction.getResponse(); System.out.println("读取输入寄存器 (地址 " + startAddress + " - " + (startAddress + count - 1) + "):"); for (int i = 0; i < response.getWordCount(); i++) { int registerValue = response.getRegisterValue(i); System.out.println(" 地址 " + (startAddress + i) + ": " + registerValue + " (0x" + Integer.toHexString(registerValue) + ")"); } } catch (Exception e) { System.err.println("读取输入寄存器失败: " + e.getMessage()); } } /** * 获取校验位字符串表示 */ private String getParityString(int parity) { switch (parity) { case com.fazecast.jSerialComm.SerialPort.NO_PARITY: return "无"; case com.fazecast.jSerialComm.SerialPort.EVEN_PARITY: return "偶校验"; case com.fazecast.jSerialComm.SerialPort.ODD_PARITY: return "奇校验"; case com.fazecast.jSerialComm.SerialPort.MARK_PARITY: return "标记"; case com.fazecast.jSerialComm.SerialPort.SPACE_PARITY: return "空格"; default: return "未知"; } } /** * 设置超时时间 */ public void setTimeout(int timeout) { if (connection != null) { connection.setTimeout(timeout); } } /** * 主方法 - 使用示例 */ public static void main(String[] args) { // 配置串口参数 String portName = "COM2"; // Windows: COM1, COM2... Linux: /dev/ttyUSB0, /dev/ttyS0... int baudRate = 9600; // 波特率 int dataBits = 8; // 数据位 int stopBits = 1; // 停止位 int parity = com.fazecast.jSerialComm.SerialPort.NO_PARITY; // 校验位 int slaveId = 1; // 从站设备 ID // 创建 RTU 读取器实例 ModbusRtuSlaveReader reader = new ModbusRtuSlaveReader( portName, baudRate, dataBits, stopBits, parity, slaveId); try { // 设置超时时间(毫秒) reader.setTimeout(3000); // 建立连接 if (!reader.connect()) { return; } // 执行各种读取操作 // System.out.println("=线圈" ); // reader.readCoils(0, 10); // 读取线圈地址 0-4 // // System.out.println("=" .repeat(50)); // reader.readDiscreteInputs(0, 5); // 读取离散输入地址 0-4 System.out.println("=" .repeat(50)); reader.readHoldingRegisters(0, 10); // 读取保持寄存器地址 0-4 // System.out.println("=" .repeat(50)); // reader.readInputRegisters(0, 5); // 读取输入寄存器地址 0-4 } finally { // 确保连接关闭 reader.disconnect(); } } }注意事项
- 串口权限:在 Linux 系统上,可能需要设置串口权限(例如,使用
chmod命令)。 - 从站地址:确保从站地址正确,否则无法读取数据。
- 错误处理:Modbus 通信可能因超时或配置错误而失败,务必添加异常处理。
- 寄存器类型:根据设备文档,选择正确的读取方法(如
readInputRegisters用于输入寄存器)。 - 测试工具:建议使用 Modbus 测试工具(如 ModScan)验证设备响应,再集成到代码中。
如果遇到问题,请检查串口配置、设备地址和网络连接。j2mod 的文档和示例可在其 GitHub 仓库 中找到。