Springboot + rxtx 实现串口读写 案例

经过大量翻阅文章总结出springboot连接串口最可行的方法!希望能帮到大家( •̀ ω •́ )✧

使用Rxtx实现串口通信

1、配置pom.xml

		<!-- 串口内容读取 -->
        <dependency>
            <groupId>org.bidib.jbidib.org.qbang.rxtx</groupId>
            <artifactId>rxtxcomm</artifactId>
            <version>2.2</version>
        </dependency>
        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

2、创建串口类

@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、创建监听器

@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(new Date() + "【字节数组转16进制字符串】:-----" + ModBusUtils.bytes2HexString(bytes));
                log.info("===========end===========");
                break;
            // 2.输出缓冲区已清空
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                log.error("输出缓冲区已清空");
                break;
            // 3.清除待发送数据
            case SerialPortEvent.CTS:
                log.error("清除待发送数据");
                break;
            // 4.待发送数据准备好了
            case SerialPortEvent.DSR:
                log.error("待发送数据准备好了");
                break;
            // 10.通讯中断
            case SerialPortEvent.BI:
                log.error("与串口设备通讯中断");
                break;
            default:
                break;
        }
    }
}

4、创建串口工具类

@Slf4j
public class SerialPortUtil {

    private static SerialPortUtil serialPortUtil = null;

    static {
        serialPortUtil = new SerialPortUtil();
    }

    private SerialPortUtil(){

    }

    /**
     * 获取提供服务的SerialTool对象
     * @return serialPortUtil
     */
    public static SerialPortUtil getSerialPortUtil(){
        if(serialPortUtil == null){
            serialPortUtil = new SerialPortUtil();
        }
        return serialPortUtil;
    }

    /**
     * 查找所有可用端口
     * @return 可用端口名称列表
     */
    public ArrayList<String> findPort() {
        // 获得当前所有可用串口
        Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
        ArrayList<String> portNameList = new ArrayList<>();
        // 将可用串口名添加到List并返回该List
        while (portList.hasMoreElements()) {
            String portName = portList.nextElement().getName();
            portNameList.add(portName);
        }
        return portNameList;
    }

    /**
     * 打开串口
     * @param portName 端口名称
     * @param baudrate 波特率  9600
     * @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, 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;
    }

    /**
     * 关闭串口
     * @param serialPort 待关闭的串口对象
     */
    public void closePort(SerialPort serialPort) {
        if (serialPort != null) {
            serialPort.close();
        }
    }

    /**
     * 往串口发送数据
     * @param serialPort 串口对象
     */
    public void sendToPort(SerialPort serialPort, byte[] bytes) {
        OutputStream out = null;
        try {
            out = serialPort.getOutputStream();
            out.write(bytes);
            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 bufferlength = in.available();
            while (bufferlength != 0) {
                // 初始化byte数组为buffer中数据的长度
                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;
    }


    /**
     * 添加监听器
     * @param port     串口对象
     * @param listener 串口监听器
     */
    public void addListener(SerialPort port, SerialPortEventListener listener) {
        try {
            // 给串口添加监听器
            port.addEventListener(listener);
            // 设置当有数据到达时唤醒监听接收线程
            port.notifyOnDataAvailable(true);
            // 设置当通信中断时唤醒中断线程
            port.notifyOnBreakInterrupt(true);
        } catch (TooManyListenersException e) {
            log.error("太多监听器");
            e.printStackTrace();
        }
    }

    /**
     * 删除监听器
     *
     * @param port     串口对象
     * @param listener 串口监听器
     */
    public void removeListener(SerialPort port, SerialPortEventListener listener) {
        // 删除串口监听器
        port.removeEventListener();
    }

    /**
     * 设置串口的Listener
     *
     * @param serialPort
     * @param listener
     */
    public static void setListenerToSerialPort(SerialPort serialPort, SerialPortEventListener listener) {
        try {
            // 给串口添加事件监听
            serialPort.addEventListener(listener);
        } catch (TooManyListenersException e) {
            e.printStackTrace();
        }
        // 串口有数据监听
        serialPort.notifyOnDataAvailable(true);
        // 中断事件监听
        serialPort.notifyOnBreakInterrupt(true);
    }
}

5、Modbus工具类

public class ModBusUtils {

    public static char byteToASCLL(byte b){
        return (char) b;
    }


    /*
     * 字节数组转16进制字符串
     */
    public static String bytes2HexString(byte[] b) {
        String r = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            r += hex.toUpperCase()+" ";
        }
        return r;
    }

6、测试类

@SpringBootTest
@Slf4j
public class RxtxTest {
    @Test
    public void SerialDemo(){
        SerialPortEntity serialPortEntity = new SerialPortEntity("COM4",19200,"NONE","8","1");
        try {
            SerialPort serialPort = serialPortEntity.connect();
            serialPortEntity.send(new byte[]{1,3,0,38,0,1,101,(byte)193});
            Thread.sleep(10000);
            serialPortEntity.close();
        }catch (Exception e){
            serialPortEntity.close();
            e.printStackTrace();
        }
    }
}

7、遇到java.lang.NoClassDefFoundError: Could not initialize class gnu.io.RXTXVersion问题的小伙伴可以安装两个DLL文件到 JDK目录/jre/bin下来解决此报错。
文件下载链接:https://download.csdn.net/download/qq_37281772/88356664

好啦!现在来测试一下( •̀ ω •́ )✧~

准备调试工具

串口调试工具http://www.zlmcu.com/document/com_debug_tools.html在这里插入图片描述

虚拟串口工具https://download.csdn.net/download/qq_37281772/87449453

在这里插入图片描述

操作步骤
1、打开虚拟串口工具,创建虚拟串口对,我使用的是COM4和COM5(端口可自定义)
2、打开串口调试工具,打开COM5串口(因为上面测试代码打开了COM4)
3、启动Test代码

COM4给COM5下发指令

在这里插入图片描述在这里插入图片描述

COM5给COM4下发指令

在这里插入图片描述在这里插入图片描述

萌新第一次写文,如文章有误,欢迎指出哈ヾ(>▽<)o*

参考文献:
1、Netty介绍:https://www.cnblogs.com/MrRightZhao/p/11925307.html
2、Netty集成串口RXTX编程:https://www.jianshu.com/p/9e9747dedc3d
3、Java使用Netty实现Modbus-RTU通信协议:https://blog.csdn.net/qq_40042416/article/details/128457288
4、springboot+netty+串口:https://blog.csdn.net/csweldn520/article/details/116661871
5、java利用socket通信实现Modbus-RTU通信协议:https://blog.csdn.net/qq_40042416/article/details/115672989

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值