文章目录  SpringBoot使用RXTX连接串口教程及遇到的坑总结 一、所用环境及依赖 二、部署流程 2.1 下载RXTXComm包 2.2 部署RXTXComm包 三、编写串口使用程序 3.1 编写RXTXConfig.java 3.2 编写实体类SerialPortEntity 3.3 编写监听器SerialPortListener 3.4 编写工具类SerialPortUtil 3.5 编写枚举类WarningLightEnum 3.6 编写测试类TestController 四、所踩到的坑   
 
本文主要记录了本人在近期使用串口操作设备时的流程以及踩到的坑,希望能够帮助到有需要运用JAVA去操作串口需求的朋友,避免像我一样踩了很多坑才成功连接上。 java: jdk11 maven: apache-maven-3.5.3 sprintboot:2.7.6 rxtx: 2.1.7 operate system: windows10 可以选择从官网下载 也直接下载我网盘的分享文件网盘文件,提取码:sjsf 下载完成后把压缩包里的RXTXcomm.jar、rxtxParallel.dll、rxtxSerial.dll复制出来 由于我的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> </ groupId> < artifactId> </ artifactId> < version> </ version> </ dependency> 为了更有效、更清晰地使用RXTXcomm,我编写了以下几个文件,分享出来供参考 我的工程目录如下 首先在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; } } 
在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 > =  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方法!" ) ; } } 
在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 ; } } } 在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 > =  CommPortIdentifier . getPortIdentifiers ( ) ; ArrayList < String > =  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 ) ; } } 在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; } } 
在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 > )  { 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.  RXTXCommDriverException  in thread "AWT-EventQueue-0"  java. lang.  NoClassDefFoundError:  Could  not initialize class  gnu. io.  RXTXVersiongnu. io.  CommPortIdentifier. < clinit> ( CommPortIdentifier . java: 123 ) 
如果你使用的是jdk1.8以上的版本的话,SerialPortUtil中sendToPort方法一定不要加out.flush,否则会报错。这个地方卡了我大半天,后来才找出来。 当使用java调用串口时,不要使用其他串口测试工具,否则会出现报错