
本文详细讲解如何使用 jamod 库通过串口(Modbus RTU 协议)向从站设备的指定地址写入多个保持寄存器,重点解决 WriteMultipleRegistersRequest 构造与执行失败的问题,并提供可直接运行的健壮示例代码。
本文详细讲解如何使用 jamod 库通过串口(modbus rtu 协议)向从站设备的指定地址写入多个保持寄存器,重点解决 `writemultipleregistersrequest` 构造与执行失败的问题,并提供可直接运行的健壮示例代码。
在 Java 中基于 jamod(如经典版本 jamod-1.2 或兼容 fork)实现 Modbus RTU 写操作时,常见错误源于对 WriteMultipleRegistersRequest 构造方式的理解偏差:该请求不能仅设置地址和 unit ID 后空置寄存器数据,必须显式传入待写入的 Register[] 数组。你原代码中调用 ModbusRequest.createModbusRequest(0x05) 是无效的(0x05 是写单线圈功能码,非多寄存器写),且未初始化寄存器值,导致协议帧不合法,设备拒绝响应。
以下是正确、完整的写入实现(以向地址 55 开始的 2 个保持寄存器写入当前时间戳为例):
✅ 正确写入步骤与代码
import net.wimpi.modbus.Modbus;
import net.wimpi.modbus.io.ModbusSerialTransaction;
import net.wimpi.modbus.msg.WriteMultipleRegistersRequest;
import net.wimpi.modbus.msg.WriteMultipleRegistersResponse;
import net.wimpi.modbus.procimg.SimpleRegister;
import net.wimpi.modbus.serial.SerialConnection;
import net.wimpi.modbus.serial.SerialParameters;
public static void writeCurrentTimeToHoldingRegisters() {
SerialConnection con = null;
ModbusSerialTransaction trans = null;
final int unitId = 1;
final int startAddress = 55; // 目标起始寄存器地址(0-based)
final int registerCount = 2; // 写入 2 个寄存器(例如:高位+低位表示 32 位时间戳)
// 1. 构造待写入数据:将当前毫秒时间戳拆分为两个 16 位寄存器
long now = System.currentTimeMillis();
Register[] registers = new Register[registerCount];
registers[0] = new SimpleRegister((int) ((now >> 16) & 0xFFFF)); // 高16位
registers[1] = new SimpleRegister((int) (now & 0xFFFF)); // 低16位
try {
// 2. 配置串口参数(务必与设备完全一致!)
SerialParameters params = new SerialParameters();
params.setPortName(SelectCOM.ComPortBox.getSelectedItem().toString());
params.setBaudRate(SelectCOM.BaudRateBox.getSelectedItem().toString());
params.setDatabits(SelectCOM.DataBitBox.getSelectedItem().toString());
params.setParity(SelectCOM.ParityBox.getSelectedItem().toString());
params.setStopbits(SelectCOM.StopBitBox.getSelectedItem().toString());
params.setEncoding("rtu");
params.setEcho(false);
con = new SerialConnection(params);
con.open();
// 3. 创建并配置写请求(关键:必须传入 address + registers 数组)
WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(startAddress, registers);
request.setUnitID(unitId);
request.setHeadless(); // 启用无头模式(不自动添加 MBAP 头,RTU 模式必需)
trans = new ModbusSerialTransaction(con);
trans.setRequest(request);
// 4. 执行写操作
trans.execute();
WriteMultipleRegistersResponse response = (WriteMultipleRegistersResponse) trans.getResponse();
System.out.println("✅ 成功写入 " + registerCount + " 个寄存器到地址 " + startAddress);
} catch (Exception e) {
e.printStackTrace();
System.err.println("❌ 写入失败:" + e.getMessage());
} finally {
// 5. 安全关闭资源
if (trans != null) trans.close();
if (con != null && con.isOpen()) {
try { con.close(); } catch (Exception ignored) {}
}
}
}
⚠️ 关键注意事项
-
寄存器地址偏移:Modbus 协议中,功能码 0x10(Write Multiple Registers)操作的是保持寄存器(Holding Registers),地址通常以 4xxxx 表示(如 40055 对应寄存器地址 55)。代码中
startAddress = 55即对应 40055,无需额外加 40000。 -
数据格式匹配:确保写入的
SimpleRegister值为int类型(0–65535),超出范围会截断。若需写入浮点数或 32 位整数,请按 IEEE754 或高低字节顺序正确拆分。 -
串口稳定性:RTU 对时序敏感,务必保证
SerialParameters(波特率、校验位等)与从站设备完全一致;建议在con.open()后添加Thread.sleep(50)避免端口未就绪。 -
异常处理增强:生产环境应捕获
ModbusIOException(通信失败)、ModbusSlaveException(设备返回异常码,如非法地址/值)并做重试或告警。 -
线程安全:
ModbusSerialTransaction和SerialConnection非线程安全,高并发场景需加锁或使用连接池。
✅ 验证建议
写入后,立即调用你已有的 MGo() 方法读取地址 55–56 的值,对比是否与写入的 now 时间戳一致,即可闭环验证整个读写链路。
掌握此模式后,你可轻松扩展为写任意数量寄存器、动态构造数据数组,真正实现工业级 Modbus RTU 设备控制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











