首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

Java 串口通訊實戰:基於 RXTX 的跨平台開發指南

     編輯:J2ME

前言

在經歷一年多的 RXTX 串口項目開發後,我將常見問題與解決方案整理於此,供後續開發者參考。RXTX 是基於 Sun 早期 Comm API 擴展的開源庫,突破了原版僅支援 Windows 的限制,實現了 Windows、Linux、Mac OS X 及 Solaris 的全平台串口/並口操作能力。

環境部署詳解

通用下載地址

官方發布頁:RXTX Download

Windows 平台

  • 將 RXTXcomm.jar 複製至 %JAVA_HOME%\jre\lib\ext
  • 將 rxtxSerial.dll 複製至 %JAVA_HOME%\jre\bin

Mac OS X (x86 / PPC)

官方提供安裝包,但手動配置易失敗。建議直接使用社區整合的安裝程式:
下載 RXTX_Tiger.pkg 一鍵完成環境佈署。

Linux (x86, x86_64, ia64 等)

  • RXTXcomm.jar → $JAVA_HOME/jre/lib/ext
  • librxtxSerial.so → $JAVA_HOME/jre/lib/[架構目錄] (如 i386)
  • 執行 chmod a+r librxtxSerial.so 賦予所有用戶讀取權限

Solaris (SPARC)

步驟同 Linux,同樣需釋放 .so 文件權限。

核心代碼實現

以下類別封裝了串口的開啟、讀寫、事件監聽與資源釋放,採用 SerialPortEventListener 異步接收資料,並提供同步寫入鎖保證線程安全。

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TooManyListenersException;
import gnu.io.*;

public class SerialComm implements SerialPortEventListener, Runnable {
    public final static String PORT_OWNER = "MonitorApp";

    private boolean isOpen, isStart, isSave, isPrint;
    private Thread readThread;
    private String portName, portAddress, dataProtocol;
    private CommPortIdentifier portId;
    private SerialPort serialPort;
    private DataInputStream inputStream;
    private OutputStream outputStream;
    private SimpleDateFormat formatter;
    private final Object readWriteLock = new Object();

    public SerialComm() {
        isOpen = isStart = false;
        isSave = true; isPrint = false;
        formatter = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss,SSS]");
        portName = "COM1"; portAddress = "LOCAL"; dataProtocol = "Gooseli";
    }

    public void init(String port, String protocol) throws Exception {
        portName = port; portAddress = portName; dataProtocol = protocol; init();
    }

    public void init(String port, String address, String protocol) throws Exception {
        portName = port; portAddress = address; dataProtocol = protocol; init();
    }

    public void init() throws Exception {
        if (isOpen) close();
        try {
            portId = CommPortIdentifier.getPortIdentifier(portName);
            serialPort = (SerialPort) portId.open(PORT_OWNER, 2000);
            inputStream = new DataInputStream(serialPort.getInputStream());
            outputStream = serialPort.getOutputStream();
            isOpen = true;
        } catch (NoSuchPortException | PortInUseException ex) {
            throw new Exception(ex.toString());
        }
    }

    public void start() throws Exception {
        if (!isOpen) throw new Exception(portName + " has not been opened.");
        try {
            readThread = new Thread(this); readThread.start();
            serialPort.notifyOnDataAvailable(true);
            serialPort.addEventListener(this);
            isStart = true;
        } catch (TooManyListenersException ex) {
            throw new Exception(ex.toString());
        }
    }

    @Override public void run() {
        String at = "at^hcmgr=1\r";
        String cmd = at + (char) Integer.parseInt("1a", 16) + "z";
        writeComm(cmd);
        isPrint = true;
    }

    public void stop() {
        if (isStart) {
            serialPort.notifyOnDataAvailable(false);
            serialPort.removeEventListener();
            isStart = false;
        }
    }

    public void close() {
        stop();
        if (isOpen) {
            try {
                inputStream.close(); outputStream.close(); serialPort.close();
                isOpen = false;
            } catch (IOException ignored) {}
        }
    }

    @Override public void serialEvent(SerialPortEvent event) {
        if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) readComm();
    }

    private void readComm() {
        StringBuilder buf = new StringBuilder();
        try (InputStreamReader isr = new InputStreamReader(inputStream, "UTF-8")) {
            int c; while ((c = isr.read()) != -1) buf.append((char) c);
            String data = buf.toString().trim();
            String ts = formatter.format(new Date());
            // 此處可對 data 進行協議解析、持久化或回調處理
            System.out.println(ts + " RECV: " + data);
        } catch (IOException ex) { ex.printStackTrace(); }
    }

    public void writeComm(String outString) {
        synchronized (readWriteLock) {
            try { outputStream.write(outString.getBytes()); }
            catch (IOException ex) { ex.printStackTrace(); }
        }
    }

    public static void main(String[] args) {
        SerialComm sc = new SerialComm();
        try {
            sc.init("COM3", "Air");        // Windows 測試
            // sc.init("/dev/ttyUSB0", "Air"); // Linux 測試
            sc.start();
        } catch (Exception ex) { ex.printStackTrace(); }
    }
}

關鍵點提示

  • 端口名稱:Windows 為 COMx,Linux/Mac 為 /dev/ttyUSBx 或 /dev/ttySx。
  • 波特率等參數:本例未顯式設定,實際項目需呼叫 serialPort.setSerialPortParams(...)。
  • 編碼處理:讀取時指定 UTF-8,若設備回傳 GBK/Big5 需對應調整。
  • 資源釋放:務必在應用退出前呼叫 close(),避免端口被鎖死。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved