最近在做傳感器數據采集的工作,底層是基於Zigbee的無線傳感網絡,所有數據采集到Zigbee協調器上然後通知上位機數據采集完成,上位機通過USB轉串口去讀取數據就可以了。那麼問題來了,如何進行串口通訊呢?老板說你用Java寫個程序好了嘛,用Java寫串口程序也是醉了。實驗室也沒別人寫了,所以就讓我寫了。當我聽到要讓我用Java寫串口通訊程序的時候我本來是拒絕的,然後。。。就沒有然後了。。就只能寫了。
網上看了一下,最後用了一個開源的Java串口通訊庫RXTX做串口通訊,下面記錄一下RXTX的使用方法。
RXTX做串口通訊,有一個jar包(RXTXcomm.jar)和一個rxtxSerial.dll(Windows環境下)或者librxtxSerial.so(Linux環境下),因為開發是在Windows上,但是工作是在Linux上,所以兩個都用到了。
Windows環境下
文檔裡是這麼寫的
Copy rxtxSerial.dll ---> <JAVA_HOME>\jre\bin
但是這個做了之後並不能用,會有一些很奇怪的問題,不知道是不是我的Java環境配置有問題還是怎麼了,我是把dll文件copy到了C:\Windows\System32,然後一切正常,一直很奇怪,為什麼要cp到<JAVA_HOME>\jre\bin呢?求解答!
Linux環境下
Copy librxtxSerial.so ---> <JAVA_HOME>/jre/lib/i386/
or
Copy librxtxSerial.so ---> <JAVA_HOME>/jre/lib/x86_64/
這個按照文檔沒問題。
小問題
我用的是rxtx-2.2pre2版本的,文檔裡有寫運行時會報版本不匹配的WARNING,實際使用中的確也是這樣的,不過目前還沒有別的問題,不影響使用。
1.查找端口
/** * 查找所有可用端口 * * @return 所有端口列表 */
publicstaticfinal ArrayList<String> findPort() {
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();//獲得所有串口
ArrayList<String> portNameList = new ArrayList<>();
//串口名字添加到List並返回
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
2.打開端口
/** * 打開串口 * * @param portName 端口名稱 * @param baudrate 波特率 * @return 串口對象 */
publicstaticfinal 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) {
LOGGER.error("Set Serialport Parameters failure", e);
}
System.out.println("Open " + portName + " sucessfully !");
return serialPort;
} else {
LOGGER.error("This port is not a serialport");
return null;
}
} catch (NoSuchPortException | PortInUseException e) {
LOGGER.error("There is no " + portName + "or it's occupied!", e);
return null;
}
}
3.發送數據
/** * 發送數據 * * @param serialPort 串口對象 * @param order 命令字節 */
publicvoidsendToPort(SerialPort serialPort, byte[] order) {
try {
OutputStream out = serialPort.getOutputStream();
out.write(order);
out.flush();
out.close();
} catch (IOException e) {
LOGGER.error("Send to SerialPort failure", e);
}
}
4.讀取數據
/** * 讀取數據 * * @return 字節ArrayList */
public byte[] readFromPort(InputStream inStream) {
byte[] bytes = null;
try {
while (true) {
//獲取buffer裡的數據長度
int bufflenth = inStream.available();
if (0 == bufflenth) {
break;
}
bytes = new byte[bufflenth];
inStream.read(bytes);
}
} catch (IOException e) {
LOGGER.error("Read Data Failure", e);
}
return bytes;
}
1.實現監聽器
繼承SerialPortEventListener然後重寫serialEvent,然後再各個對應case裡面寫代碼就好啦。
public class TestExample implements SerialPortEventListener {
@Override
publicvoidserialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()) {
case SerialPortEvent.BI: // 10通訊中斷
case SerialPortEvent.OE: // 7溢位錯誤
case SerialPortEvent.FE: // 9幀錯誤
case SerialPortEvent.PE: // 8奇偶校驗錯
case SerialPortEvent.CD: // 6載波檢測
case SerialPortEvent.CTS: // 3清除發送
case SerialPortEvent.DSR: // 4數據設備准備好
case SerialPortEvent.RI: // 5振鈴指示
case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2輸出緩沖區已清空
case SerialPortEvent.DATA_AVAILABLE: // 1讀到可用數據時激活
}
}
}
2.給串口添加監聽器
/** * 添加監聽器 * * @param port 串口對象 * @param listener 串口監聽器 */
publicstaticvoidaddListener(SerialPort port, SerialPortEventListener listener) {
try {
// 給串口添加監聽器
port.addEventListener(listener);
// 設置當有數據到達時喚醒監聽接收線程
port.notifyOnDataAvailable(true);
port.notifyOnBreakInterrupt(true);
System.out.println("Add listeners to " + port.getName() + " sucessfully !");
} catch (TooManyListenersException e) {
LOGGER.error("There is too many listeners !", e);
}
}
** 一定記得從串口發指令取數據之後加一個延時,等待底層數據傳輸完成再去buffer裡面取,不然很大可能數據包不完整。 **