前言
项目中需要使用串口来连接操控烟雾报警器
且只能使用modbus-rtu协议
在找了一堆资料后终于成功了在此呈上代码和资料链接
【ModBus】modbus之modbus4j的使用和流程原理解析(5)-CSDN博客
使用modbus4j通过串口解析modbus协议(java)_java modbus4j-CSDN博客
串口通讯需要使用modbus4j包
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
maven拉取modbus4j包时拉取不下来
去官网下载jar引入本地maven仓库
执行下述命令
mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=<packaging>
<path-to-file>: 要安装的JAR的本地路径 ./libs/json-simple-1.1.1.jar
<group-id>:要安装的JAR的Group Id
<artifact-id>: 要安装的JAR的 Artificial Id
<version>: JAR 版本
<packaging>: 打包类型,例如JAR\
下面是本人使用的命令(不过是另一个第三方jar包)
mvn install:install-file -Dfile=D:\Users\Administrator\IdeaProjects\campus-screen\ruoyi-admin\lib\examples.jar -DgroupId=com.sun.jna -DartifactId=examples -Dversion=1.0 -Dpackaging=jar
上面的命令解释:
-Dfile:指明你当前jar包的位置(就是第1步存放jar的路径+jar包名);
-DgroupId, -DartifactId, -Dversion:三个参数,就是指明了存放maven仓库中的位置;
-Dpackaging :猜测就是指明文件类型;
Maven Repository: com.infiniteautomation » modbus4j » 3.0.5 (mvnrepository.com)
下述代码直接使用即可
package com.ruoyi.system.domain.iot.agreement.detector;
/**
*
* Copyright (c) 2009-2020 Freedomotic Team http://www.freedomotic-iot.com
*
* This file is part of Freedomotic
*
* This Program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2, or (at your option) any later version.
*
* This Program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* Freedomotic; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import jssc.SerialPort;
import jssc.SerialPortException;
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 SerialPort port;
private String commPortId;
private int baudRate;
private int dataBits;
private int stopBits;
private int parity;
private int flowControlIn;
private int flowControlOut;
public SerialPortWrapperImpl(String commPortId, int baudRate, int dataBits, int stopBits, int parity, int flowControlIn,
int flowControlOut) {
this.commPortId = commPortId;
this.baudRate = baudRate;
this.dataBits = dataBits;
this.stopBits = stopBits;
this.parity = parity;
this.flowControlIn = flowControlIn;
this.flowControlOut = flowControlOut;
port = new SerialPort(this.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 (SerialPortException 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;
}
}
package com.ruoyi.system.domain.iot.agreement.detector;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
import jssc.SerialPort;
import java.io.IOException;
import java.io.InputStream;
/**
* Class that wraps a {@link SerialPort} to provide {@link InputStream}
* functionality. This stream also provides support for performing blocking
* reads with timeouts.
* <br>
* It is instantiated by passing the constructor a {@link SerialPort} instance.
* Do not create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @author Charles Hache <[email protected]>
*
* Attribution: https://github.com/therealchalz/java-simple-serial-connector
*
*/
public class SerialInputStream extends InputStream {
private SerialPort serialPort;
private int defaultTimeout = 0;
/**
* Instantiates a SerialInputStream for the given {@link SerialPort} Do not
* create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @param sp The serial port to stream.
*/
public SerialInputStream(SerialPort sp) {
serialPort = sp;
}
/**
* Set the default timeout (ms) of this SerialInputStream. This affects
* subsequent calls to {@link #read()}, {@link #(int[])}, and
* {@link #(int[], int, int)} The default timeout can be 'unset'
* by setting it to 0.
*
* @param time The timeout in milliseconds.
*/
public void setTimeout(int time) {
defaultTimeout = time;
}
/**
* Reads the next byte from the port. If the timeout of this stream has been
* set, then this method blocks until data is available or until the timeout
* has been hit. If the timeout is not set or has been set to 0, then this
* method blocks indefinitely.
*/
@Override
public int read() throws IOException {
return read(defaultTimeout);
}
/**
* The same contract as {@link #read()}, except overrides this stream's
* default timeout with the given timeout in milliseconds.
*
* @param timeout The timeout in milliseconds.
* @return The read byte.
* @throws IOException On serial port error or timeout
*/
public int read(int timeout) throws IOException {
byte[] buf = new byte[1];
try {
if (timeout > 0) {
buf = serialPort.readBytes(1, timeout);
} else {
buf = serialPort.readBytes(1);
}
return buf[0];
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* Non-blocking read of up to buf.length bytes from the stream. This call
* behaves as read(buf, 0, buf.length) would.
*
* @param buf The buffer to fill.
* @return The number of bytes read, which can be 0.
* @throws IOException on error.
*/
@Override
public int read(byte[] buf) throws IOException {
return read(buf, 0, buf.length);
}
/**
* Non-blocking read of up to length bytes from the stream. This method
* returns what is immediately available in the input buffer.
*
* @param buf The buffer to fill.
* @param offset The offset into the buffer to start copying data.
* @param length The maximum number of bytes to read.
* @return The actual number of bytes read, which can be 0.
* @throws IOException on error.
*/
@Override
public int read(byte[] buf, int offset, int length) throws IOException {
if (buf.length < offset + length) {
length = buf.length - offset;
}
int available = this.available();
if (available > length) {
available = length;
}
try {
byte[] readBuf = serialPort.readBytes(available);
// System.arraycopy(readBuf, 0, buf, offset, length);
System.arraycopy(readBuf, 0, buf, offset, readBuf.length);
return readBuf.length;
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* Blocks until buf.length bytes are read, an error occurs, or the default
* timeout is hit (if specified). This behaves as blockingRead(buf, 0,
* buf.length) would.
*
* @param buf The buffer to fill with data.
* @return The number of bytes read.
* @throws IOException On error or timeout.
*/
public int blockingRead(byte[] buf) throws IOException {
return blockingRead(buf, 0, buf.length, defaultTimeout);
}
/**
* The same contract as {@link #blockingRead(byte[])} except overrides this
* stream's default timeout with the given one.
*
* @param buf The buffer to fill.
* @param timeout The timeout in milliseconds.
* @return The number of bytes read.
* @throws IOException On error or timeout.
*/
public int blockingRead(byte[] buf, int timeout) throws IOException {
return blockingRead(buf, 0, buf.length, timeout);
}
/**
* Blocks until length bytes are read, an error occurs, or the default
* timeout is hit (if specified). Saves the data into the given buffer at
* the specified offset. If the stream's timeout is not set, behaves as
* {@link #read(byte[], int, int)} would.
*
* @param buf The buffer to fill.
* @param offset The offset in buffer to save the data.
* @param length The number of bytes to read.
* @return the number of bytes read.
* @throws IOException on error or timeout.
*/
public int blockingRead(byte[] buf, int offset, int length) throws IOException {
return blockingRead(buf, offset, length, defaultTimeout);
}
/**
* The same contract as {@link #blockingRead(byte[], int, int)} except
* overrides this stream's default timeout with the given one.
*
* @param buf The buffer to fill.
* @param offset Offset in the buffer to start saving data.
* @param length The number of bytes to read.
* @param timeout The timeout in milliseconds.
* @return The number of bytes read.
* @throws IOException On error or timeout.
*/
public int blockingRead(byte[] buf, int offset, int length, int timeout) throws IOException {
if (buf.length < offset + length) {
throw new IOException("Not enough buffer space for serial data");
}
if (timeout < 1) {
return read(buf, offset, length);
}
try {
byte[] readBuf = serialPort.readBytes(length, timeout);
System.arraycopy(readBuf, 0, buf, offset, length);
return readBuf.length;
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
public int available() throws IOException {
int ret;
try {
ret = serialPort.getInputBufferBytesCount();
if (ret >= 0) {
return ret;
}
throw new IOException("Error checking available bytes from the serial port.");
} catch (Exception e) {
throw new IOException("Error checking available bytes from the serial port.");
}
}
}
package com.ruoyi.system.domain.iot.agreement.detector;
/**
*
* Copyright (c) 2009-2020 Freedomotic Team http://www.freedomotic-iot.com
*
* This file is part of Freedomotic
*
* This Program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2, or (at your option) any later version.
*
* This Program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* Freedomotic; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
import jssc.SerialPort;
import jssc.SerialPortException;
import java.io.IOException;
import java.io.OutputStream;
/**
* Class that wraps a {@link SerialPort} to provide {@link OutputStream}
* functionality.
* <br>
* It is instantiated by passing the constructor a {@link SerialPort} instance.
* Do not create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @author Charles Hache <[email protected]>
*
* Attribution: https://github.com/therealchalz/java-simple-serial-connector
*
*/
public class SerialOutputStream extends OutputStream {
SerialPort serialPort;
/**
* Instantiates a SerialOutputStream for the given {@link SerialPort} Do not
* create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @param sp The serial port to stream.
*/
public SerialOutputStream(SerialPort sp) {
serialPort = sp;
}
@Override
public void write(int b) throws IOException {
try {
serialPort.writeInt(b);
} catch (SerialPortException e) {
throw new IOException(e);
}
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
byte[] buffer = new byte[len];
System.arraycopy(b, off, buffer, 0, len);
try {
serialPort.writeBytes(buffer);
} catch (SerialPortException e) {
throw new IOException(e);
}
}
}
package com.ruoyi.system.domain.iot.agreement.detector;
/**
*
* Copyright (c) 2009-2020 Freedomotic Team http://www.freedomotic-iot.com
*
* This file is part of Freedomotic
*
* This Program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2, or (at your option) any later version.
*
* This Program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* Freedomotic; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import jssc.SerialPort;
import jssc.SerialPortException;
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 SerialPort port;
private String commPortId;
private int baudRate;
private int dataBits;
private int stopBits;
private int parity;
private int flowControlIn;
private int flowControlOut;
public SerialPortWrapperImpl(String commPortId, int baudRate, int dataBits, int stopBits, int parity, int flowControlIn,
int flowControlOut) {
this.commPortId = commPortId;
this.baudRate = baudRate;
this.dataBits = dataBits;
this.stopBits = stopBits;
this.parity = parity;
this.flowControlIn = flowControlIn;
this.flowControlOut = flowControlOut;
port = new SerialPort(this.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 (SerialPortException 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;
}
}
连接设备 COM3换成你自己的串口号,9600换成你的波特率
ModbusUtils.getSerialPortRtuMaster("COM3", 9600, 8, 2, 0);
读寄存器
/**
* 读寄存器
* @param slaveId 设备地址
* @param startOffset 寄存器地址
* @param numberOfRegisters 查询寄存器的数量吧 (我也不太懂)
* @throws ModbusTransportException
*/
@Anonymous
@PostMapping("read")
public void read(int slaveId,int startOffset, int numberOfRegisters) throws ModbusTransportException {
ReadInputRegistersRequest readRequest = new ReadInputRegistersRequest(slaveId, startOffset, numberOfRegisters);
// 发送请求并接收响应
ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(readRequest);
if (response.isException())
System.out.println("Exception response: message=" + response.getExceptionMessage());
else
System.out.println(Arrays.toString(response.getShortData()));
}
写寄存器
/**
* 写寄存器
* @param slaveId 设备地址
* @param writeOffset 寄存器地址
* @param writeValue value值
* @throws ModbusTransportException
*/
@Anonymous
@PostMapping("write")
public void write(int slaveId,int writeOffset,int writeValue) throws ModbusTransportException {
WriteRegisterRequest writeRequest = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
// 发送请求并接收响应
WriteRegisterResponse response = (WriteRegisterResponse) master.send(writeRequest);
if (response.isException())
System.out.println("Exception response: message=" + response.getExceptionMessage());
else
System.out.println("写入寄存器成功");
}
只连了一个设备的话设备地址(slaveId)应该都是1默认的
寄存器地址去看设备说明书
第二个寄存器地址(writeOffset)是0033H转成10进制时51
value就是传给寄存器的值