参考:
(1)Java使用RXTX进行串口SerialPort通讯:Java使用RXTX进行串口SerialPort通讯 - 简书
(2)rxtx-2.2pre2-bins.zip下载:https://download.csdn.net/download/zhp614/5943663
我:
(1)添加插件依赖,并 把 rxtxSerial.dll 文件 放到 C:\Windows\System32 目录。
<!-- RXTX串口通信包,需要将 rxtxParallel.dll、rxtxSerial.dll 直接放到 C:\Windows\System32 目录 -->
<dependency>
<groupId>org.rxtx</groupId>
<artifactId>rxtxcomm</artifactId>
<version>2.2pre2</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/libs/RXTXcomm.jar</systemPath>
</dependency>
(2)16进制转换工具类:
package utils;
public class HexUtils
{
/**
* 接收串口返回的二进制字节流时,
* 二进制字节流数组(一个字节是8位二进制,如00000001)转换成十六进制字符串
*/
public static final String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = byteToHexString(bArray[i]);
sb.append(sTemp);
}
return sb.toString();
}
public static final String byteToHexString(byte bt_8){
// 先将8位二进制的一个字节转为 32位二进制的整数类型,避免在后续的转换中丢失数据
int it_32 = byteToInt(bt_8);
// 再将 it_32 转为 16进制字符串
String hex = Integer.toHexString(it_32);
if (hex.length() < 2){
hex = "0" + hex;
}
hex = hex.toUpperCase();
return hex;
}
/**
* 给串口发送命令时,
* hex字符串转byte数组 并且添加最后一位校验码
* @param inHex 待转换的Hex字符串
* @return 转换后的byte数组结果
*/
public static byte[] hexToByteArray(String inHex) {
byte[] result = new byte[(inHex.length() / 2)];
int j = 0;
for(int i = 0; i < inHex.length(); i += 2){
result[j] = hexToByte(inHex.substring(i, i + 2));
j++;
}
return result;
}
/**
* Hex字符串转byte
* @param inHex 待转换的Hex字符串
* @return 转换后的byte
*/
public static byte hexToByte(String inHex) {
int b = Integer.parseInt(inHex, 16);
return (byte)b;
}
/**
* 十进制字符串转 16进制字符串
*/
public static String decToHex(String dec){
return bytesToHexString(dec.getBytes());
}
/**
* 字节 转 数字
*/
public static int byteToInt(byte bt_8){
int it_32 = bt_8 & 0xFF;
return it_32;
}
}
(3)串口监听自定义interface:
/**
* @date 2020-06-08
* @author wuguixin 吴桂鑫
* @qq 1393180819
*/
package utils.serialPort;
/**
* 回调方法接口
* @author 吴桂鑫 wuguixin
*/
public interface IMyPortListener
{
// 监听
boolean onSerialPort(byte[] return_bytes);
// 监听之前 刷新
void refreshData();
// 监听时获取数据
Object getData();
}
(4)串口工具类:
/**
* @date 2020-06-08
* @author wuguixin 吴桂鑫
* @qq 1393180819
*/
package utils.serialPort;
import gnu.io.*;
import utils.HexUtils;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/**
* RxtxAPI 的核心是抽象的CommPort类及其两个子类:SerialPort类和ParallePort类。
* 其中,SerialPort类是用于串口通信的类,P
* arallePort类是用于并行口通信的类。
* CommPort类还提供了常规的通信模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,
* 专用于与端口上的设备进行通信。
* 然而,这些类的构造方法都被有意的设置为非公有的(non-public)。
* 所以,不能直接构造对象,而是先通过静态的CommPortIdentifer.getPortIdentifiers()获得端口列表,
* 再从这个端口列表中选择所需要的端口,并调用CommPortIdentifer对象的Open( )方法,
* 这样,就能得到一个CommPort对象。当
* 然,还要将这个CommPort对象的类型转换为某个非抽象的子类,表明是特定的通讯设备,
* 该子类可以是SerialPort类和ParallePort类中的一个。
*/
public class SerialPortUtil implements SerialPortEventListener{
// 串口名称
private String portName;
// 波特率
private int baudRate;
//open 端口时的等待时间
private int timeout = 2000;
// 数据位
private int dataBits = 8;
// 停止位
private int stopBits = 1;
// 检验位 0 表示none
private int parity = 0;
// 串行端口
private SerialPort serialPort;
private static SerialPortUtil util;
private IMyPortListener myPortListener;
public static SerialPortUtil getInstance(String portName,int baudRate)throws Exception{
// 如果不存在,则创建
if(null == util){
util = new SerialPortUtil(portName,baudRate);
}
// 如果 串口名称 和 波特率 不一样,则重新创建
else if( (!util.portName.equals(portName)) || (util.baudRate != baudRate) ){
util.clear();
util = new SerialPortUtil(portName,baudRate);
}
return util;
}
public void clear(){
serialPort.close();
util = null;
}
private SerialPortUtil(String portName,int baudRate) throws Exception{
this.portName = portName;
this.baudRate = baudRate;
init();
}
/**
* 根据 串口名称 与 对应串口 相连接
* @throws Exception
*/
private void init() throws Exception {
// 串口是否在 可用串口list中
List ports = findPortNameList();
if(ports.size()<=0){
throw new Exception("错误: 没有找到可用的串口");
}
if(!ports.contains(portName)){
throw new Exception("错误: 该串口不存在");
}
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
if ( portIdentifier.isCurrentlyOwned() ) {
throw new Exception("错误: 该串口已被占用");
}
// 调用 open方法 连接 串口
CommPort commPort = portIdentifier.open(this.getClass().getName(),timeout);
if ( !(commPort instanceof SerialPort) ) {
throw new Exception("错误: 无法处理非串行端口");
}
serialPort = (SerialPort) commPort;
// 设置串口参数:波特率、数据位、停止位、检验位
serialPort.setSerialPortParams(baudRate,dataBits,stopBits,parity);
// 监听
serialPort.addEventListener(this);
// 设置当通信中断时唤醒中断线程
serialPort.notifyOnBreakInterrupt(true);
System.out.println("连接" + portName + "成功");
}
/**
* 发送命令 给 串口
* @param command 命令
*/
public boolean sendCommand(String command, IMyPortListener myPortListener){
try{
// 清空旧数据
myPortListener.refreshData();
System.out.println(command);
// 设置当有数据到达时唤醒监听接收线程
serialPort.notifyOnDataAvailable(true);
// 在发送命令之前,设置相应命令的监听处理方式
this.myPortListener = myPortListener;
byte[] writerBuffer = HexUtils.hexToByteArray(command);
// 添加校验位
writerBuffer = addCRC(writerBuffer);
System.out.println("发送命令:"+HexUtils.bytesToHexString(writerBuffer));
OutputStream out = serialPort.getOutputStream();
out.write(writerBuffer);
System.out.println("发送命令成功");
return true;
}catch (Exception e){
e.printStackTrace();
clear();
System.out.println("发送命令失败");
}
return false;
}
/**
* 读取串口返回信息
*/
public byte[] readData(){
byte[] readBuffer = null;
InputStream inputStream = null;
try{
inputStream = serialPort.getInputStream();
// 通过输入流对象的available方法获取数组字节长度
readBuffer = new byte[inputStream.available()];
// 从线路上读取数据流
int len = 0;
while ((len = inputStream.read(readBuffer)) != -1) {
break;
}
inputStream.close();
System.out.println("读取数据流成功");
}catch (Exception e){
e.printStackTrace();
System.out.println("读取数据流失败");
clear();
}
return readBuffer;
}
/**
* 列出所有可用的串口名称
*/
public static List<String> findPortNameList(){
List<String> portNameList = new ArrayList<>();
Enumeration<CommPortIdentifier> en = CommPortIdentifier.getPortIdentifiers();
System.out.println("now to list all Port of this PC:" +en);
while(en.hasMoreElements()){
CommPortIdentifier port = en.nextElement();
if(port.getPortType() == CommPortIdentifier.PORT_SERIAL){
portNameList.add(port.getName());
}
}
return portNameList;
}
@Override
public void serialEvent(SerialPortEvent event)
{
int type = event.getEventType();
if(type == SerialPortEvent.DATA_AVAILABLE){
// 有数据到达
byte[] return_bytes = util.readData();
// 处理数据
boolean flag = this.myPortListener.onSerialPort(return_bytes);
if(flag){
// 设置当 处理数据返回为 true 时 关闭 监听接收线程
serialPort.notifyOnDataAvailable(false);
}
}
}
/**
* 添加最后一位校验码
* @param bytes
* @return
*/
public static byte[] addCRC(byte[] bytes){
byte[] result = new byte[bytes.length + 1];
for(int i=0; i < bytes.length; i++){
result[i] = bytes[i];
}
int crc = bytes[3] + bytes[4]+ bytes[5]+ bytes[6]
+ bytes[7]+ bytes[8]+ bytes[9]+ bytes[10]
+ bytes[11]+ bytes[12]+ bytes[13]+ bytes[14] + 0x42;
result[result.length-1] = (byte)crc;
return result;
}
}
(5)例子:
(5.1)wifi监听:
package operation.wifiManage;
import lombok.Data;
import utils.HexUtils;
import utils.serialPort.IMyPortListener;
@Data
public class WifiReadListener implements IMyPortListener
{
// wifi mac指令前缀
static final String WIFI_COMMAND_PREFIX = "F8F8A8";
// wifi mac地址长度
static final Integer WIFI_MAC_LENGTH = 6;
// 监听到的mac地址
String mac;
// 是否监听到了mac地址
Boolean flag;
@Override
public boolean onSerialPort(byte[] return_bytes)
{
if(null != return_bytes){
String return_hex = HexUtils.bytesToHexString(return_bytes);
if(return_hex.startsWith(WIFI_COMMAND_PREFIX)){
// 获取 经典 mac地址
for(int i=0;i< WIFI_MAC_LENGTH ;i++){
String po = HexUtils.byteToHexString(return_bytes[i+3]);
if(i==0){
mac = po;
}else{
mac = mac + ":" + po;
}
}
flag = true;
return true;
}
}
return false;
}
@Override
public void refreshData()
{
mac = null;
flag = false;
}
@Override
public Object getData()
{
if(flag){
return mac;
}else{
return null;
}
}
}
(5.2)发送读取wifi地址的命令并获取返回mac:
//wifi读取命令
static String WIFI_MAC_QUERY = "F8F8A8"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00";
//wifi监听事件类
WifiReadListener wifiReadListener = new WifiReadListener();
private void readMac(){
// todo 调用读取接口
try{
SerialPortUtil serialPortUtil = SerialPortUtil.getInstance("COM4",38400);
// 发送命令
serialPortUtil.sendCommand(WIFI_MAC_QUERY, wifiReadListener);
System.out.println("读取中...");
Long startTime = System.currentTimeMillis();
while(true){
Object res = wifiReadListener.getData();
if(null != res){
System.out.println("读取成功:"+res);
}
Long interval = System.currentTimeMillis() - startTime;
if(interval>2000){
System.out.println("读取超时,没有监听到目标数据");
break;
}
}
}catch (Exception e){
e.printStackTrace();
System.out.println("读取命令发送失败,"+e.getMessage());
}
}