Java读取串口数据

2年前 (2022) 程序员胖胖胖虎阿
333 0 0

本文主要实现读取员工刷卡信息
将淘宝买的刷卡IC卡读取器插入Windows笔记本的USB接口,安装驱动,设备管理器会自动添加一个模拟的COM3串口
本次使用的Java环境为
java version “1.8.0_112”
Java™ SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot™ 64-Bit Server VM (build 25.112-b15, mixed mode)

1. 下载java读取串口的所需依赖

下载地址TXTX for Java
Java读取串口数据
Java读取串口数据

2. 将下载文件解压后dll文件放入jdk的jre文件中

rxtxParallel.dll,rxtxSerial.dll
copy to C:\Program Files\Java\jdk1.8.0_112\jre\bin

3. 新建Java项目

我使用的是创建一个JavaFX项目,因为只是写一个Demo,JavaFX和Java项目区别不大

在项目中创建一个lib包,将下载的RXTXcomm.jar文件放入lib包中,将lib包设置为项目资源
IDEA设置:右键RXTXcomm.jar,选择Add as Library...即可

4. 新建读取串口的工具SerialTool


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale;
import java.util.TooManyListenersException;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
public class SerialTool {

    /**
     * 查找所有可用端口
     * @return 可用端口名称列表
     */
    public static final ArrayList<String> findPort() {

        //获得当前所有可用串口
        @SuppressWarnings("unchecked")
        Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
        ArrayList<String> portNameList = new ArrayList<String>();
        //将可用串口名添加到List并返回该List
        while (portList.hasMoreElements()) {
            String portName = portList.nextElement().getName();
            portNameList.add(portName);
        }

        return portNameList;

    }

    /**
     * 打开串口
     * @param portName 端口名称
     * @param baudrate 波特率
     * @return 串口对象
     */
    public static final SerialPort openPort(String portName, int baudrate) {
        try {
            //通过端口名识别端口
            CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
            //打开端口,并给端口名字和一个timeout(打开操作的超时时间)
            CommPort commPort = portIdentifier.open(portName, 2000);
            //判断是不是串口
            if (commPort instanceof SerialPort) {
                SerialPort serialPort = (SerialPort) commPort;
                try {
                    //设置一下串口的波特率等参数
                    serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
                } catch (UnsupportedCommOperationException e) {
                    throw new RuntimeException("设置串口参数失败!打开串口操作未完成!");
                }
                System.out.println("Open " + portName + " successfully !");
                return serialPort;
            }
            else {
                //不是串口
                throw new RuntimeException("端口指向设备不是串口类型");
            }
        } catch (NoSuchPortException e1) {
            throw new RuntimeException("没有该端口对应的串口设备");
        } catch (PortInUseException e2) {
            throw new RuntimeException("端口已被占用");
        }
    }

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

    /**
     * 往串口发送数据
     * @param serialPort 串口对象
     * @param order 待发送数据
     */
    public static void sendToPort(SerialPort serialPort, byte[] order) {
        try (OutputStream out = serialPort.getOutputStream()){
            out.write(order);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从串口读取数据
     * @param serialPort 当前已建立连接的SerialPort对象
     * @return 读取到的数据
     */
    public static byte[] readFromPort(SerialPort serialPort)  {

        InputStream in = null;
        byte[] bytes = null;

        try {

            in = serialPort.getInputStream();
            int bufflenth = in.available(); //获取buffer里的数据长度

            while (bufflenth != 0) {
                bytes = new byte[bufflenth]; //初始化byte数组为buffer中数据的长度
                in.read(bytes);
                bufflenth = in.available();
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("从串口读取数据时出错");
        } finally {
            try {
                if (in != null) {
                    in.close();
                    in = null;
                }
            } catch(IOException e) {
                e.printStackTrace();
                System.err.println("关闭串口对象输入流出错");
            }

        }
        System.out.println("读取成功");
        return bytes;

    }

    /**
     * 添加监听器
     * @param port 串口对象
     * @param listener 串口监听器
     */
    public static void addListener(SerialPort port, DataAvailableListener listener){

        try {

            //给串口添加监听器
            port.addEventListener(new SerialPortListener(listener));
            //设置当有数据到达时唤醒监听接收线程
            port.notifyOnDataAvailable(true);
            //设置当通信中断时唤醒中断线程
            port.notifyOnBreakInterrupt(true);

        } catch (TooManyListenersException e) {
           e.printStackTrace();
            System.err.println("监听类对象过多");
        }
    }
    /**
     * 串口监听
     */
    public static class SerialPortListener implements SerialPortEventListener {

        private DataAvailableListener mDataAvailableListener;

        public SerialPortListener(DataAvailableListener mDataAvailableListener) {
            this.mDataAvailableListener = mDataAvailableListener;
        }

        public void serialEvent(SerialPortEvent serialPortEvent) {
            switch (serialPortEvent.getEventType()) {
                case SerialPortEvent.DATA_AVAILABLE: // 1.串口存在有效数据
                    if (mDataAvailableListener != null) {
                        mDataAvailableListener.dataAvailable();
                    }
                    break;

                case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2.输出缓冲区已清空
                    break;

                case SerialPortEvent.CTS: // 3.清除待发送数据
                    break;

                case SerialPortEvent.DSR: // 4.待发送数据准备好了
                    break;

                case SerialPortEvent.RI: // 5.振铃指示
                    break;

                case SerialPortEvent.CD: // 6.载波检测
                    break;

                case SerialPortEvent.OE: // 7.溢位(溢出)错误
                    break;

                case SerialPortEvent.PE: // 8.奇偶校验错误
                    break;

                case SerialPortEvent.FE: // 9.帧错误
                    break;

                case SerialPortEvent.BI: // 10.通讯中断
                    System.out.println("与串口设备通讯中断");
                    break;

                default:
                    break;
            }
        }
    }

    /**
     * 串口存在有效数据监听
     */
    public interface DataAvailableListener {
        /**
         * 串口存在有效数据
         */
        void dataAvailable();
    }

    public static byte[] hex2Bytes(String hex) {
        if (hex == null || hex.length() == 0) {
            return null;
        }

        char[] hexChars = hex.toCharArray();
        byte[] bytes = new byte[hexChars.length / 2];   // 如果 hex 中的字符不是偶数个, 则忽略最后一个

        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16);
        }

        return bytes;
    }
    /**
     * byte[]转十六进制字符串
     *
     * @param array
     *            byte[]
     * @return 十六进制字符串
     */
    public static String byteArrayToHexString(byte[] array) {
        if (array == null) {
            return "";
        }
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < array.length; i++) {
            buffer.append(byteToHex(array[i]));
        }
        return buffer.toString();
    }
    /**
     * byte转十六进制字符
     *
     * @param b
     *            byte
     * @return 十六进制字符
     */
    public static String byteToHex(byte b) {
        String hex = Integer.toHexString(b & 0xFF);
        if (hex.length() == 1) {
            hex = '0' + hex;
        }
        return hex.toUpperCase(Locale.getDefault());
    }

    public static String stringToAscii(String value)
    {
        StringBuffer sbu = new StringBuffer();
        char[] chars = value.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if(i != chars.length - 1)
            {
                sbu.append((int)chars[i]).append(",");
            }
            else {
                sbu.append((int)chars[i]);
            }
        }
        return sbu.toString();
    }
}

5. 读写串口测试

import gnu.io.SerialPort;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import sample.utils.*;

import java.util.ArrayList;


public class Controller {

    @FXML
    Button writeButton;
    @FXML
    TextArea readTextarea;
    @FXML
    TextArea writeTextarea;
    @FXML
    TextField comName;
    // 串口
    SerialPort port = null;

    /** 开启串口
     *
     */
    @FXML
    public void startCom() {
        if(port != null){
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "串口已经打开:" + port.getName());
            alert.show();
            return;
        }
        ArrayList<String> findPorts = SerialTool.findPort();
        if(findPorts.isEmpty()){
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "未找到串口信息!");
            alert.show();
            return;
        }
        String portName = findPorts.get(0);//默认打开第一个串口
        // 设置使用的串口
        comName.setText(portName);
        try {
            port = SerialTool.openPort(portName, 9600);//打开串口
        } catch (RuntimeException e){
            Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage());
            alert.show();
            return;
        }

        SerialTool.addListener(port, () -> {
            byte[] data = null;
            try {
                if (port == null) {
                    System.out.println("串口对象为空,监听失败!");
                } else {
                    // 读取串口数据
                    data = SerialTool.readFromPort(port);
                    String resultHEX = SerialTool.byteArrayToHexString(data);
                    readTextarea.setText(resultHEX);
                }
            } catch (Exception e) {
                Alert alert = new Alert(Alert.AlertType.ERROR, "发生了一个异常:".concat(e.getMessage()));
                alert.show();
            }
        });

        Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "串口开启成功!");
        alert.show();
    }

    /** 写入串口数据
     *
     */
    @FXML
    public void writeCom() {
        if(port == null){
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "请先开启串口!");
            alert.show();
            return;
        }

        String content = writeTextarea.getText();
        if(content == null || content.trim().length() <=0){
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "请填写写入的数据!");
            alert.show();
            return;
        }
        //设定发送字符串
        byte[] bs = SerialTool.hex2Bytes(content);
        SerialTool.sendToPort(port, bs);//写入,写入应该在监听器打开之后而不是之前
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "写入成功!");
        alert.show();
    }
}

完成后实际界面如下:
Java读取串口数据

6. 结尾

我写的demo源码地址
源码网盘下载地址 提取码:pz8i
参考文档-java串口通信demo

版权声明:程序员胖胖胖虎阿 发表于 2022年9月21日 上午10:24。
转载请注明:Java读取串口数据 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...