本文只对 写保持寄存器 HoldingRegister 做操作,其他类型的寄存器方法方法也在ModbusWriteOrRead类中,可自行测试。
报警器设备型号(USB版):JD01AX07 01
设备外观及亮灯:
文档说明-部分:
注: 以下图第一个绿灯开启的二进制命令为例:
01 06:寄存器类型
00 00:寄存器地址
00 01:数据(打开)
48 0A:CRC校验码
接下来上代码:没有yml配置,需要将配置写入静态代码,如果有可以在静态代码中读取配置信息的方法请@我修改,谢谢~
maven配置:因为引入了外部lib,所以一定要加jar包路径
<dependency>
<groupId>jssc</groupId>
<artifactId>jssc</artifactId>
<version>2.8.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/libs/jssc.jar</systemPath>
</dependency>
<dependency>
<groupId>com.serotonin.modbus4j</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/libs/modbus4j-3.0.3.jar</systemPath>
</dependency>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- <executable>true</executable>-->
<!-- <fork>true</fork>-->
<!--值为true是指打包时包含scope为system的第三方Jar包-->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
文章后面有lib附件,直接引用
需要注意的地方:当设备插入时会使用某一个空闲串口,打开计算机管理,找到使用的串口,替换到工具类的串口参数。(虚拟机串口查询:ls /dev/tty*)
工具类封装:
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.locator.BaseLocator;
import com.serotonin.modbus4j.msg.WriteCoilRequest;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteCoilsRequest;
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ModbusWriteOrRead {
//设备id
private static final Integer SLAVE_ID = 1;
// master 工厂
static ModbusFactory modbusFactory;
// master 对象
static ModbusMaster master;
// 静态方法初始化 master
static {
modbusFactory = new ModbusFactory();
// 使用 Modbus-TCP 进行通信
// IpParameters param = new IpParameters();
// param.setHost("localhost");
// param.setPort(502);
// master = modbusFactory.createTcpMaster(param, false);
// 使用 Modbus-RTU 进行通信 // 设置串口参数,串口是COM12,波特率是9600,数据长8字节
SerialPortWrapper serialParameters = new SerialPortWrapperImpl("COM5", 9600,
8, 1, 0, 0, 0);
master = modbusFactory.createRtuMaster(serialParameters);
try {
//设置超时时间
master.setTimeout(3000);
//设置重连次数
master.setRetries(3);
master.init();
} catch (ModbusInitException e) {
log.error("master 初始化失败~");
throw new RuntimeException("master 初始化失败~");
}
}
/**
* 主方法测试
* 绿灯黄灯亮度红灯默认亮度8080, 第一个80指的是红灯亮度,16进制80对应十进制128,为亮度最大值的一半,第二个80是绿灯亮度,含义同红灯,当设置十进制65535时对应16进制的FFFF,FF对应255,此时为最亮
* 注:已调最大亮度,红绿黄亮度值最大
*/
public static void maain(String[] args) throws ModbusTransportException, ErrorResponseException {
// writeRegister(SLAVE_ID, SlaveOffsetEnum.OFFSET_CONTROL_RED_OFF.getOffset(), SlaveOffsetEnum.OFFSET_CONTROL_RED_OFF.getValue(), DataType.TWO_BYTE_INT_UNSIGNED);
// writeRegister(SLAVE_ID, 3, 255, DataType.TWO_BYTE_INT_UNSIGNED);
// writeRegister(SLAVE_ID, 1, 0, DataType.TWO_BYTE_INT_UNSIGNED);
// writeRegister(SLAVE_ID, 2, 0, DataType.TWO_BYTE_INT_UNSIGNED);
// writeRegister(SLAVE_ID, 5, 0, DataType.TWO_BYTE_INT_UNSIGNED);
batchRead();
// writeRegister(SLAVE_ID, SlaveOffsetEnum.OFFSET_CONTROL_RED_ON.getOffset(), SlaveOffsetEnum.OFFSET_CONTROL_RED_ON.getValue(), DataType.TWO_BYTE_INT_UNSIGNED);
// System.out.println("=====读线圈 CoilStatus=====");
// System.out.println("0>>>" + readCoilStatus(1, 0));
// System.out.println("5>>>" + readCoilStatus(1, 5));
// System.out.println("=====读离散量输入 InputStatus=====");
// System.out.println("7>>>" + readInputStatus(1, 7));
// System.out.println("8>>>" + readInputStatus(1, 8));
// System.out.println("=====读保持寄存器 HoldingRegister=====");
// System.out.println("0>>>" + readHoldingRegister(1, 0, DataType.FOUR_BYTE_INT_SIGNED));
// System.out.println("2>>>" + readHoldingRegister(1, 2, DataType.FOUR_BYTE_INT_SIGNED));
// System.out.println("=====读输入寄存器 InputRegisters=====");
// System.out.println("6>>>" + readInputRegisters(2, 1, DataType.FOUR_BYTE_INT_SIGNED));
// System.out.println("8>>>" + readInputRegisters(2, 2, DataType.FOUR_BYTE_INT_SIGNED));
// System.out.println("=====测试批量读取=====");
// batchRead();
}
/**
* 读线圈 CoilStatus
*
* @param slaveId 从机id
* @param offset 偏移量
* @return 读取数据值
*/
public static Boolean readCoilStatus(int slaveId, int offset)
throws ModbusTransportException, ErrorResponseException {
BaseLocator<Boolean> locator = BaseLocator.coilStatus(slaveId, offset);
return master.getValue(locator);
}
/**
* 读离散量输入 InputStatus
*
* @param slaveId 从机id
* @param offset 偏移量
* @return 读取数据值
*/
public static Boolean readInputStatus(int slaveId, int offset)
throws ModbusTransportException, ErrorResponseException {
BaseLocator<Boolean> locator = BaseLocator.inputStatus(slaveId, offset);
return master.getValue(locator);
}
/**
* 读保持寄存器 HoldingRegister
*
* @param slaveId 从机id
* @param offset 偏移量
* @param dataType 数据类型
* @return 读取数据值
*/
public static Number readHoldingRegister(int slaveId, int offset, int dataType)
throws ModbusTransportException, ErrorResponseException {
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
return master.getValue(locator);
}
/**
* 读输入寄存器 InputRegisters
*
* @param slaveId 从机id
* @param offset 偏移量
* @param dataType 数据类型
* @return 读取数据值
*/
public static Number readInputRegisters(int slaveId, int offset, int dataType)
throws ModbusTransportException, ErrorResponseException {
BaseLocator<Number> locator = BaseLocator.inputRegister(slaveId, offset, dataType);
return master.getValue(locator);
}
/**
* 写线圈 CoilStatus
*
* @param slaveId 从机id
* @param writeOffset 偏移量
* @param writeValue 写入值
* @return 写入结果
*/
public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)
throws ModbusTransportException {
// 创建请求
WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
// 发送请求并获取响应对象
WriteCoilResponse response = (WriteCoilResponse) master.send(request);
return !response.isException();
}
/**
* 批量写线圈 CoilStatus
*
* @param slaveId 从机id
* @param startOffset 写入起始偏移量
* @param bdata 写入数据集
* @return 写入结果
*/
public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)
throws ModbusTransportException {
// 创建请求
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
// 发送请求并获取响应对象
WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);
return !response.isException();
}
/**
* 写保持寄存器 HoldingRegister
*
* @param slaveId 从机id
* @param writeOffset 偏移量
* @param writeValue 写入值
* @param dataType 写入值数据类型
*/
public static void writeRegister(int slaveId, int writeOffset, Number writeValue, int dataType)
throws ModbusTransportException, ErrorResponseException {
// 创建寻址对象
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, writeOffset, dataType);
// 执行写入操作
master.setValue(locator, writeValue);
}
/**
* @param
* @Description: 批量读取使用方法
* @return:
* @Author: hyh
* @Date: 2023/6/12 15:56
*/
public static void batchRead() throws ModbusTransportException, ErrorResponseException {
log.info("绿灯 寄存器地址:0值= " + readHoldingRegister(SLAVE_ID, 0, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("黄灯 寄存器地址:1值= " + readHoldingRegister(SLAVE_ID, 1, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("红灯 寄存器地址:2值= " + readHoldingRegister(SLAVE_ID, 2, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("亮度 寄存器地址:3值= " + readHoldingRegister(SLAVE_ID, 3, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("寄存器地址:4值= " + readHoldingRegister(SLAVE_ID, 4, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("蜂鸣器 寄存器地址:5值= " + readHoldingRegister(SLAVE_ID, 5, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("寄存器地址:6值= " + readHoldingRegister(SLAVE_ID, 6, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("寄存器地址:7值= " + readHoldingRegister(SLAVE_ID, 7, DataType.TWO_BYTE_INT_UNSIGNED).toString());
log.info("寄存器地址:8值= " + readHoldingRegister(SLAVE_ID, 8, DataType.TWO_BYTE_INT_UNSIGNED).toString());
}
// public static void batchRead() throws ModbusTransportException, ErrorResponseException {
//
// BatchRead<Integer> batch = new BatchRead<Integer>();
//
batch.addLocator(0, BaseLocator.coilStatus(1, 0));
batch.addLocator(1, BaseLocator.inputStatus(1, 7));
batch.addLocator(2, BaseLocator.holdingRegister(1, 0, DataType.FOUR_BYTE_FLOAT));
batch.addLocator(3, BaseLocator.inputRegister(1, 6, DataType.FOUR_BYTE_FLOAT));
//
// batch.addLocator(0, BaseLocator.holdingRegister(1, 0, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(1, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(2, BaseLocator.holdingRegister(1, 2, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(3, BaseLocator.holdingRegister(1, 3, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(4, BaseLocator.holdingRegister(1, 4, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(5, BaseLocator.holdingRegister(1, 5, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(6, BaseLocator.holdingRegister(1, 6, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(7, BaseLocator.holdingRegister(1, 7, DataType.FOUR_BYTE_FLOAT));
// batch.addLocator(8, BaseLocator.holdingRegister(1, 8, DataType.FOUR_BYTE_FLOAT));
// batch.setContiguousRequests(false);
// BatchResults<Integer> results = master.send(batch);
// }
}
连接及发送方法重写:
package com.vcom.acoustooptic.utils;
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import jssc.SerialPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.io.OutputStream;
/**
*
*/
public class SerialPortWrapperImpl implements SerialPortWrapper {
private static final Logger LOG = LoggerFactory.getLogger(SerialPortWrapperImpl.class);
private final SerialPort port;
private final int baudRate;
private final int dataBits;
private final int stopBits;
private final int parity;
private final int flowControlIn;
private final int flowControlOut;
public SerialPortWrapperImpl(String commPortId, int baudRate, int dataBits, int stopBits,
int parity, int flowControlIn, int flowControlOut) {
// 设置端口波特率
this.baudRate = baudRate;
//设定数据位的位数 RTU:8位 ASCII:7位
this.dataBits = dataBits;
//停止位的位数,如果无奇偶校验为2,有奇偶校验为1
this.stopBits = stopBits;
//奇偶校验位 无校验:0 奇校验:1 偶校验:2
this.parity = parity;
//硬件之间输入流应答控制
this.flowControlIn = flowControlIn;
//硬件之间输出流应答控制
this.flowControlOut = flowControlOut;
// 设定MODBUS通讯的串行口
port = new SerialPort(commPortId);
}
@Override
public void close() throws Exception {
port.closePort();
//listeners.forEach(PortConnectionListener::closed);
LOG.debug("Serial port {} closed", port.getPortName());
}
@Override
public void open() {
try {
port.openPort();
port.setParams(this.getBaudRate(), this.getDataBits(), this.getStopBits(), this.getParity());
port.setFlowControlMode(this.getFlowControlIn() | this.getFlowControlOut());
//listeners.forEach(PortConnectionListener::opened);
LOG.debug("Serial port {} opened", port.getPortName());
} catch (Exception ex) {
LOG.error("Error opening port : {} for {} ", port.getPortName(), ex);
}
}
@Override
public InputStream getInputStream() {
return new SerialInputStream(port);
}
@Override
public OutputStream getOutputStream() {
return new SerialOutputStream(port);
}
@Override
public int getBaudRate() {
return baudRate;
//return SerialPort.BAUDRATE_9600;
}
@Override
public int getFlowControlIn() {
return flowControlIn;
//return SerialPort.FLOWCONTROL_NONE;
}
@Override
public int getFlowControlOut() {
return flowControlOut;
//return SerialPort.FLOWCONTROL_NONE;
}
@Override
public int getDataBits() {
return dataBits;
//return SerialPort.DATABITS_8;
}
@Override
public int getStopBits() {
return stopBits;
//return SerialPort.STOPBITS_1;
}
@Override
public int getParity() {
return parity;
//return SerialPort.PARITY_NONE;
}
}
注意:DataType-写入读取类型要一致
TWO_BYTE_INT_UNSIGNED 这个是写入到一个寄存器里面
FOUR_BYTE_INT_UNSIGNED 这个是写入到多个寄存器