背景
先说背景,
最近公司项目需要用到传感器,采集设备温湿度,倾斜角,电流…,公司采购采购了一个温湿度传感器给我们开发测试使用,如下图:
看着还挺精致有没有。
进入正题
有了这个温湿度传感器,我们如何读取其采集到的数据呢。需要一个RS485转串口工具
,下面这个蓝色的东东:
把它接到传感器上,然后插到电脑上
通过电脑设备管理器-端口,可以看到该设备串口号是COM3
,这个参数记住下面采集数据需要。
接下来,先用工具TH11S-B Tool_V105
,直接读取温湿度。
工具长这样
打开工具,
除了串口号,其他参数基本都是固定的,也看到页面上串口号也确实是COM3,然后点击连接直接连接串口。
此时点击读取就能读到当时的温湿度值,可以看到通过串口与传感器交互的协议报文。
读取报文01 03 00 00 00 02 C4 0B
,经过搜索得知这个是ModBus-RTU协议
(传感器常用协议)的报文,具体含义如下:
ChatGPT回答:
这个报文
01 03 00 00 00 02 C4 0B
是一个 Modbus RTU 协议的请求报文,它的各部分含义如下:
01
:从站地址,表示要操作的 Modbus 从站的地址。在这个例子中,从站地址为 1。03
:功能码,表示请求的操作类型。在 Modbus 中,功能码 03 表示读取保持寄存器。这个功能码用于读取传感器的数据。00 00
:起始寄存器地址,指示要读取的寄存器的起始地址。在这个例子中,起始寄存器地址为 0x0000(通常是一个 16 位的寄存器地址)。00 02
:寄存器数量,表示要读取的连续寄存器的数量。在这个例子中,要读取 2 个寄存器。C4 0B
:CRC 校验,用于确保报文的完整性。这个请求报文的目的是要求 Modbus 从站(通常是传感器或其他设备)返回起始寄存器地址为 0x0000 的 2 个寄存器的数据。这些数据通常包括传感器的温度和湿度值,具体含义取决于传感器的规格和协议。所以,你需要根据你的传感器的规格和协议来解析这些数据。
具体协议不做研究,主要看看响应报文01 03 04 00 FE 02 50 9A 9F
,具体含义如下:
ChatGPT回答:
这个响应报文
01 03 04 00 FE 02 50 9A 9F
是 Modbus RTU 协议的响应报文,其各部分含义如下:
01
:从站地址,表示响应来自 Modbus 从站的地址。在这个例子中,从站地址为 1,与请求报文相同。
03
:功能码,表示响应的操作类型。在 Modbus 中,功能码 03 表示读取保持寄存器。这个功能码用于读取传感器的数据,与请求报文相同。
04
:字节数,表示响应数据的字节数。在这个例子中,有 4 个字节的数据。
00 FE
:数据,表示读取到的保持寄存器数据。这里的数据是 2 个 16 位寄存器的值,以大端模式表示(高位在前,低位在后)。在这个例子中,它的十六进制值为00 FE
,对应的十进制值是 254。
02
:CRC 校验,用于确保报文的完整性。根据响应报文中的数据部分,这个特定传感器的温度值为 254。要了解确切的温度单位和规范,请参考传感器的文档或规格表。通常,温度值需要进一步处理和解释,以转换为实际的温度单位(例如摄氏度或华氏度)。
其他无需关注只需知道**00 FE
(第四五字节)表示温度值,02 50
(第六七字节)表示湿度值**即可,十六进制转为十进制然后直接除以十就是真实温湿度值。
00 FE
:254 / 10.0 = 25.4度(温度)
02 50
:592 / 10.0 = 59.2%(湿度)
代码实战
经过一些调研,要使用Java读取传感器数据,jSerialComm
依赖用的比较多。
添加依赖
项目中添加依赖
<dependency><groupId>com.fazecast</groupId><artifactId>jSerialComm</artifactId><version>2.9.2</version></dependency>
封装构建请求报文工具类
接下来构建请求报文,创建工具类与请求报文格式对应如下:
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;}
}
与传感器交互,解析响应报文
接下来与传感器交互,解析响应报文温湿度值
简单测试一下:
public class SerialSensorReader {public static void main(String[] args) {// 设置串口一些参数,除了串口号其他基本固定 COM3就是开头要大家记住的串口号SerialPort serialPort = SerialPort.getCommPort("COM3"); // 串口设备名称serialPort.setBaudRate(9600); // 波特率serialPort.setNumDataBits(8); // 数据位serialPort.setParity(SerialPort.NO_PARITY); // 校验位serialPort.setNumStopBits(1); // 停止位if (serialPort.openPort()) {// 借助工具类,构建请求报文byte[] requestMessage = ModbusRTURequestBuilder.buildModbusRTURequest(1, 3, 0, 2);// 读取温湿度serialPort.writeBytes(requestMessage, requestMessage.length);try {Thread.sleep(100); // 等待足够的时间以确保传感器响应} catch (InterruptedException e) {e.printStackTrace();}// 响应数据的长度根据传感器规格进行配置(该传感器响应报文长度为9,不同传感器自行调整)byte[] responseData = new byte[9]; // 获取响应 int bytesRead = serialPort.readBytes(responseData, responseData.length);if (bytesRead == responseData.length) {// 解析温湿度数据int temperature = ((responseData[3] << 8) | (responseData[4] & 0xFF));int humidity = ((responseData[5] << 8) | (responseData[6] & 0xFF));System.out.println("温度: " + temperature / 10.0);System.out.println("湿度: " + humidity / 10.0);} else {System.out.println("未收到完整的响应数据");}serialPort.closePort();} else {System.out.println("无法打开串口");}}
}
经测试,数据没有问题:
文章不长,也是博主一点一点搜出来、ChatGPT出来的,其中也是踩了不少坑,创作不易,请一键三连啊!(如果文章对您有用的话,不妨给我一个大大的赞,您的赞是我创作的动力。)