ITPub博客

首页 > 应用开发 > IT综合 > Java串口通信完全指南:基于jSerialComm的核心技术与实践

Java串口通信完全指南:基于jSerialComm的核心技术与实践

投稿 IT综合 作者:bhluo 时间:2025-11-26 11:41:18 0 删除 编辑

# Java串口通信完全指南:基于jSerialComm的核心技术与实践


在工业控制、物联网设备和嵌入式系统开发中,串口通信仍然是不可或缺的技术。jSerialComm作为Java平台的串口通信库,为开发者提供了强大的跨平台串口操作能力。本文将深入解析jSerialComm的核心功能和使用方法。


## 环境配置与基础概念


### 项目依赖与初始化


```java

// Maven依赖配置

<dependency>

    <groupId>com.fazecast</groupId>

    <artifactId>jSerialComm</artifactId>

    <version>2.9.3</version>

</dependency>


// Gradle依赖配置

implementation 'com.fazecast:jSerialComm:2.9.3'


// 基础导入

import com.fazecast.jSerialComm.*;

import java.io.InputStream;

import java.io.OutputStream;

```


### 串口基础信息获取


```java

public class SerialPortDiscovery {

    

    /**

     * 发现系统中可用的串口

     */

    public static void listAvailablePorts() {

        SerialPort[] ports = SerialPort.getCommPorts();

        

        System.out.println("系统中可用的串口:");

        System.out.println("=================================");

        

        for (int i = 0; i < ports.length; i++) {

            SerialPort port = ports[i];

            System.out.printf("%d. 端口名称: %s%n", i + 1, port.getSystemPortName());

            System.out.printf("   描述: %s%n", port.getPortDescription());

            System.out.printf("   系统路径: %s%n", port.getSystemPortPath());

            System.out.printf("   厂商ID: 0x%04X%n", port.getVendorID());

            System.out.printf("   产品ID: 0x%04X%n", port.getProductID());

            System.out.println("---------------------------------");

        }

    }

    

    /**

     * 获取特定串口的详细信息

     */

    public static void printPortDetails(String portName) {

        SerialPort port = SerialPort.getCommPort(portName);

        

        if (port != null) {

            System.out.println("串口详细信息:");

            System.out.println("端口名称: " + port.getSystemPortName());

            System.out.println("描述: " + port.getPortDescription());

            System.out.println("波特率支持: " + Arrays.toString(port.getSupportedBaudRates()));

            System.out.println("数据位支持: " + Arrays.toString(port.getSupportedDataBits()));

            System.out.println("停止位支持: " + Arrays.toString(port.getSupportedStopBits()));

            System.out.println("校验位支持: " + Arrays.toString(port.getSupportedParities()));

        } else {

            System.out.println("未找到串口: " + portName);

        }

    }

    

    public static void main(String[] args) {

        listAvailablePorts()<"YC.1853.HK">;

        

        // 查看COM3的详细信息(Windows)

        printPortDetails("COM3");

    }

}

```


## 串口通信核心实现


### 基础串口配置与连接


```java

public class BasicSerialCommunication {

    private SerialPort serialPort;

    private InputStream inputStream;

    private OutputStream outputStream;

    private volatile boolean isConnected = false;

    

    /**

     * 打开并配置串口

     */

    public boolean openPort(String portName, int baudRate) {

        serialPort = SerialPort.getCommPort(portName);

        

        // 配置基本参数

        serialPort.setBaudRate(baudRate);

        serialPort.setNumDataBits(8);

        serialPort.setNumStopBits(SerialPort.ONE_STOP_BIT);

        serialPort.setParity(SerialPort.NO_PARITY);

        

        // 设置超时

        serialPort.setComPortTimeouts(

            SerialPort.TIMEOUT_READ_BLOCKING, 

            1000,  // 读取超时1秒

            0      // 写入无超时

        );

        

        // 打开端口

        if (serialPort.openPort()) {

            inputStream = serialPort.getInputStream();

            outputStream = serialPort.getOutputStream();

            isConnected = true;

            System.out.println("串口打开成功: " + portName);

            return true;

        } else {

            System.err.println("无法打开串口: " + portName);

            return false;

        }

    }

    

    /**

     * 关闭串口

     */

    public void closePort() {

        if (serialPort != null && serialPort.isOpen()) {

            serialPort.closePort();

            isConnected = false;

            System.out.println("串口已关闭");

        }

    }

    

    /**

     * 发送数据

     */

    public boolean sendData(byte[] data) {

        if (!isConnected) {

            System.err.println("串口未连接");

            return false;

        }

        

        try {

            outputStream.write(data);

            outputStream.flush();

            System.out.println("发送数据: " + Arrays.toString(data));

            return true;

        } catch (Exception e) {

            System.err.println("发送数据失败: " + e.getMessage());

            return false<"YINGCH.1853.HK">;

        }

    }

    

    /**

     * 发送字符串数据

     */

    public boolean sendString(String data) {

        return sendData(data.getBytes());

    }

    

    /**

     * 读取数据

     */

    public byte[] readData(int length) {

        if (!isConnected) {

            System.err.println("串口未连接");

            return new byte[0];

        }

        

        try {

            byte[] buffer = new byte[length];

            int bytesRead = inputStream.read(buffer);

            

            if (bytesRead > 0) {

                byte[] result = new byte[bytesRead];

                System.arraycopy(buffer, 0, result, 0, bytesRead);

                System.out.println("接收到数据: " + Arrays.toString(result));

                return result;

            }

        } catch (Exception e) {

            System.err.println("读取数据失败: " + e.getMessage());

        }

        

        return new byte[0];

    }

}

```


### 事件驱动的串口通信


```java

public class EventDrivenSerialPort implements SerialPortDataListener {

    private SerialPort serialPort;

    private DataReceivedCallback callback;

    

    public interface DataReceivedCallback {

        void onDataReceived(byte[] data);

        void onError(String errorMessage);

    }

    

    public EventDrivenSerialPort(DataReceivedCallback callback) {

        this.callback = callback;

    }

    

    /**

     * 配置并打开事件驱动的串口

     */

    public boolean openPortWithEvents(String portName, int baudRate) {

        serialPort = SerialPort.getCommPort(portName);

        

        // 基础配置

        serialPort.setBaudRate(baudRate);

        serialPort.setNumDataBits(8);

        serialPort.setNumStopBits(SerialPort.ONE_STOP_BIT);

        serialPort.setParity(SerialPort.NO_PARITY);

        

        // 设置事件监听

        serialPort.addDataListener(this);

        

        if (serialPort.openPort()) {

            System.out.println("事件驱动串口打开成功: " + portName);

            return true;

        } else {

            System.err.println("无法打开串口: " + portName);

            return false<"YINGCHAOO.1853.HK">;

        }

    }

    

    @Override

    public int getListeningEvents() {

        // 监听数据可用和通信错误事件

        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE | 

               SerialPort.LISTENING_EVENT_DATA_RECEIVED;

    }

    

    @Override

    public void serialEvent(SerialPortEvent event) {

        if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {

            try {

                // 读取所有可用数据

                byte[] buffer = new byte[serialPort.bytesAvailable()];

                int bytesRead = serialPort.readBytes(buffer, buffer.length);

                

                if (bytesRead > 0) {

                    byte[] data = new byte[bytesRead];

                    System.arraycopy(buffer, 0, data, 0, bytesRead);

                    

                    // 回调处理数据

                    if (callback != null) {

                        callback.onDataReceived(data);

                    }

                }

            } catch (Exception e) {

                if (callback != null) {

                    callback.onError("数据读取错误: " + e.getMessage());

                }

            }

        }

    }

    

    /**

     * 发送数据

     */

    public boolean sendData(byte[] data) {

        try {

            int bytesWritten = serialPort.writeBytes(data, data.length);

            return bytesWritten == data.length;

        } catch (Exception e) {

            if (callback != null) {

                callback.onError("数据发送错误: " + e.getMessage());

            }

            return false;

        }

    }

    

    public void close() {

        if (serialPort != null) {

            serialPort.removeDataListener();

            serialPort.closePort();

        }

    }

}

```


## 高级通信模式


### 协议帧处理


```java

public class FrameBasedCommunication {

    private static final byte FRAME_START = 0x02;   // STX

    private static final byte FRAME_END = 0x03;     // ETX

    private static final byte ESCAPE_CHAR = 0x10;   // DLE

    

    /**

     * 封装数据帧

     */

    public static byte[] encodeFrame(byte[] payload) {

        ByteArrayOutputStream frame = new ByteArrayOutputStream();

        

        // 帧开始

        frame.write(FRAME_START);

        

        // 转义处理

        for (byte b : payload) {

            if (b == FRAME_START || b == FRAME_END || b == ESCAPE_CHAR) {

                frame.write(ESCAPE_CHAR);

            }

            frame.write(b)<"YINCHAO.1853.HK">;

        }

        

        // 帧结束

        frame.write(FRAME_END);

        

        return frame.toByteArray();

    }

    

    /**

     * 解析数据帧

     */

    public static byte[] decodeFrame(byte[] frameData) throws IOException {

        if (frameData.length < 3 || frameData[0] != FRAME_START || frameData[frameData.length - 1] != FRAME_END) {

            throw new IOException("无效的帧格式");

        }

        

        ByteArrayOutputStream payload = new ByteArrayOutputStream();

        boolean escapeNext = false;

        

        // 跳过STX,处理到ETX之前

        for (int i = 1; i < frameData.length - 1; i++) {

            byte current = frameData[i];

            

            if (escapeNext) {

                payload.write(current);

                escapeNext = false;

            } else if (current == ESCAPE_CHAR) {

                escapeNext = true;

            } else {

                payload.write(current);

            }

        }

        

        if (escapeNext) {

            throw new IOException("帧转义序列不完整");

        }

        

        return payload.toByteArray();

    }

}


/**

 * 帧接收器 - 处理不完整帧和粘包问题

 */

public class FrameReceiver {

    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    private boolean inFrame = false;

    private boolean escapeNext = false;

    

    public List<byte[]> processData(byte[] newData) {

        List<byte[]> completeFrames = new ArrayList<>();

        

        for (byte b : newData) {

            if (!inFrame) {

                if (b == FrameBasedCommunication.FRAME_START) {

                    inFrame = true;

                    buffer.reset();

                }

                continue;

            }

            

            // 在帧内处理

            if (escapeNext) {

                buffer.write(b);

                escapeNext = false;

            } else if (b == FrameBasedCommunication.ESCAPE_CHAR) {

                escapeNext = true;

            } else if (b == FrameBasedCommunication.FRAME_END) {

                // 帧结束

                completeFrames.add(buffer.toByteArray());

                buffer.reset();

                inFrame = false;

                escapeNext = false;

            } else {

                buffer.write(b);

            }

        }

        

        return completeFrames;

    }

}

```


### 串口通信管理器


```java

public class SerialPortManager {

    private Map<String, SerialPort> openPorts = new ConcurrentHashMap<>();

    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);

    

    private static class SingletonHolder {

        private static final SerialPortManager INSTANCE = new SerialPortManager();

    }

    

    public static SerialPortManager getInstance() {

        return SingletonHolder.INSTANCE<"YINGCO.1853.HK">;

    }

    

    private SerialPortManager() {}

    

    /**

     * 打开串口连接

     */

    public SerialPort openConnection(String portName, int baudRate, 

                                   SerialPortDataListener listener) {

        if (openPorts.containsKey(portName)) {

            System.out.println("串口已打开: " + portName);

            return openPorts.get(portName);

        }

        

        SerialPort port = SerialPort.getCommPort(portName);

        port.setBaudRate(baudRate);

        port.setNumDataBits(8);

        port.setNumStopBits(SerialPort.ONE_STOP_BIT);

        port.setParity(SerialPort.NO_PARITY);

        

        if (listener != null) {

            port.addDataListener(listener);

        }

        

        if (port.openPort()) {

            openPorts.put(portName, port);

            System.out.println("串口连接成功: " + portName);

            return port;

        } else {

            System.err.println("串口连接失败: " + portName);

            return null;

        }

    }

    

    /**

     * 关闭串口连接

     */

    public void closeConnection(String portName) {

        SerialPort port = openPorts.remove(portName);

        if (port != null) {

            port.removeDataListener();

            port.closePort();

            System.out.println("串口已关闭: " + portName);

        }

    }

    

    /**

     * 关闭所有串口连接

     */

    public void closeAllConnections() {

        for (String portName : openPorts.keySet()) {

            closeConnection(portName);

        }

        executor.shutdown();

    }

    

    /**

     * 发送数据到指定串口

     */

    public boolean sendData(String portName, byte[] data) {

        SerialPort port = openPorts.get(portName);

        if (port == null) {

            System.err.println("串口未打开: " + portName);

            return false;

        }

        

        try {

            int bytesWritten = port.writeBytes(data, data.length);

            return bytesWritten == data.length;

        } catch (Exception e) {

            System.err.println("发送数据失败: " + e.getMessage());

            return false;

        }

    }

    

    /**

     * 异步发送数据

     */

    public CompletableFuture<Boolean> sendDataAsync(String portName, byte[] data) {

        return CompletableFuture.supplyAsync(() -> sendData(portName, data), executor);

    }

}

```


## 实际应用案例


### 传感器数据采集系统


```java

public class SensorDataCollector implements SerialPortDataListener {

    private SerialPortManager portManager = SerialPortManager.getInstance();

    private String portName;

    private Gson gson = new Gson();

    

    // 传感器数据结构

    public static class SensorData {

        public long timestamp;

        public double temperature;

        public double humidity;

        public double pressure;

        public int lightLevel;

        

        public SensorData(long timestamp, double temperature, double humidity, 

                         double pressure, int lightLevel) {

            this.timestamp = timestamp;

            this.temperature = temperature;

            this.humidity = humidity;

            this.pressure = pressure;

            this.lightLevel = lightLevel;

        }

    }

    

    public SensorDataCollector(String portName) {

        this.portName = portName;

    }

    

    public boolean startCollection() {

        SerialPort port = portManager.openConnection(portName, 9600, this);

        return port != null;

    }

    

    public void stopCollection() {

        portManager.closeConnection(portName);

    }

    

    @Override

    public int getListeningEvents() {

        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;

    }

    

    @Override

    public void serialEvent(SerialPortEvent event) {

        if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {

            return;

        }

        

        SerialPort port = (SerialPort) event.getSerialPort();

        byte[] buffer = new byte[port.bytesAvailable()];

        port.readBytes(buffer, buffer.length);

        

        // 解析传感器数据

        SensorData data = parseSensorData(buffer);

        if (data != null) {

            processSensorData(data);

        }

    }

    

    private SensorData parseSensorData(byte[] rawData) {

        try {

            String dataString = new String(rawData).trim();

            

            // 假设数据格式: T:25.6,H:60.5,P:1013.2,L:850

            String[] parts = dataString.split(",");

            

            if (parts.length == 4) {

                double temp = extractValue(parts[0]);  // T:25.6

                double humidity = extractValue(parts[1]); // H:60.5

                double pressure = extractValue(parts[2]); // P:1013.2

                int light = (int) extractValue(parts[3]); // L:850

                

                return new SensorData(System.currentTimeMillis(), temp, humidity, pressure, light);

            }

        } catch (Exception e) <"8B.8412.HK">{

            System.err.println("解析传感器数据失败: " + e.getMessage());

        }

        

        return null;

    }

    

    private double extractValue(String part) {

        String[] keyValue = part.split(":");

        if (keyValue.length == 2) {

            return Double.parseDouble(keyValue[1]);

        }

        return 0.0;

    }

    

    private void processSensorData(SensorData data) {

        // 转换为JSON并输出

        String json = gson.toJson(data);

        System.out.println("传感器数据: " + json);

        

        // 这里可以添加数据存储、转发等逻辑

        if (data.temperature > 35.0) {

            System.out.println("警告: 温度过高!");

        }

    }

}

```


### 工业设备控制协议


```java

public class IndustrialDeviceController {

    private SerialPortManager portManager = SerialPortManager.getInstance();

    private String portName;

    private FrameReceiver frameReceiver = new FrameReceiver();

    

    // 设备指令定义

    public enum DeviceCommand {

        READ_STATUS((byte) 0x01),

        SET_OUTPUT((byte) 0x02),

        READ_SENSORS((byte) 0x03),

        RESET_DEVICE((byte) 0x04);

        

        private final byte code;

        

        DeviceCommand(byte code) {

            this.code = code;

        }

        

        public byte getCode() {

            return code;

        }

    }

    

    public IndustrialDeviceController(String portName) {

        this.portName = portName;

    }

    

    /**

     * 发送设备指令

     */

    public boolean sendCommand(DeviceCommand command, byte[] parameters) {

        byte[] payload = new byte[1 + (parameters != null ? parameters.length : 0)];

        payload[0] = command.getCode();

        

        if (parameters != null) {

            System.arraycopy(parameters, 0, payload, 1, parameters.length);

        }

        

        byte[] frame = FrameBasedCommunication.encodeFrame(payload);

        return portManager.sendData(portName, frame);

    }

    

    /**

     * 读取设备状态

     */

    public DeviceStatus readDeviceStatus() {

        if (sendCommand(DeviceCommand.READ_STATUS, null)) {

            // 等待响应

            try {

                Thread.sleep(100);

                // 在实际应用中,这里应该实现响应等待机制

                return new DeviceStatus(); // 模拟返回状态

            } catch (InterruptedException e) {

                Thread.currentThread().interrupt();

            }

        }

        return null;

    }

    

    /**

     * 设置设备输出

     */

    public boolean setOutput(int outputNumber, boolean state) {

        byte[] params = new byte[2];

        params[0] = (byte) outputNumber;

        params[1] = (byte) (state ? 1 : 0);

        

        return sendCommand(DeviceCommand.SET_OUTPUT, params);

    }

    

    public boolean connect() {

        SerialPort port = portManager.openConnection(portName, 115200, new SerialPortDataListener() {

            @Override

            public int getListeningEvents() {

                return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;

            }

            

            @Override

            public void serialEvent(SerialPortEvent event) {

                if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {

                    SerialPort port = (SerialPort) event.getSerialPort();

                    byte[] buffer = new byte[port.bytesAvailable()];

                    port.readBytes(buffer, buffer.length);

                    

                    // 处理接收到的帧

                    List<byte[]> frames = frameReceiver.processData(buffer);

                    for (byte[] frame : frames)<"2J.8412.HK"> {

                        handleDeviceResponse(frame);

                    }

                }

            }

        });

        

        return port != null;

    }

    

    private void handleDeviceResponse(byte[] frameData) {

        try {

            byte[] payload = FrameBasedCommunication.decodeFrame(frameData);

            // 处理设备响应

            System.out.println("收到设备响应: " + Arrays.toString(payload));

        } catch (IOException e) {

            System.err.println("解析设备响应失败: " + e.getMessage());

        }

    }

    

    public void disconnect() {

        portManager.closeConnection(portName);

    }

    

    // 设备状态类

    public static class DeviceStatus {

        public boolean isOnline;

        public int errorCode;

        public double voltage;

        public int outputStates;

        

        // 构造函数、getter、setter等

    }

}

```


## 调试与故障排除


### 串口调试工具


```java

public class SerialPortDebugger implements SerialPortDataListener {

    private PrintWriter logWriter;

    private boolean logToFile = false;

    

    public SerialPortDebugger() {

        try {

            logWriter = new PrintWriter(new FileWriter("serial_debug.log", true));

            logToFile = true;

        } catch (IOException e) {

            System.err.println("无法创建日志文件: " + e.getMessage());

            logWriter = new PrintWriter(System.out);

        }

    }

    

    @Override

    public int getListeningEvents() {

        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE | 

               SerialPort.LISTENING_EVENT_DATA_WRITTEN;

    }

    

    @Override

    public void serialEvent(SerialPortEvent event) {

        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);

        SerialPort port = (SerialPort) event.getSerialPort();

        

        switch (event.getEventType()) {

            case SerialPort.LISTENING_EVENT_DATA_AVAILABLE:

                byte[] buffer = new byte[port.bytesAvailable()];

                int bytesRead = port.readBytes(buffer, buffer.length);

                logData("RECV", timestamp, port.getSystemPortName(), buffer, bytesRead);

                break;

                

            case SerialPort.LISTENING_EVENT_DATA_WRITTEN:

                logWriter.printf("[%s] [%s] 数据发送完成%n", timestamp, port.getSystemPortName());

                break<"5L.8412.HK">;

        }

        

        logWriter.flush();

    }

    

    private void logData(String direction, String timestamp, String portName, 

                        byte[] data, int length) {

        logWriter.printf("[%s] [%s] %s %d bytes: ", timestamp, portName, direction, length);

        

        // 十六进制格式

        for (int i = 0; i < Math.min(length, 32); i++) {

            logWriter.printf("%02X ", data[i]);

        }

        

        if (length > 32) {

            logWriter.print("...");

        }

        

        logWriter.println();

        

        // ASCII格式(可打印字符)

        logWriter.printf("[%s] [%s] %s ASCII: ", timestamp, portName, direction);

        for (int i = 0; i < Math.min(length, 32); i++) {

            char c = (char) data[i];

            logWriter.print(Character.isISOControl(c) ? '.' : c);

        }

        logWriter.println();

    }

    

    public void close() {

        if (logToFile) {

            logWriter.close();

        }

    }

}

```


## 总结


jSerialComm为Java开发者提供了强大而灵活的串口通信能力,从基础的串口操作到复杂的工业协议实现,都能找到相应的解决方案。通过本文介绍的核心技术和实际案例,开发者可以快速掌握串口通信的关键要点。


在实际项目中,建议根据具体需求选择合适的通信模式,合理处理数据帧和异常情况,确保通信的稳定性和可靠性。随着对jSerialComm的深入理解,开发者可以构建出更加健壮和高效的串口通信应用。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3103486/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933924