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包
可以选择从官网 下载 也直接下载我网盘的分享文件网盘文件 ,提取码:sjsf 下载完成后把压缩包里的RXTXcomm.jar、rxtxParallel.dll、rxtxSerial.dll复制出来
2.2 部署RXTXComm包
由于我的Springboot工程使用的是Maven仓库,因此需要通过命令先把RXTXcomm.jar导入到自己Maven的Repository里面(Maven的具体使用方式本文不做详解) 打开CMD,输入以下命令,导入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 ( "===========end===========" ) ;
break ;
case SerialPortEvent . OUTPUT_BUFFER_EMPTY:
log. error ( "输出缓冲区已清空" ) ;
break ;
case SerialPortEvent . CTS:
log. error ( "清除待发送数据" ) ;
break ;
case SerialPortEvent . DSR:
log. error ( "待发送数据准备好了" ) ;
break ;
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 ( ) {
}
public static SerialPortUtil getSerialPortUtil ( ) {
if ( serialPortUtil == null ) {
serialPortUtil = new SerialPortUtil ( ) ;
}
return serialPortUtil;
}
public ArrayList < String > findPort ( ) {
Enumeration < CommPortIdentifier > portList = CommPortIdentifier . getPortIdentifiers ( ) ;
ArrayList < String > portNameList = new ArrayList < > ( ) ;
while ( portList. hasMoreElements ( ) ) {
String portName = portList. nextElement ( ) . getName ( ) ;
portNameList. add ( portName) ;
}
return portNameList;
}
public SerialPort openPort ( String portName, int baudrate, int databits, int parity, int stopbits) {
try {
CommPortIdentifier portIdentifier = CommPortIdentifier . getPortIdentifier ( portName) ;
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 ;
}
public void closePort ( SerialPort serialPort) {
if ( serialPort != null ) {
serialPort. close ( ) ;
}
}
public void sendToPort ( SerialPort serialPort, byte [ ] bytes) {
OutputStream out = null ;
try {
out = serialPort. getOutputStream ( ) ;
out. write ( bytes) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
try {
if ( out != null ) {
out. close ( ) ;
}
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
public byte [ ] readFromPort ( SerialPort serialPort) {
InputStream in = null ;
byte [ ] bytes = null ;
try {
Thread . sleep ( 500 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
try {
in = serialPort. getInputStream ( ) ;
int bufferlength = in. available ( ) ;
while ( bufferlength != 0 ) {
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;
}
public void addListener ( SerialPort port, SerialPortEventListener listener) {
try {
port. addEventListener ( listener) ;
port. notifyOnDataAvailable ( true ) ;
port. notifyOnBreakInterrupt ( true ) ;
} catch ( TooManyListenersException e) {
log. error ( "太多监听器" ) ;
e. printStackTrace ( ) ;
}
}
public void removeListener ( SerialPort port, SerialPortEventListener listener) {
port. removeEventListener ( ) ;
}
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 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) ;
}
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调用串口时,不要使用其他串口测试工具,否则会出现报错