一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
前言
最近接到一个新的需求,使用java代码实现串口通信,之前也没做过这方面的业务;原因是专网和互联网网络是不通的,也申请不到专网卡,我们领导就想到用串口通信,让我来研究实现,我在网上找了一些,发现都不全,目前功能已实现,记录一下,方便小伙伴们以后有用到的机会可以参考。
环境搭建
资料我已上传我们的百度云盘,可自行下载:
提取码:t6nw
我公司的台式机没有串口的接口,于是就用了两个USB转串口线;这个需要安装驱动,软件都在云盘里。安装成功后显示如下图:
现在我们用串口工具来测试一下环境是否OK,打开工具。
我这里打开两个串口,模拟接受和发送数据。
上图中,可以看出,环境是OK的,两个串口之间的通信是正常的,下面我们来用Java代码来实现串口通信。
串口通信
所谓串口,指的是串行通信接口(com),区别于并行通信,一次性可传输8位数据,不会发生数据位序混乱,但是比并行通信传输时间长得多,可以双向通信,主要用于设备与设备之间的通信,常用的主要有两类:
- RS232(一对一数据传输,适合本地设备之间的通信)。
- RS485(一对多数据传输,适合远程设备之间的通信)。
Java实现
开始写代码之前,我们要在jdk加入几个文件,首先是将rxtxSerial.dll、rxtxParallel.dll
两个文件放入到下面的目录下;
再将RXTXcomm.jar
放到相应的文件夹里面。
引入依赖
<dependency>
<groupId>org.bidib.jbidib.org.qbang.rxtx</groupId>
<artifactId>rxtxcomm</artifactId>
<version>2.2</version>
</dependency>
目录结构如下:
通信原理
设置串口通信参数—》打开串口—》发送数据—》获得返回的数据—》解析数据—》关闭串口。
设置参数
@Component
public class PortInit implements ApplicationRunner
{
public static SerialPort serialPort = null;
@Override
public void run(ApplicationArguments args)
{
String portName = "COM3";
//查看所有串口
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
ArrayList<String> port = serialPortUtil.findPort();
System.out.println("发现全部串口:" + port);
System.out.println("打开指定portName:" + portName);
//打开该对应portName名字的串口
PortInit.serialPort = serialPortUtil.openPort(
portName,
115200,
SerialPort.DATABITS_8,
SerialPort.PARITY_NONE,
SerialPort.PARITY_ODD);
//给对应的serialPort添加监听器
serialPortUtil.addListener(PortInit.serialPort, new MyLister());
}
}
打开串口
/**
* 打开串口
*
* @param portName 端口名称
* @param baudrate 波特率 19200
* @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, 2000);
//判断是不是串口
if (commPort instanceof SerialPort)
{
SerialPort serialPort = (SerialPort) commPort;
//设置一下串口的波特率等参数
serialPort.setSerialPortParams(baudrate, databits, stopbits, parity);
System.out.println("Open " + portName + " sucessfully !");
return serialPort;
}
else
{
logger.error("不是串口");
}
}
catch (NoSuchPortException e1)
{
logger.error("没有找到端口");
e1.printStackTrace();
}
catch (PortInUseException e2)
{
logger.error("端口被占用");
e2.printStackTrace();
}
catch (UnsupportedCommOperationException e)
{
e.printStackTrace();
}
return null;
}
设置监听
/**
* @Author jiangwang
* @Created by 2022/4/18 16:41
* @Description: 串口监听器
*/
public class MyLister implements SerialPortEventListener
{
private static final Logger logger = LoggerFactory.getLogger(MyLister.class);
@Override
public void serialEvent(SerialPortEvent event)
{
switch (event.getEventType())
{
//串口存在有效数据
case SerialPortEvent.DATA_AVAILABLE:
byte[] bytes = SerialPortUtil.getSerialPortUtil().readFromPort(PortInit.serialPort);
String byteStr = new String(bytes, 0, bytes.length).trim();
System.out.println("===========start===========");
System.out.println(new Date() + "【读到的字符串】:-----" + byteStr);
System.out.println(new Date() + "【字节数组转16进制字符串】:-----" + printHexString(bytes));
System.out.println("===========end===========");
break;
// 2.输出缓冲区已清空
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
logger.error("输出缓冲区已清空");
break;
// 3.清除待发送数据
case SerialPortEvent.CTS:
logger.error("清除待发送数据");
break;
// 4.待发送数据准备好了
case SerialPortEvent.DSR:
logger.error("待发送数据准备好了");
break;
// 10.通讯中断
case SerialPortEvent.BI:
logger.error("与串口设备通讯中断");
break;
default:
break;
}
}
/**
* 字节数组转16进制字符串
*
* @param b 字节数组
* @return 16进制字符串
*/
public static String printHexString(byte[] b)
{
StringBuilder sbf = new StringBuilder();
for (byte value : b)
{
String hex = Integer.toHexString(value & 0xFF);
if (hex.length() == 1)
{
hex = '0' + hex;
}
sbf.append(hex.toUpperCase()).append(" ");
}
return sbf.toString().trim();
}
/**
* 16进制字符串转字节数组
*
* @param hex 16进制字符串
* @return 字节数组
*/
public static byte[] hex2byte(String hex)
{
if (!isHexString(hex))
{
return null;
}
char[] arr = hex.toCharArray();
byte[] b = new byte[hex.length() / 2];
for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++)
{
String swap = "" + arr[i++] + arr[i];
int byteint = Integer.parseInt(swap, 16) & 0xFF;
b[j] = new Integer(byteint).byteValue();
}
return b;
}
/**
* 校验是否是16进制字符串
*
* @param hex
* @return
*/
public static boolean isHexString(String hex)
{
if (hex == null || hex.length() % 2 != 0)
{
return false;
}
for (int i = 0; i < hex.length(); i++)
{
char c = hex.charAt(i);
if (!isHexChar(c))
{
return false;
}
}
return true;
}
/**
* 校验是否是16进制字符
*
* @param c
* @return
*/
private static boolean isHexChar(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
}
发送数据
/**
* 往串口发送数据
*
* @param serialPort 串口对象
* @param order 待发送数据
*/
public void sendToPort(SerialPort serialPort, byte[] order)
{
OutputStream out = null;
try
{
out = serialPort.getOutputStream();
out.write(order);
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 bufflenth = in.available();
while (bufflenth != 0)
{
// 初始化byte数组为buffer中数据的长度
bytes = new byte[bufflenth];
in.read(bytes);
bufflenth = in.available();
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
return bytes;
}
关闭串口
@PreDestroy
public void destroy()
{
//关闭应用前 关闭端口
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
serialPortUtil.removeListener(PortInit.serialPort, new MyLister());
serialPortUtil.closePort(PortInit.serialPort);
}
测试
开启一个定时器往串口发送数据,利用串口工具可以查看接受到的数据,同时可以接收工具发来的数据,下面我们启动一下程序,看下效果。
接收和发送数据都是可以的,完整代码我上传gitee上,需要的自行star。
最后
码字不易,手下留赞,希望对一些人有点帮助。