jSerialComm 教程

1. 简介

jSerialComm 是一个用于 Java 串行通信的库,相比 RXTX 和 Java Communications API,它具有以下优点:

  • 无需额外安装本地库(自动提取)

  • 跨平台支持(Windows、Linux、macOS、Solaris)

  • 易于使用的 API

  • 支持 USB 串口热插拔检测

2. 安装

Maven

html 复制代码
<dependency>
    <groupId>com.fazecast</groupId>
    <artifactId>jSerialComm</artifactId>
    <version>2.10.2</version>
</dependency>

3. 基础用法

3.1 列出可用串口

java 复制代码
import com.fazecast.jSerialComm.SerialPort;

public class ListPorts {
    public static void main(String[] args) {
        SerialPort[] ports = SerialPort.getCommPorts();
        System.out.println("可用串口:");
        for (SerialPort port : ports) {
            System.out.println(port.getSystemPortName() + " - " 
                + port.getDescriptivePortName() + " - " 
                + port.getPortDescription());
        }
    }
}

3.2 打开和配置串口

java 复制代码
SerialPort port = SerialPort.getCommPort("COM3"); // Windows
// SerialPort port = SerialPort.getCommPort("/dev/ttyUSB0"); // Linux/Mac

// 配置串口参数
port.setBaudRate(9600);
port.setNumDataBits(8);
port.setNumStopBits(1);
port.setParity(SerialPort.NO_PARITY);

// 打开串口
if (port.openPort()) {
    System.out.println("串口打开成功");
} else {
    System.out.println("串口打开失败");
    return;
}

// 设置读写超时
port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);

3.3 发送数据

java 复制代码
// 发送字符串
String data = "Hello Serial\n";
port.writeBytes(data.getBytes(), data.length());

// 发送字节数组
byte[] buffer = {0x01, 0x02, 0x03, 0x04};
port.writeBytes(buffer, buffer.length);

3.4 接收数据

java 复制代码
// 读取数据
byte[] readBuffer = new byte[1024];
int numRead = port.readBytes(readBuffer, readBuffer.length);
String received = new String(readBuffer, 0, numRead);
System.out.println("接收: " + received);

4. 完整示例

简单收发示例

java 复制代码
import com.fazecast.jSerialComm.SerialPort;
import java.util.Scanner;

public class SerialCommExample {
    public static void main(String[] args) {
        // 选择串口
        SerialPort[] ports = SerialPort.getCommPorts();
        if (ports.length == 0) {
            System.out.println("未找到串口");
            return;
        }
        
        SerialPort port = ports[0];
        System.out.println("使用串口: " + port.getSystemPortName());
        
        // 配置串口
        port.setBaudRate(9600);
        port.setNumDataBits(8);
        port.setNumStopBits(1);
        port.setParity(SerialPort.NO_PARITY);
        port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
        
        // 打开串口
        if (!port.openPort()) {
            System.out.println("无法打开串口");
            return;
        }
        
        // 创建读取线程
        Thread readThread = new Thread(() -> {
            byte[] buffer = new byte[1024];
            while (true) {
                int bytesRead = port.readBytes(buffer, buffer.length);
                if (bytesRead > 0) {
                    String data = new String(buffer, 0, bytesRead);
                    System.out.print("接收: " + data);
                }
            }
        });
        readThread.start();
        
        // 主线程发送数据
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入要发送的数据(输入 'exit' 退出):");
        
        while (true) {
            String input = scanner.nextLine();
            if (input.equalsIgnoreCase("exit")) {
                break;
            }
            input += "\n";
            port.writeBytes(input.getBytes(), input.length());
        }
        
        // 关闭串口
        port.closePort();
        System.exit(0);
    }
}

5. 事件监听方式

java 复制代码
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;

public class EventListenerExample {
    public static void main(String[] args) {
        SerialPort port = SerialPort.getCommPort("COM3");
        port.setBaudRate(9600);
        
        // 添加数据监听器
        port.addDataListener(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) {
                    return;
                }
                
                byte[] buffer = new byte[port.bytesAvailable()];
                int bytesRead = port.readBytes(buffer, buffer.length);
                
                if (bytesRead > 0) {
                    String data = new String(buffer);
                    System.out.println("收到数据: " + data);
                }
            }
        });
        
        port.openPort();
        
        // 保持程序运行
        try {
            Thread.sleep(60000); // 运行60秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        port.closePort();
    }
}

6. 高级功能

6.1 检测串口热插拔

java 复制代码
import com.fazecast.jSerialComm.*;

public class HotPlugDetection {
    public static void main(String[] args) {
        SerialPort.addCommPortDiscoveryListener(new SerialPortDiscoveryListener() {
            @Override
            public void portAdded(SerialPort port) {
                System.out.println("串口已连接: " + port.getSystemPortName());
            }
            
            @Override
            public void portRemoved(SerialPort port) {
                System.out.println("串口已移除: " + port.getSystemPortName());
            }
        });
        
        // 保持程序运行
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

6.2 获取串口详细信息

java 复制代码
SerialPort port = SerialPort.getCommPort("COM3");
System.out.println("系统名称: " + port.getSystemPortName());
System.out.println("描述性名称: " + port.getDescriptivePortName());
System.out.println("端口描述: " + port.getPortDescription());
System.out.println("制造商: " + port.getManufacturer());
System.out.println("产品ID: " + port.getProductID());
System.out.println("供应商ID: " + port.getVendorID());
System.out.println("串口号: " + port.getPortLocation());

6.3 配置流控制

java 复制代码
// 禁用所有流控制
port.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);

// 启用硬件流控制 (RTS/CTS)
port.setFlowControl(SerialPort.FLOW_CONTROL_RTS_ENABLED | 
                    SerialPort.FLOW_CONTROL_CTS_ENABLED);

// 启用软件流控制 (XON/XOFF)
port.setFlowControl(SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED | 
                    SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED);

7. 常见问题解决

7.1 权限问题 (Linux)

bash 复制代码
# 将用户添加到 dialout 组
sudo usermod -a -G dialout $USER

# 或者临时修改权限
sudo chmod 666 /dev/ttyUSB0

7.2 读取阻塞问题

java 复制代码
// TIMEOUT_READ_BLOCKING - 无限期等待
// TIMEOUT_READ_SEMI_BLOCKING - 等待直到有数据或超时
// TIMEOUT_NONBLOCKING - 立即返回,无数据则返回0
port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 500, 0);

8. 完整应用示例:串口调试助手

java 复制代码
import com.fazecast.jSerialComm.SerialPort;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.IOException;

public class SerialDebugger extends JFrame {
    private SerialPort selectedPort;
    private JTextArea textArea;
    private JTextField inputField;
    private JComboBox<String> portCombo;
    private JComboBox<Integer> baudRateCombo;
    private JButton openButton;
    private JButton sendButton;
    private boolean isOpen = false;
    
    public SerialDebugger() {
        setTitle("串口调试助手");
        setSize(600, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        initUI();
        refreshPorts();
    }
    
    private void initUI() {
        // 顶部控制面板
        JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        
        controlPanel.add(new JLabel("串口:"));
        portCombo = new JComboBox<>();
        controlPanel.add(portCombo);
        
        controlPanel.add(new JLabel("波特率:"));
        Integer[] baudRates = {9600, 19200, 38400, 57600, 115200};
        baudRateCombo = new JComboBox<>(baudRates);
        baudRateCombo.setSelectedIndex(0);
        controlPanel.add(baudRateCombo);
        
        JButton refreshButton = new JButton("刷新");
        refreshButton.addActionListener(e -> refreshPorts());
        controlPanel.add(refreshButton);
        
        openButton = new JButton("打开串口");
        openButton.addActionListener(e -> togglePort());
        controlPanel.add(openButton);
        
        add(controlPanel, BorderLayout.NORTH);
        
        // 数据显示区域
        textArea = new JTextArea();
        textArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        add(scrollPane, BorderLayout.CENTER);
        
        // 底部发送区域
        JPanel sendPanel = new JPanel(new BorderLayout());
        inputField = new JTextField();
        sendButton = new JButton("发送");
        sendButton.setEnabled(false);
        sendButton.addActionListener(e -> sendData());
        
        sendPanel.add(inputField, BorderLayout.CENTER);
        sendPanel.add(sendButton, BorderLayout.EAST);
        add(sendPanel, BorderLayout.SOUTH);
    }
    
    private void refreshPorts() {
        portCombo.removeAllItems();
        SerialPort[] ports = SerialPort.getCommPorts();
        for (SerialPort port : ports) {
            portCombo.addItem(port.getSystemPortName());
        }
    }
    
    private void togglePort() {
        if (!isOpen) {
            // 打开串口
            String portName = (String) portCombo.getSelectedItem();
            selectedPort = SerialPort.getCommPort(portName);
            selectedPort.setBaudRate((Integer) baudRateCombo.getSelectedItem());
            selectedPort.setNumDataBits(8);
            selectedPort.setNumStopBits(1);
            selectedPort.setParity(SerialPort.NO_PARITY);
            selectedPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
            
            if (selectedPort.openPort()) {
                isOpen = true;
                openButton.setText("关闭串口");
                sendButton.setEnabled(true);
                portCombo.setEnabled(false);
                baudRateCombo.setEnabled(false);
                
                // 启动读取线程
                new Thread(this::readData).start();
            } else {
                JOptionPane.showMessageDialog(this, "无法打开串口");
            }
        } else {
            // 关闭串口
            selectedPort.closePort();
            isOpen = false;
            openButton.setText("打开串口");
            sendButton.setEnabled(false);
            portCombo.setEnabled(true);
            baudRateCombo.setEnabled(true);
        }
    }
    
    private void readData() {
        byte[] buffer = new byte[1024];
        while (isOpen) {
            int bytesRead = selectedPort.readBytes(buffer, buffer.length);
            if (bytesRead > 0) {
                String data = new String(buffer, 0, bytesRead);
                SwingUtilities.invokeLater(() -> {
                    textArea.append("接收: " + data + "\n");
                    textArea.setCaretPosition(textArea.getDocument().getLength());
                });
            }
        }
    }
    
    private void sendData() {
        String data = inputField.getText();
        if (!data.isEmpty() && isOpen) {
            data += "\n";
            selectedPort.writeBytes(data.getBytes(), data.length());
            textArea.append("发送: " + data);
            inputField.setText("");
        }
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new SerialDebugger().setVisible(true);
        });
    }
}

9. 总结

jSerialComm 提供了简单而强大的串口通信 API,主要特点:

  • 简单直观的 API 设计

  • 跨平台兼容性好

  • 支持事件驱动编程

  • 提供热插拔检测

  • 性能稳定可靠

建议在实际项目中使用时,根据具体需求选择合适的通信模式(轮询或事件监听)。

相关推荐
sR916Mecz4 小时前
Netty 线程模型
java·数据库·oracle
Vfw3VsDKo4 小时前
Flink源码阅读:Netty通信
java·前端·flink
疯狂打码的少年4 小时前
【Day 6 Java转Python】字符串处理的“降维打击”
java·开发语言·python
EFCY1MJ904 小时前
MYSQL ID耗尽应急恢复方案
java·数据库·mysql
hogenlaw4 小时前
Stream流
android·java·开发语言
pl4H522a65 小时前
Python 高效实现 Excel 转 TXT 文本
java·python·excel
稻草猫.5 小时前
Spring事务操作全解析
java·数据库·后端·spring
她说..6 小时前
Java 基本数据类型高频面试题
java·开发语言·jvm·spring boot
y = xⁿ6 小时前
小林coding:HashMap的原理,ConcurrentHashMap实现逻辑,1.8并发是如何超越1.7的
java·面试·hash