java 整合ModBus协议
·
需求是java 整合ModBus,使用RTU协议
引入依赖
当前使用环境是jdk17 + spring boot 3.5.5
<dependency>
<groupId>com.ghgande</groupId>
<artifactId>j2mod</artifactId>
<version>3.2.1</version>
</dependency>
添加ModBusConfig
采用条件注解开关
@ConditionalOnProperty(name = "modbus.enable", havingValue = "true")
如果配置开启 则注入该Bean其他关键信息都是采用配置
串口协议
波特率
停止位
校验位
@Configuration
@ConditionalOnProperty(name = "modbus.enable", havingValue = "true")
public class ModbusConfig {
// 从配置文件读取串口参数
@Value("${modbus.port:COM1}")
private String portName;
@Value("${modbus.baud-rate:9600}")
private int baudRate;
@Value("${modbus.data-bits:8}")
private int dataBits;
@Value("${modbus.parity:None}")
private String parity;
@Value("${modbus.stop-bits:1}")
private int stopBits;
@Value("${modbus.unit-id:1}")
private int unitId;
/**
* 创建Modbus RTU连接(单例模式,自动关闭)
*/
@Bean(destroyMethod = "close")
public SerialConnection serialConnection() {
try {
SerialParameters params = new SerialParameters();
params.setPortName(portName);
params.setBaudRate(baudRate);
params.setDatabits(dataBits);
params.setParity(parity);
params.setStopbits(stopBits);
params.setEncoding("rtu"); // RTU协议
params.setEcho(false);
SerialConnection connection = new SerialConnection(params);
connection.open(); // 启动时建立连接
return connection;
} catch (Exception e) {
throw new RuntimeException("Modbus连接初始化失败", e);
}
}
/**
* 注册Modbus服务(注入连接)
*/
@Bean
public ModbusService modbusService(SerialConnection connection) {
return new ModbusService();
}
}
yml配置
modbus:
enable: false
port: COM1 # 串口名称
baud-rate: 9600 # 波特率(与设备匹配)
data-bits: 8 # 数据位
parity: None # 校验位(None/Even/Odd)
stop-bits: 1 # 停止位
unit-id: 1 # 设备地址(从机ID)
timeout: 3000
读取寄存器数据
具体读取逻辑取决于寄存器上的数据内容和表达内容,对应解析即可
@Service
@ConditionalOnProperty(name = "modbus.enable", havingValue = "true")
@Slf4j
public class ModbusService {
@Value("${modbus.unit-id:1}")
private int unitId; // 设备地址
@Resource
private SerialConnection connection;
@Resource
private WindingShortMapper windingShortMapper;
/**
* 读取所有寄存器数据并转换为ModbusData对象
*/
public WindingShortPO readAllData() {
WindingShortPO windingShortPO = new WindingShortPO();
try {
// 1. 读取时间寄存器(40001-40006,共6个寄存器)
// 注意:Modbus寄存器地址通常为0-based,40001对应参考地址0
int[] timeRegisters = readRegisters(0, 6); // 起始地址0,读取6个寄存器
// 2. 读取highV1-highV16(40007-40022,共16个寄存器)
int[] highVRegisters = readRegisters(6, 16); // 读取16个
// 3. 读取lowV1及后续(40023-40038,共16个寄存器)
int[] lowVRegisters = readRegisters(22, 16); // 读取16个
// 4. 读取feature及后续(40039-40054,共16个寄存器)
int[] valueRegisters = readRegisters(38, 16); // 读取16个
// 5. 读取status及后续(40055-40070,共16个寄存器)
int[] statusRegisters = readRegisters(54, 16); // 读取16个
windingShortPO.setCollectTime(parseTime(timeRegisters));
windingShortPO.setHighV(arrayToCsv(highVRegisters));
windingShortPO.setLowV(arrayToCsv(lowVRegisters));
windingShortPO.setFeature(arrayToCsv(valueRegisters));
windingShortPO.setStatus(arrayToCsv(statusRegisters));
windingShortMapper.insert(windingShortPO);
} catch (Exception e) {
throw new RuntimeException("Modbus数据读取失败", e);
}
return windingShortPO;
}
/**
* 通用寄存器读取方法(读取保持寄存器,对应40001+系列)
* @param startRef 起始参考地址(0-based,40001对应0)
* @param count 读取数量
*/
private int[] readRegisters(int startRef, int count) throws Exception {
log.info("=== Modbus 读取配置 ===");
log.info("串口路径: {}", connection.getPortName());
log.info("波特率: {}", connection.getBaudRate());
log.info("数据位: {}", connection.getNumDataBits());
log.info("校验位: {}", connection.getParity());
log.info("停止位: {}", connection.getNumStopBits());
log.info("设备地址(unitId): {}", unitId);
log.info("请求参数: 起始地址={}, 读取数量={}", startRef, count);
log.info("======================");
ModbusSerialTransaction transaction = new ModbusSerialTransaction(connection);
ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(startRef, count);
request.setUnitID(unitId);
transaction.setRequest(request);
transaction.setRetries(1); // 设置重试次数
try {
transaction.execute();
ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse();
if (response != null) {
log.info("读取成功,返回寄存器数量: {}", response.getWordCount());
int[] values = new int[count];
for (int i = 0; i < count; i++) {
int rawValue = response.getRegisterValue(i); // 原始值 0~65535
values[i] = (short) rawValue; // 转为有符号 short,自动扩展为 int
}
return values;
} else {
log.error("读取失败: 未收到响应");
return new int[0];
}
} catch (ModbusIOException e) {
log.error("Modbus IO 异常: 无法从串口读取数据", e);
log.error("可能原因: 串口参数不匹配、设备未连接、权限不足或设备地址错误");
throw e;
}
}
/**
* 解析时间寄存器(40001-40006)为LocalDateTime
* 寄存器顺序:[年, 月, 日, 时, 分, 秒]
*/
private LocalDateTime parseTime(int[] timeRegisters) {
if (timeRegisters.length != 6) {
throw new IllegalArgumentException("时间寄存器数量错误");
}
int year = timeRegisters[0]; // 40001: 年(如2025)
int month = timeRegisters[1]; // 40002: 月(1-12)
int day = timeRegisters[2]; // 40003: 日(1-31)
int hour = timeRegisters[3]; // 40004: 时(0-23)
int minute = timeRegisters[4]; // 40005: 分(0-59)
int second = timeRegisters[5]; // 40006: 秒(0-59)
return LocalDateTime.of(year, month, day, hour, minute, second);
}
/**
* 将 int 数组转换为英文逗号分隔的字符串(如 [1,2,3] → "1,2,3")
*/
private String arrayToCsv(int[] array) {
if (array == null || array.length == 0) {
return ""; // 空数组返回空字符串(或根据需求返回 "null")
}
// 使用 Stream 流拼接数组元素
return Arrays.stream(array)
.mapToObj(String::valueOf) // 转为 String
.collect(Collectors.joining(",")); // 用逗号连接
}
}
更多推荐

所有评论(0)