Java串口通信--------基于RXTX (附带资源地址)
最近帮老师做了一个小项目,一个牧场公司想用传感器收集一些环境信息,记录到数据库里去,然后加以分析查看。这里面和传感器通信用到了串口通信,我也是接触了一下,把用到的东西分享出来。
准备工作:
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进行监听:如图
然后运行程序,相互传输数据即可。