最近幫老師做了一個小項目,一個牧場公司想用傳感器收集一些環境信息,記錄到數據庫里去,然后加以分析查看。這里面和傳感器通信用到了串口通信,我也是接觸了一下,把用到的東西分享出來。
准備工作:
RXTX:封裝了Java對串口操作的類庫,具體的話大家自己百度一下吧。 資源地址:http://pan.baidu.com/s/1nuLZex7 密碼 m358
VSPD(Virtual Serial Port Driver):可以建立兩個虛擬的串口,我們用這兩個虛擬串口模擬測試。 資源地址:http://pan.baidu.com/s/1dEQtfPn 密碼 nhnc
串口調試助手:可以模擬兩個串口之間的通信,作為我們的測試環境。 資源地址 :http://pan.baidu.com/s/1hs0eNxM 密碼 009k
下載RXTX資源,解壓壓縮包,里面應該有三個文件,兩個dll文件,一個jar文件,三個文件都拷貝到你項目的lib目錄,build path。然后在你項目的configure build path里面 找到Native library location,edit 把目錄定位到你存放之前那兩個dll文件的文件夾,也就是lib文件夾。 ok,如下圖,准備工作完畢。

具體代碼:
1 import java.io.BufferedInputStream; 2 import java.io.BufferedOutputStream; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.util.Enumeration; 7 import java.util.Properties; 8 import java.util.TooManyListenersException; 9 10 import org.springframework.beans.factory.annotation.Autowired; 11 12 import com.dodo.detection.service.Services; 13 14 import gnu.io.CommPortIdentifier; 15 import gnu.io.PortInUseException; 16 import gnu.io.SerialPort; 17 import gnu.io.SerialPortEvent; 18 import gnu.io.SerialPortEventListener; 19 20 public class ReadSerialPort implements Runnable, SerialPortEventListener { 21 22 private String appName = "dodo傳感器測試"; 23 private int timeout = 2000;//open 端口時的等待時間 24 private int threadTime = 0; 25 private String sport; 26 private CommPortIdentifier commPort; 27 private SerialPort serialPort; 28 private InputStream inputStream; 29 private OutputStream outputStream; 30 31 private Services service; 32 33 @Autowired 34 public void setService(Services service) { 35 this.service = service; 36 } 37 /** 38 * @方法名稱 :listPort 39 * @功能描述 :列出所有可用的串口 40 * @返回值類型 :void 41 */ 42 @SuppressWarnings("rawtypes") 43 public void listPort(){ 44 CommPortIdentifier cpid; 45 Enumeration en = CommPortIdentifier.getPortIdentifiers(); 46 47 System.out.println("now to list all Port of this PC:" +en); 48 49 while(en.hasMoreElements()){ 50 cpid = (CommPortIdentifier)en.nextElement(); 51 if(cpid.getPortType() == CommPortIdentifier.PORT_SERIAL){ 52 System.out.println(cpid.getName() + ", " + cpid.getCurrentOwner()); 53 } 54 } 55 } 56 57 public ReadSerialPort() { 58 } 60 61 62 /** 63 * @方法名稱 :selectPort 64 * @功能描述 :選擇一個端口,比如:COM1 65 * @返回值類型 :void 66 * @param portName 67 */ 68 @SuppressWarnings("rawtypes") 69 public void selectPort(String portName){ 70 71 this.commPort = null; 72 CommPortIdentifier cpid; 73 Enumeration en = CommPortIdentifier.getPortIdentifiers(); 74 75 while(en.hasMoreElements()){ 76 cpid = (CommPortIdentifier)en.nextElement(); 77 if(cpid.getPortType() == CommPortIdentifier.PORT_SERIAL 78 && cpid.getName().equals(portName)){ 79 this.commPort = cpid; 80 break; 81 } 82 } 83 84 openPort(); 85 } 86 87 /** 88 * @方法名稱 :openPort 89 * @功能描述 :打開SerialPort 90 * @返回值類型 :void 91 */ 92 private void openPort(){ 93 if(commPort == null) 94 log(String.format("無法找到名字為'%1$s'的串口!", commPort.getName())); 95 else{ 96 log("端口選擇成功,當前端口:"+commPort.getName()+",現在實例化 SerialPort:"); 97 98 try{ 99 serialPort = (SerialPort)commPort.open(appName, timeout); 100 log("實例 SerialPort 成功!"); 101 }catch(PortInUseException e){ 102 throw new RuntimeException(String.format("端口'%1$s'正在使用中!", 103 commPort.getName())); 104 } 105 } 106 } 107 108 /** 109 * @方法名稱 :checkPort 110 * @功能描述 :檢查端口是否正確連接 111 * @返回值類型 :void 112 */ 113 private void checkPort(){ 114 if(commPort == null) 115 throw new RuntimeException("沒有選擇端口,請使用 " + 116 "selectPort(String portName) 方法選擇端口"); 117 118 if(serialPort == null){ 119 throw new RuntimeException("SerialPort 對象無效!"); 120 } 121 } 122 123 /** 124 * @方法名稱 :write 125 * @功能描述 :向端口發送數據,請在調用此方法前 先選擇端口,並確定SerialPort正常打開! 126 * @返回值類型 :void 127 * @param message 128 */ 129 public void write(String message) { 130 checkPort(); 131 132 try{ 133 outputStream = new BufferedOutputStream(serialPort.getOutputStream()); 134 }catch(IOException e){ 135 throw new RuntimeException("獲取端口的OutputStream出錯:"+e.getMessage()); 136 } 137 138 try{ 139 outputStream.write(message.getBytes()); 140 log("信息發送成功!"); 141 }catch(IOException e){ 142 throw new RuntimeException("向端口發送信息時出錯:"+e.getMessage()); 143 }finally{ 144 try{ 145 outputStream.close(); 146 }catch(Exception e){ 147 } 148 } 149 } 150 151 /** 152 * @方法名稱 :startRead 153 * @功能描述 :開始監聽從端口中接收的數據 154 * @返回值類型 :void 155 * @param time 監聽程序的存活時間,單位為秒,0 則是一直監聽 156 */ 157 public void startRead(int time){ 158 checkPort(); 159 160 try{ 161 inputStream = new BufferedInputStream(serialPort.getInputStream()); 162 }catch(IOException e){ 163 throw new RuntimeException("獲取端口的InputStream出錯:"+e.getMessage()); 164 } 165 166 try{ 167 serialPort.addEventListener(this); 168 }catch(TooManyListenersException e){ 169 throw new RuntimeException(e.getMessage()); 170 } 171 172 serialPort.notifyOnDataAvailable(true); 173 174 log(String.format("開始監聽來自'%1$s'的數據--------------", commPort.getName())); 175 if(time > 0){ 176 this.threadTime = time*1000; 177 Thread t = new Thread(this); 178 t.start(); 179 log(String.format("監聽程序將在%1$d秒后關閉。。。。", threadTime)); 180 } 181 } 182 public void startRead(){ 183 checkPort(); 184 185 try{ 186 inputStream = new BufferedInputStream(serialPort.getInputStream()); 187 }catch(IOException e){ 188 throw new RuntimeException("獲取端口的InputStream出錯:"+e.getMessage()); 189 } 190 191 try{ 192 serialPort.addEventListener(this); 193 }catch(TooManyListenersException e){ 194 throw new RuntimeException(e.getMessage()); 195 } 196 197 serialPort.notifyOnDataAvailable(true); 198 199 log(String.format("開始監聽來自'%1$s'的數據--------------", commPort.getName())); 200 201 } 202 203 /** 204 * @方法名稱 :close 205 * @功能描述 :關閉 SerialPort 206 * @返回值類型 :void 207 */ 208 public void close(){ 209 serialPort.close(); 210 serialPort = null; 211 commPort = null; 212 } 213 214 215 public void log(String msg){ 216 System.out.println(appName+" --> "+msg); 217 } 218 219 220 /** 221 * 數據接收的監聽處理函數 222 */ 223 @Override 224 public void serialEvent(SerialPortEvent arg0) { 225 switch(arg0.getEventType()){ 226 case SerialPortEvent.BI:/*Break interrupt,通訊中斷*/ 227 case SerialPortEvent.OE:/*Overrun error,溢位錯誤*/ 228 case SerialPortEvent.FE:/*Framing error,傳幀錯誤*/ 229 case SerialPortEvent.PE:/*Parity error,校驗錯誤*/ 230 case SerialPortEvent.CD:/*Carrier detect,載波檢測*/ 231 case SerialPortEvent.CTS:/*Clear to send,清除發送*/ 232 case SerialPortEvent.DSR:/*Data set ready,數據設備就緒*/ 233 case SerialPortEvent.RI:/*Ring indicator,響鈴指示*/ 234 case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*Output buffer is empty,輸出緩沖區清空*/ 235 break; 236 case SerialPortEvent.DATA_AVAILABLE:/*Data available at the serial port,端口有可用數據。讀到緩沖數組,輸出到終端*/ 237 byte[] readBuffer = new byte[1024]; 238 String readStr=""; 239 String s2 = ""; 240 try { 241 242 while (inputStream.available() > 0) { 243 inputStream.read(readBuffer); 244 readStr += new String(readBuffer).trim(); 245 } 246 247 s2 = new String(readBuffer).trim(); 248 249 log("接收到端口返回數據(長度為"+readStr.length()+"):"+readStr); 250 log(s2); 251 service.addRecord(s2); 252 253 } catch (IOException e) { 254 e.printStackTrace(); 255 } 256 } 257 } 258 259 260 @Override 261 public void run() { 262 try{ 263 Thread.sleep(threadTime); 264 serialPort.close(); 265 log(String.format("端口''監聽關閉了!", commPort.getName())); 266 }catch(Exception e){ 267 e.printStackTrace(); 268 } 269 } 270 276 277 public String getSport() { 278 return sport; 279 } 280 281 public void setSport(String sport) { 282 this.sport = sport; 283 } 284 }
注:我里面的那個sport屬性是spring注入進來的,大家測試的時候可以寫死 比如“COM1”,然后里面的service都不需要的。有問題請留言。
測試過程:
public static void main(String[] args) { ReadSerialPortsp = new ReadSerialPort(); sp.listPort(); sp.selectPort("COM1"); sp.write("123456"); sp.write("hello"); sp.startRead(120); }
打開VSPD ,虛擬兩個串口,我這里虛擬的是COM1 和COM2

然后打開串口助手,找到COM2進行監聽:如圖

然后運行程序,相互傳輸數據即可。
