和豆包,deepSeek友好的聊了一天,也是终于实现了这一个功能。。。
首先下载依赖
XML
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jserialcomm</artifactId>
<version>2.10.4</version>
</dependency>
完整的POM.xml文件如下
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>projectDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>ias-snapshots</id>
<name>Infinite Automation Snapshot Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-snapshot/</url>
</repository>
<repository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>ias-releases</id>
<name>Infinite Automation Release Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-release/</url>
</repository>
</repositories>
<dependencies>
<!--串口 jSerialComm 正常可用-->
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.10.4</version>
</dependency>
<!-- 稳定版modbus4j -->
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jserialcomm</artifactId>
<version>2.10.4</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
然后,创建两个工具类
java
package org.modbus.util;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import java.util.ArrayList;
import java.util.List;
public class Modbus4jUtils {
/**
* 工厂。
*/
static ModbusFactory modbusFactory;
static {
if (modbusFactory == null) {
modbusFactory = new ModbusFactory();
}
}
/**
* 获取master
*
* @return
* @throws ModbusInitException
*/
public static ModbusMaster getMaster(SerialPortWrapper wrapper, int type) {
if (type == 1) { //RTU
return modbusFactory.createRtuMaster(wrapper);
} else if (type == 2) { //ASC
return modbusFactory.createAsciiMaster(wrapper);
} else {
throw new RuntimeException("参数有误");
}
}
/**
* 获取master
*
* @return
* @throws ModbusInitException
*/
public static ModbusMaster getMaster(IpParameters params, Boolean keepAlive) {
if (keepAlive != null) { //TCP
return modbusFactory.createTcpMaster(params, keepAlive);
} else return modbusFactory.createUdpMaster(params); //UDP
}
/**
* 读取[01 Coil Status 0x]类型 开关数据
*
* @param slaveId slaveId
* @param startAddress 起始寄存器位置
* @param numberOfPoints 读取寄存器数量
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
* @throws ModbusInitException 异常
*/
public static BatchResults<Integer> readCoilStatus(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints)
throws ModbusTransportException, ErrorResponseException {
BatchRead<Integer> batch = new BatchRead<Integer>();
for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
batch.addLocator(j, BaseLocator.coilStatus(slaveId, i));
}
batch.setContiguousRequests(false);
BatchResults<Integer> results = master.send(batch);
return results;
}
/**
* 读取[02 Input Status 1x]类型 开关数据
*
* @param slaveId
* @param startAddress 起始寄存器位置
* @param numberOfPoints 读取寄存器数量
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static BatchResults<Integer> readInputStatus(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints)
throws ModbusTransportException, ErrorResponseException{
BatchRead<Integer> batch = new BatchRead<Integer>();
for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
batch.addLocator(j, BaseLocator.inputStatus(slaveId, i));
}
batch.setContiguousRequests(false);
BatchResults<Integer> results = master.send(batch);
return results;
}
/**
* 读取[03 Holding Register类型 2x]模拟量数据
*
* @param slaveId slave Id
* @param startAddress 起始寄存器位置
* @param numberOfPoints 读取寄存器数量
* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
* @return
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
*/
public static BatchResults<Integer> readHoldingRegister(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints, int dataType)
throws ModbusTransportException, ErrorResponseException {
BatchRead<Integer> batch = new BatchRead<Integer>();
for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
batch.addLocator(j, BaseLocator.holdingRegister(slaveId, i, dataType));
}
batch.setContiguousRequests(false);
BatchResults<Integer> results = master.send(batch);
return results;
}
/**
* 读取[04 Input Registers 3x]类型 模拟量数据
*
* @param slaveId slaveId
* @param startAddress 起始寄存器位置
* @param numberOfPoints 读取寄存器数量
* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
* @return 返回结果
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
*/
public static BatchResults<Integer> readInputRegisters(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints, int dataType)
throws ModbusTransportException, ErrorResponseException {
BatchRead<Integer> batch = new BatchRead<Integer>();
for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
batch.addLocator(j, BaseLocator.inputRegister(slaveId, i, dataType));
}
batch.setContiguousRequests(false);
BatchResults<Integer> results = master.send(batch);
return results;
}
/**
* 批量读取使用方法
*
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static void batchRead(ModbusMaster master) throws ModbusTransportException, ErrorResponseException, ModbusInitException {
BatchRead<Integer> batch = new BatchRead<Integer>();
batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
batch.addLocator(1, BaseLocator.inputStatus(1, 0));
batch.setContiguousRequests(false);
BatchResults<Integer> results = master.send(batch);
System.out.println(results.getValue(0));
System.out.println(results.getValue(1));
}
}
这个工具类封装了创建工厂,读取数据的所有方法
java
package org.modbus.util;
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import com.fazecast.jSerialComm.SerialPort;
import java.io.InputStream;
import java.io.OutputStream;
public class SerialPortWrapperJSerialImpl implements SerialPortWrapper {
private SerialPort serialPort;
private final String portName;
private final int baudRate;
private final int dataBits;
private final int stopBits;
private final int parity;
public SerialPortWrapperJSerialImpl(String portName, int baudRate, int dataBits, int stopBits, int parity) {
this.portName = portName;
this.baudRate = baudRate;
this.dataBits = dataBits;
this.stopBits = stopBits;
this.parity = parity;
}
@Override
public int getBaudRate() {
return baudRate;
}
@Override
public int getDataBits() {
return dataBits;
}
@Override
public int getStopBits() {
return stopBits;
}
@Override
public int getParity() {
return parity;
}
@Override
public int getFlowControlIn() {
return 0;
}
@Override
public int getFlowControlOut() {
return 0;
}
@Override
public void open() throws Exception {
if (serialPort != null && serialPort.isOpen()) {
return;
}
serialPort = SerialPort.getCommPort(portName);
serialPort.setBaudRate(baudRate);
serialPort.setNumDataBits(dataBits);
serialPort.setNumStopBits(stopBits);
// 转换modbus4j parity常量到jSerialComm
switch (parity) {
case 0:
serialPort.setParity(SerialPort.NO_PARITY);
break;
case 1:
serialPort.setParity(SerialPort.ODD_PARITY);
break;
case 2:
serialPort.setParity(SerialPort.EVEN_PARITY);
break;
default:
serialPort.setParity(SerialPort.NO_PARITY);
}
// 关闭硬件流控,485不需要
serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
// 超时防止阻塞
serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1000, 0);
serialPort.openPort();
}
@Override
public void close() throws Exception {
if (serialPort != null && serialPort.isOpen()) {
serialPort.closePort();
}
serialPort = null;
}
@Override
public InputStream getInputStream() {
return serialPort.getInputStream();
}
@Override
public OutputStream getOutputStream() {
return serialPort.getOutputStream();
}
}
这个工具用来初始化串口
好了,一切准备就绪,开始测试
java
package org.modbus;
import com.fazecast.jSerialComm.SerialPort;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import org.modbus.util.Modbus4jUtils;
import org.modbus.util.SerialPortWrapperJSerialImpl;
public class Demo02 {
public static void main(String[] args) throws ModbusTransportException, ErrorResponseException, ModbusInitException {
redTest();
}
private static void redTest() throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 1. 实例化JSSC串口包装类,8N1无校验
SerialPortWrapper wrapper = new SerialPortWrapperJSerialImpl("COM1",9600,8,1,0);
ModbusMaster master = Modbus4jUtils.getMaster(wrapper, 1);
master.init();
//读取从站1的保持型寄存器0~9
BatchResults<Integer> res = Modbus4jUtils.readHoldingRegister(master, 1, 0, 9, DataType.TWO_BYTE_INT_UNSIGNED);
System.out.println(res);
//读取从站1的线圈状态0~7
BatchResults<Integer> coils = Modbus4jUtils.readCoilStatus(master,1,0,8);
System.out.println(coils);
//读取从站1的输入线圈0~7
BatchResults<Integer> inputs = Modbus4jUtils.readInputStatus(master,1,0,8);
System.out.println(inputs);
//读取从站5的输入寄存器0~4
BatchResults<Integer> inputRegisters = Modbus4jUtils.readInputRegisters(master,5,0,5,DataType.TWO_BYTE_INT_UNSIGNED);
System.out.println(inputRegisters);
}
}
配置主站(我这里使用的是虚拟的)

然后我们run一下,看一下效果

家人们,牛逼坏了我,搞了一天啊!!!