Java Modbus RTU整合与虚拟串口测试:一站式指南
目的
在无设备的情况下,电脑搭建虚拟环境,进行代码编写以及测试
准备工作
一.测试工具
1.模拟虚拟串口工具,模式数据发送(主站),模拟数据接收(从站)
modbus RTU使用一问一答模式进行数据交互。
本文内容:主站发送读取数据请求,从站应答并返回数据
2.个人设置:COM2为主站,COM3为从站。所以测试类的串口,使用的COM2
工具下载链接: 下载地址
工具使用方式:参考链接
二.java读取寄存器数据
2.1 依赖包`java
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.9.2</version>
</dependency>
2.2 工具类
说明:发起读取数据请求时,将十进制数据转换为协议所使用的格式
package org.dromara.modbusTest;
public class ModbusRTURequestBuilder {
/**
* @param slaveAddress 从站地址
* @param functionCode 功能码
* @param startAddress 起始寄存器地址
* @param registerCount 寄存器数量
* @return Modbus-RTU 协议请求报文
*/
public static byte[] buildModbusRTURequest(int slaveAddress, int functionCode, int startAddress, int registerCount) {
// 创建一个字节数组用于存储报文
byte[] request = new byte[8];
// 设置从机地址
request[0] = (byte) slaveAddress;
// 设置功能码
request[1] = (byte) functionCode;
// 设置起始寄存器地址(高位和低位)
request[2] = (byte) (startAddress >> 8);
request[3] = (byte) (startAddress & 0xFF);
// 设置寄存器数量(高位和低位)
request[4] = (byte) (registerCount >> 8);
request[5] = (byte) (registerCount & 0xFF);
// 计算CRC校验并设置到报文中
int crc = calculateCRC(request, 6);
request[6] = (byte) (crc & 0xFF); // 低位
request[7] = (byte) (crc >> 8); // 高位
return request;
}
private static int calculateCRC(byte[] message, int length) {
int crc = 0xFFFF;
for (int i = 0; i < length; i++) {
crc ^= (message[i] & 0xFF);
for (int j = 0; j < 8; j++) {
if ((crc & 1) == 1) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc = crc >> 1;
}
}
}
return crc;
}
}
2.3 测试类
说明:主站(COM2)发起读取数据请求,对应的COM3接收到请求后,返回对应的数据。
package org.dromara.modbusTest;
import com.fazecast.jSerialComm.SerialPort;
import java.util.Arrays;
public class SerialSensorReader {
public static void main(String[] args) {
// 设置串口一些参数,除了串口号其他基本固定 COM2就是文章前配置的
SerialPort serialPort = SerialPort.getCommPort("COM2"); // 串口设备名称
serialPort.setBaudRate(9600); // 波特率
serialPort.setNumDataBits(8); // 数据位
serialPort.setParity(SerialPort.NO_PARITY); // 校验位
serialPort.setNumStopBits(1); // 停止位
if (serialPort.openPort()) {
// 借助工具类,构建请求报文。表示:向从站地址1,发送功能3,寄存器起始地址0,读取寄存器数量10的请求(从起始地址开始,向后读取10个数据)
byte[] requestMessage = ModbusRTURequestBuilder.buildModbusRTURequest(1, 3, 0, 10);
// 读取数据
serialPort.writeBytes(requestMessage, requestMessage.length);
try {
Thread.sleep(100); // 等待足够的时间以确保传感器响应
} catch (InterruptedException e) {
e.printStackTrace();
}
// 响应数据的长度根据以下公式自行调整
// 报文长度组成:从站地址 + 功能码 + 数据总长度 + 数据个数 + CRC校验(2个)
// 返回报文示例 [1, 3, 20, 0, 0, 0, 0, 0, 2, 39, 15, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 45, 30]
// 从站地址(1) + 功能码(1) + 数据总长度(1) + 数据个数(20) + CRC校验(2)
// 数据总长度20的原因:读取10个寄存器的数据,会返回*2的字节数
byte[] responseData = new byte[25];
// 获取响应
int bytesRead = serialPort.readBytes(responseData, responseData.length);
// 通过打印可以看到,获取的数据为十进制
System.out.println(Arrays.toString(responseData));
if (bytesRead == responseData.length) {
// 解析数据,返回的数据为字节码两两一组,表示第7和第8(即数据区的第3个数据)进行换算
int temperature = ((responseData[7] << 8) | (responseData[8] & 0xFF));
int humidity = ((responseData[9] << 8) | (responseData[10] & 0xFF));
System.out.println("温度: " + temperature);
System.out.println("湿度: " + humidity);
} else {
System.out.println("未收到完整的响应数据");
}
serialPort.closePort();
} else {
System.out.println("无法打开串口");
}
}
}
请求报文解析
返回报文解析,及测试结果
有帮助的同学,能不能给个留个赞?
作者:java丶小萌新