Java实现串口通信

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

前言

最近接到一个新的需求,使用java代码实现串口通信,之前也没做过这方面的业务;原因是专网和互联网网络是不通的,也申请不到专网卡,我们领导就想到用串口通信,让我来研究实现,我在网上找了一些,发现都不全,目前功能已实现,记录一下,方便小伙伴们以后有用到的机会可以参考。

环境搭建

资料我已上传我们的百度云盘,可自行下载:

image.png

链接:pan.baidu.com/s/1Foujlz_e…

提取码:t6nw

我公司的台式机没有串口的接口,于是就用了两个USB转串口线;这个需要安装驱动,软件都在云盘里。安装成功后显示如下图:

image-20220419144612307

现在我们用串口工具来测试一下环境是否OK,打开工具。

image-20220419145150682

我这里打开两个串口,模拟接受和发送数据。

image.png

上图中,可以看出,环境是OK的,两个串口之间的通信是正常的,下面我们来用Java代码来实现串口通信。

串口通信

所谓串口,指的是串行通信接口(com),区别于并行通信,一次性可传输8位数据,不会发生数据位序混乱,但是比并行通信传输时间长得多,可以双向通信,主要用于设备与设备之间的通信,常用的主要有两类:

  1. RS232(一对一数据传输,适合本地设备之间的通信)。
  2. RS485(一对多数据传输,适合远程设备之间的通信)。

Java实现

开始写代码之前,我们要在jdk加入几个文件,首先是将rxtxSerial.dll、rxtxParallel.dll两个文件放入到下面的目录下;

image.png

再将RXTXcomm.jar放到相应的文件夹里面。

image.png

引入依赖

<dependency>
    <groupId>org.bidib.jbidib.org.qbang.rxtx</groupId>
    <artifactId>rxtxcomm</artifactId>
    <version>2.2</version>
</dependency>
复制代码

目录结构如下:

image.png

通信原理

设置串口通信参数—》打开串口—》发送数据—》获得返回的数据—》解析数据—》关闭串口。

设置参数

@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);
}
复制代码

测试

开启一个定时器往串口发送数据,利用串口工具可以查看接受到的数据,同时可以接收工具发来的数据,下面我们启动一下程序,看下效果。

image.png

接收和发送数据都是可以的,完整代码我上传gitee上,需要的自行star。

码云链接:gitee.com/jiangwang00…

最后

码字不易,手下留赞,希望对一些人有点帮助。

猜你喜欢

转载自juejin.im/post/7088220527122186253