文章目录
SpringBoot使用RXTX连接串口教程及遇到的坑总结
- 本文主要记录了本人在近期使用串口操作设备时的流程以及踩到的坑,希望能够帮助到有需要运用JAVA去操作串口需求的朋友,避免像我一样踩了很多坑才成功连接上。
一、所用环境及依赖
- java: jdk11
- maven: apache-maven-3.5.3
- sprintboot:2.7.6
- rxtx: 2.1.7
- operate system: windows10
二、部署流程
2.1 下载RXTXComm包
2.2 部署RXTXComm包
- 由于我的Springboot工程使用的是Maven仓库,因此需要通过命令先把RXTXcomm.jar导入到自己Maven的Repository里面(Maven的具体使用方式本文不做详解)
- 打开CMD,输入以下命令,导入RXTXcomm.jar
/*
* DgroupId: pom.xml中映射的groupId
* DartifactId: pom.xml中映射的artifactId
* Dversion: pom.xml中映射的version
* Dpackaging: 加载的是jar包
* Dfile: RXTXcomm.jar包的路径,建议使用绝对路径
*/
mvn install:install-file-DgroupId=gnu.io -DartifactId=rxtx -Dversion=2.1.7 -Dpackaging=jar -Dfile="E:\RXTXcomm.jar"
- 运行后若在Maven的repository中存在下面的文件夹及文件,就代表导入成功了
- RXTXcomm.jar导入成功后,需要把解压到的rxtxParallel.dll、rxtxSerial.dll文件复制到JAVA_HOME/bin路径下或者C:/Windows/System32/路径下(JAVA_HOME的配置方法很简单,自行百度即可)
- 最后在pom.xml中输入以下命令引用依赖即可完成RXTXcomm依赖导入到项目中
<dependency>
<groupId>gnu.io</groupId>
<artifactId>rxtx</artifactId>
<version>2.1.7</version>
</dependency>
三、编写串口使用程序
- 为了更有效、更清晰地使用RXTXcomm,我编写了以下几个文件,分享出来供参考
- 我的工程目录如下
3.1 编写RXTXConfig.java
- 首先在application.yml中添加下面的配置
rxtx-config:
portName: COM13 # 串口名
baudRate: 9600 # 波特率
parityBit: EVEN # 检验位
dataBits: 8 # 数据位
stopBits: 1 # 停止位
- config目录下建立RXTXConfig.java,代码如下
package com.mbtxtq.app.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "rxtx-config")
public class RXTXConfig {
private String portName;
private Integer baudRate;
private String parityBit;
private String dataBits;
private String stopBits;
public String getPortName() {
return portName;
}
public void setPortName(String portName) {
this.portName = portName;
}
public Integer getBaudRate() {
return baudRate;
}
public void setBaudRate(Integer baudRate) {
this.baudRate = baudRate;
}
public String getParityBit() {
return parityBit;
}
public void setParityBit(String parityBit) {
this.parityBit = parityBit;
}
public String getDataBits() {
return dataBits;
}
public void setDataBits(String dataBits) {
this.dataBits = dataBits;
}
public String getStopBits() {
return stopBits;
}
public void setStopBits(String stopBits) {
this.stopBits = stopBits;
}
}
3.2 编写实体类SerialPortEntity
- 在entity目录下建立SerialPortEntity.java,代码如下
package com.mbtxtq.app.entity;
import com.mbtxtq.app.listener.SerialPortListener;
import com.mbtxtq.app.utils.SerialPortUtil;
import gnu.io.SerialPort;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@Slf4j
@Data
public class SerialPortEntity {
private String portName; //端口号
private Integer baudRate; //波特率
private int dataBits; //数据位
private String dataBitsStr; //数据位字符串
private int stopBits; //停止位
private String stopBitsStr; //停止位字符串
private int parityBit; //校验位
private String parityBitStr; //校验位字符串
private SerialPort serialPort = null; //串口会话
public SerialPortEntity(String portName, Integer baudRate, String parityBit, String dataBits, String stopBits){
this.portName = portName;
this.baudRate = baudRate;
this.dataBitsStr = dataBits;
this.stopBitsStr = stopBits;
this.parityBitStr = parityBit;
//校验位
switch (parityBit) {
case "NONE":
this.parityBit = SerialPort.PARITY_NONE;
break;
case "ODD":
this.parityBit = SerialPort.PARITY_ODD;
break;
case "EVEN":
this.parityBit = SerialPort.PARITY_EVEN;
break;
case "MARK":
this.parityBit = SerialPort.PARITY_MARK;
break;
case "SPACE":
this.parityBit = SerialPort.PARITY_SPACE;
break;
}
//数据位
switch (dataBits) {
case "5":
this.dataBits = SerialPort.DATABITS_5;
break;
case "6":
this.dataBits = SerialPort.DATABITS_6;
break;
case "7":
this.dataBits = SerialPort.DATABITS_7;
break;
case "8":
this.dataBits = SerialPort.DATABITS_8;
break;
}
//停止位
switch (stopBits) {
case "1":
this.stopBits = SerialPort.STOPBITS_1;
break;
case "1.5":
this.stopBits = SerialPort.STOPBITS_1_5;
break;
case "2":
this.stopBits = SerialPort.STOPBITS_2;
break;
}
}
// 连接串口
public SerialPort connect(){
// 查看所有串口
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
List<String> portList = serialPortUtil.findPort();
log.info("发现全部端口: "+portList);
log.info("尝试打开端口:"+portName+" ....");
// 打开指定端口
SerialPort serialPort = serialPortUtil.openPort(portName,baudRate,dataBits,parityBit,stopBits);
SerialPortListener listener = new SerialPortListener();
listener.setSerialPort(serialPort);
setSerialPort(serialPort);
serialPortUtil.addListener(serialPort, listener);
return serialPort;
}
// 关闭串口
public void close(){
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
if(serialPort != null) serialPortUtil.closePort(serialPort);
else log.info("请先调用connect方法!");
}
// 发送指令
public void send(byte[] code){
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
if (serialPort != null) serialPortUtil.sendToPort(serialPort,code);
else log.info("请先调用connect方法!");
}
}
3.3 编写监听器SerialPortListener
- 在listener目录下建立SerialPortEntity.java,代码如下
package com.mbtxtq.app.listener;
import com.mbtxtq.app.utils.SerialPortUtil;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.Date;
@Slf4j
@Data
public class SerialPortListener implements SerialPortEventListener {
private SerialPort serialPort = null;
@Override
public void serialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()){
// 串口存在有效数据
case SerialPortEvent.DATA_AVAILABLE:
byte[] bytes = SerialPortUtil.getSerialPortUtil().readFromPort(serialPort);
log.info("===========start===========");
log.info(new Date() + "【读到的字符】:-----" + Arrays.toString(bytes));
// log.info(new Date() + "【字节数组转16进制字符串】:-----" + ModBusUtils.bytes2HexString(bytes));
log.info("===========end===========");
break;
// 2.输出缓冲区已清空
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
log.error("输出缓冲区已清空");
break;
// 3.清除待发送数据
case SerialPortEvent.CTS:
log.error("清除待发送数据");
break;
// 4.待发送数据准备好了
case SerialPortEvent.DSR:
log.error("待发送数据准备好了");
break;
// 10.通讯中断
case SerialPortEvent.BI:
log.error("与串口设备通讯中断");
break;
default:
break;
}
}
}
3.4 编写工具类SerialPortUtil
- 在utils目录下建立SerialPortUtil.java,代码如下
package com.mbtxtq.app.utils;
import gnu.io.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.TooManyListenersException;
@Slf4j
public class SerialPortUtil {
private static SerialPortUtil serialPortUtil = null;
static {
serialPortUtil = new SerialPortUtil();
}
private SerialPortUtil(){
}
/**
* 获取提供服务的SerialTool对象
* @return serialPortUtil
*/
public static SerialPortUtil getSerialPortUtil(){
if(serialPortUtil == null){
serialPortUtil = new SerialPortUtil();
}
return serialPortUtil;
}
/**
* 查找所有可用端口
* @return 可用端口名称列表
*/
public ArrayList<String> findPort() {
// 获得当前所有可用串口
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
ArrayList<String> portNameList = new ArrayList<>();
// 将可用串口名添加到List并返回该List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
/**
* 打开串口
* @param portName 端口名称
* @param baudrate 波特率 9600
* @param databits 数据位 8
* @param parity 校验位(奇偶位) NONE :0
* @param stopbits 停止位 1
* @return 串口对象
*/
public SerialPort openPort(String portName, int baudrate, int databits, int parity, int stopbits) {
try {
// 通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
// 打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 5000);
// 判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
// 设置一下串口的波特率等参数
serialPort.setSerialPortParams(baudrate, databits, stopbits, parity);
log.info("打开串口 " + portName + " 成功 !");
return serialPort;
} else {
log.error("不是串口");
}
} catch (NoSuchPortException e1) {
log.error("没有找到端口");
e1.printStackTrace();
} catch (PortInUseException e2) {
log.error("端口被占用");
e2.printStackTrace();
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭串口
* @param serialPort 待关闭的串口对象
*/
public void closePort(SerialPort serialPort) {
if (serialPort != null) {
serialPort.close();
}
}
/**
* 往串口发送数据
* @param serialPort 串口对象
*/
public void sendToPort(SerialPort serialPort, byte[] bytes) {
OutputStream out = null;
try {
out = serialPort.getOutputStream();
out.write(bytes);
// 如果jdk大于1.8版本的话不要把out.flush()打开,会报错
// out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 从串口读取数据
* @param serialPort 当前已建立连接的SerialPort对象
* @return 读取到的数据
*/
public byte[] readFromPort(SerialPort serialPort) {
InputStream in = null;
byte[] bytes = null;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
in = serialPort.getInputStream();
// 获取buffer里的数据长度
int bufferlength = in.available();
while (bufferlength != 0) {
// 初始化byte数组为buffer中数据的长度
bytes = new byte[bufferlength];
in.read(bytes);
bufferlength = in.available();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
/**
* 添加监听器
* @param port 串口对象
* @param listener 串口监听器
*/
public void addListener(SerialPort port, SerialPortEventListener listener) {
try {
// 给串口添加监听器
port.addEventListener(listener);
// 设置当有数据到达时唤醒监听接收线程
port.notifyOnDataAvailable(true);
// 设置当通信中断时唤醒中断线程
port.notifyOnBreakInterrupt(true);
} catch (TooManyListenersException e) {
log.error("太多监听器");
e.printStackTrace();
}
}
/**
* 删除监听器
*
* @param port 串口对象
* @param listener 串口监听器
*/
public void removeListener(SerialPort port, SerialPortEventListener listener) {
// 删除串口监听器
port.removeEventListener();
}
/**
* 设置串口的Listener
*
* @param serialPort
* @param listener
*/
public static void setListenerToSerialPort(SerialPort serialPort, SerialPortEventListener listener) {
try {
// 给串口添加事件监听
serialPort.addEventListener(listener);
} catch (TooManyListenersException e) {
e.printStackTrace();
}
// 串口有数据监听
serialPort.notifyOnDataAvailable(true);
// 中断事件监听
serialPort.notifyOnBreakInterrupt(true);
}
}
3.5 编写枚举类WarningLightEnum
- 在enums目录下建立WarningLightEnum.java,代码如下
package com.mbtxtq.app.enums;
public enum WarningLightEnum {
ALL_CLOSE("全关", new byte[]{1, 5, 0, 0, 0, 0, (byte) 205, (byte) 202}),
RED_LIGHT_OPEN("红灯亮", new byte[]{1, 5, 0, 1, (byte) 255, 0, (byte) 221, (byte) 250});
private String codeType;
private byte[] codeList;
private WarningLightEnum(String codeType, byte[] codeList) {
this.codeType = codeType;
this.codeList = codeList;
}
public String getCodeType() {
return codeType;
}
public void setCodeType(String codeType) {
this.codeType = codeType;
}
public byte[] getCodeList() {
return codeList;
}
public void setCodeList(byte[] codeList) {
this.codeList = codeList;
}
}
3.6 编写测试类TestController
- 在controller目录下建立TestController.java,代码如下
package com.mbtxtq.app.controller.test;
import com.mbtxtq.app.config.RXTXConfig;
import com.mbtxtq.app.entity.main_scout_process.SerialPortEntity;
import com.mbtxtq.app.enums.WarningLightEnum;
import gnu.io.SerialPort;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Controller
@CrossOrigin(origins = "*")
@ResponseBody
@RequestMapping("/target/feature/extract")
public class TestController {
@Autowired
private RXTXConfig rxtxConfig;
@PostMapping("test")
public void test(@RequestBody Map<String, Object> params) {
String useType = (String) params.get("use_type");
byte[] codeList1 = warningLightEnum.ALL_CLOSE.getCodeList();
byte[] codeList2 = WarningLightEnum.RED_LIGHT_OPEN.getCodeList();
// 初始化实体类SerialPortEntity,参数使用RXTXConfig中的参数,可自行调整
SerialPortEntity serialPortEntity = new SerialPortEntity(
this.rxtxConfig.getPortName(), this.rxtxConfig.getBaudRate(),
this.rxtxConfig.getParityBit(), this.rxtxConfig.getDataBits(), this.rxtxConfig.getStopBits()
);
try {
SerialPort serialPort = serialPortEntity.connect();
System.out.println(serialPort);
if (useType.equals("close")) {
serialPortEntity.send(codeList1);
} else if (useType.equals("start")) {
serialPortEntity.send(codeList2);
}
// Thread.sleep(10000);
serialPortEntity.close();
} catch (Exception e){
e.printStackTrace();
serialPortEntity.close();
}
}
}
- 通过postman获取前端代码请求test的api即可调用串口的测试接口,成功连接!
四、所踩到的坑
- 复制rxtxParallel.dll、rxtxSerial.dll这两个文件很重要!没有复制或者复制错目录会导致报以下的错
java.lang.UnsatisfiedLinkError: no rxtxSerial64 in java.library.path thrown while loading gnu.io.RXTXCommDriver
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: Could not initialize class gnu.io.RXTXVersion
at gnu.io.CommPortIdentifier.<clinit>(CommPortIdentifier.java:123)
- 如果你使用的是jdk1.8以上的版本的话,SerialPortUtil中sendToPort方法一定不要加out.flush,否则会报错。这个地方卡了我大半天,后来才找出来。
- 当使用java调用串口时,不要使用其他串口测试工具,否则会出现报错
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/199442.html