转载

Java comm串口通信Utils类

javax.comm包提供了java原生的串口通信API,实际中用到的场景很多,例如很多设备的控制信号都是通过串口进行控制的,只要向指定串口发送指定消息,就可以控制设备或读取设备信息,例如读取温度传感器信息、控制自动贩卖机出货等等。

使用javax.comm进行串口通信大概分为以下几个步骤:

1、选择一个可利用串口如COM1,得到一个CommPortIdentifier类。

2、设置初始化参数(波特率、数据位、停止位、校验位),利用CommPortIdentifier.open()方法得到一个SerialPort。

3、利用SerialPort.getOutputStream得到串口输出流,向串口写入数据。

4、利用SerialPort.addEventListener(SerialPortEventListener listener)为串口添加监听事件,当串口返回数据时,在SerialPortEventListener监听器的public void serialEvent(SerialPortEvent arg0)方法中,通过SerialPort.getInputStream得到串口输入流来读取响应数据。

这里提供一个串口通信Utils类。由于串口的通信机制,SerialPortEventListener监听器每接收到8个字节时会调用一次serialEvent,因此在数据未读取完毕时需要追加读入字节数组,这样才能取到完整的返回字节数组。

源码如下,代码中的Log记录类和自定义Exception类请自行替换后通过编译。

Util类 SerialPortCommUtil:

/**  *   */ package com.ktvm.common.serial;  import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import java.util.Timer; import java.util.TimerTask;  import javax.comm.CommPortIdentifier; import javax.comm.SerialPort; import javax.comm.SerialPortEvent; import javax.comm.SerialPortEventListener;  import com.ktvm.common.exception.KtVendExceptoin; import com.ktvm.common.util.LogUtil; import com.ktvm.common.util.SysConstant;  /**  * @author weih535  * 温度监控器  */ public class SerialPortCommUtil implements SerialPortEventListener{  private SerialPort serialPort;  private InputStream inputStream;  private OutputStream outputStream;  private CommPortIdentifier commPort;  private int timeout = 2000; // open 端口时的等待时间  /**串口响应时间*/  private int responseTime;  private String portName; //端口名称  private int baudRate;  //波特率  private int dataBits=8;  //数据位  private int stopBit=1;  //停止位  private int verifyBit=0; //检验位    private int rtnDataLen = 0; //串口返回字节数据长度  private int rtnCount = 0; //返回字节数为rtnlength时,需要回调多少次    private int readlength = 0;  private boolean initTest = false;    private int callbackTimes; //串口通信当前回调次数  byte[] readBuffer = null;    /**定时器*/  private Timer timer;      public SerialPortCommUtil(String portName, int baudRate, int dataBits,    int stopBit, int verifyBit) {   this.portName = portName.toUpperCase();   this.baudRate = baudRate;   this.dataBits = dataBits;   this.stopBit = stopBit;   this.verifyBit = verifyBit;  }   /**   * 初始化串口通信   */  public void initSerial() {   LogUtil.info(LogUtil.INFO_SERIAL + "SerialCommunication.initSerial 串口通信初始化!");   responseTime = SysConstant.SERIAL_RESPONSE_TIME * 1000;   try {    listPort();    selectPort(portName);    resetCallbackTimes();    initTest = true;   } catch (KtVendExceptoin e) {    LogUtil.error(e, e.getMessage());    e.getMessage();   }  }    /**   * 列出所有可用的串口   *    * @return :void   */  @SuppressWarnings("rawtypes")  public void listPort() {   LogUtil.info(LogUtil.INFO_SERIAL + "SerialCommunication.listPort 列出所有可用的串口 ");   CommPortIdentifier cpid;   Enumeration en = CommPortIdentifier.getPortIdentifiers();    while (en.hasMoreElements()) {    cpid = (CommPortIdentifier) en.nextElement();    if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL) {     LogUtil.debug(cpid.getName() + ", " + cpid.getCurrentOwner());    }   }  }   /**   * 选择一个端口(比如:COM1)   *    * @param portName   * @return :void   */  @SuppressWarnings("rawtypes")  public void selectPort(String portName) {   LogUtil.info(LogUtil.INFO_SERIAL + "选择端口: " + portName);   this.commPort = null;   CommPortIdentifier cpid;   Enumeration en = CommPortIdentifier.getPortIdentifiers();   while (en.hasMoreElements()) {    cpid = (CommPortIdentifier) en.nextElement();    if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL && cpid.getName().equals(portName)) {     this.commPort = cpid;     break;    }   }   openPort(portName);  }   /**   * 打开SerialPort   *    * @return :void   */  private void openPort(String portName) {   if (commPort == null)    throw new KtVendExceptoin(String.format("无法找到名字为'%1$s'的串口!", portName));   else {    LogUtil.debug("端口选择成功,当前端口:" + commPort.getName() + ",现在实例化 SerialPort:");    try {     serialPort = (SerialPort) commPort.open("TemperatureMonitor", timeout);     serialPort.setSerialPortParams(baudRate,        SysConstant.SERIAL_DATA_BITS,        SysConstant.SERIAL_STOP_BITS,        SysConstant.SERIAL_PARITY);          serialPort.addEventListener(this);     LogUtil.debug("实例 SerialPort 成功!");    } catch (Exception e) {     throw new KtVendExceptoin(String.format("端口'%1$s'正在使用中!", commPort.getName()), e);    }   }  }    /**   * 数据接收的监听处理函数   *    * @return : void   */  @Override  public void serialEvent(SerialPortEvent arg0) {   if(rtnDataLen == 0)    throw new KtVendExceptoin("返回数据长度未初始化...");   switch (arg0.getEventType()) {   case SerialPortEvent.BI:/* Break interrupt,通讯中断 */   case SerialPortEvent.OE:/* Overrun error,溢位错误 */   case SerialPortEvent.FE:/* Framing error,传帧错误 */   case SerialPortEvent.PE:/* Parity error,校验错误 */   case SerialPortEvent.CD:/* Carrier detect,载波检测 */   case SerialPortEvent.CTS:/* Clear to send,清除发送 */   case SerialPortEvent.DSR:/* Data set ready,数据设备就绪 */   case SerialPortEvent.RI:/* Ring indicator,响铃指示 */   case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/* Output buffer is empty,输出缓冲区清空*/    break;   case SerialPortEvent.DATA_AVAILABLE:/* Data available at the serial port 端口有可用数据*/    //温度监控读取响应数据分两次返回,因此在第二次返回时接着上一次的开始读    if(callbackTimes % rtnCount == 0){     if(readBuffer == null)      readBuffer = new byte[rtnDataLen];     readlength = 0;     resetCallbackTimes();     }     try {     // 关闭定时器     timer.cancel();     // 读取串口返回信息     while (inputStream.available() > 0) {      readlength += inputStream.read(readBuffer, readlength, rtnDataLen-readlength);     }     System.out.println("callback:" + callbackTimes + "--readed:" + readlength);     callbackTimes++;     LogUtil.debug("串口返回数据[len:" +readlength +"]" + showByteData(readBuffer, readlength));     if(initTest) {      if(verifyRtnData(readBuffer)) {       LogUtil.info(LogUtil.INFO_SERIAL + "返回数据校验成功!");       // TODO write info to server       if(callbackTimes % 2 == 0){        //读取第二次数据上传到服务器       }             } else {       LogUtil.error(LogUtil.INFO_SERIAL + "返回数据校验失败!");      }      return;     }     serialPort.notifyOnDataAvailable(false);         } catch (Exception e) {     e.printStackTrace();     LogUtil.error(new KtVendExceptoin("串口通信失败!", e));    }   }  }    /**   * 向端口发送数据   *    * @param message   * @return :void   */  public void write(byte[] message) {   LogUtil.info(LogUtil.INFO_SERIAL + portName + "发送数据Preapre:" + showByteData(message, 8));   checkPort();   serialTimeOut();    try {    outputStream = new BufferedOutputStream(serialPort.getOutputStream());   } catch (IOException e) {    throw new KtVendExceptoin("获取端口的OutputStream出错:" + e.getMessage(), e);   }    try {    outputStream.write(message);    LogUtil.info(LogUtil.INFO_SERIAL + portName + "发送数据成功");    if(inputStream == null){     inputStream = new BufferedInputStream(serialPort.getInputStream());    }    serialPort.notifyOnDataAvailable(true);     //   startRead();   } catch (IOException e) {    throw new KtVendExceptoin(portName + "向端口发送信息时出错:" + e.getMessage(), e);   } finally {    try {     outputStream.close();    } catch (Exception e) {    }   }  }     /**   * 显示串口返回的数据   *    * @param b   */  private String showByteData(byte[] b, int length) {   String str = "";   for (int i=0; i<b.length && i < length; i++) {    str += String.format("%02X", b[i]) + " ";   }   return str;  }  /**   * 关闭 SerialPort   *    * @return :void   */  public void close() {   final Timer closeTimer = new Timer();   TimerTask task=new TimerTask() {              @Override             public void run() {              if(serialPort != null)            serialPort.close();           serialPort = null;           commPort = null;                      if(inputStream!=null){      try {       inputStream.close();      } catch (IOException e) {       e.printStackTrace();      }           }           closeTimer.cancel();           LogUtil.info(LogUtil.INFO_SERIAL  + portName +  "已关闭!");             }         };         closeTimer.schedule(task, 2000);     }  /**   * 串口通信定时器(检查是否有数据返回)   */  private void serialTimeOut(){   timer = new Timer();   TimerTask timerTask=new TimerTask() {              @Override             public void run() {                 LogUtil.error(LogUtil.ERR + "串口通信5秒内没有反应!");                 if(initTest) {      LogUtil.info(LogUtil.INFO_SERIAL + "串口测试失败!");      timer.cancel();      return;     }                 //rewrite();                 timer.cancel();             }         };         timer.schedule(timerTask, responseTime);     }    private boolean verifyRtnData(byte[] readBuffer) {   return true;  }   /**   * 检查端口是否正确连接   *    * @return :void   */  private void checkPort() {   if (commPort == null) {    throw new KtVendExceptoin("没有找到端口!");   }    if (serialPort == null) {    throw new KtVendExceptoin("SerialPort 对象无效!");   }  }      private void resetCallbackTimes(){   this.callbackTimes = 0;  }  public String getPortName() {   return portName;  }  public void setPortName(String portName) {   this.portName = portName.toUpperCase();  }  public int getBaudRate() {   return baudRate;  }  public void setBaudRate(int baudRate) {   this.baudRate = baudRate;  }   public int getDataBits() {   return dataBits;  }   public void setDataBits(int dataBits) {   this.dataBits = dataBits;  }   public int getStopBit() {   return stopBit;  }   public void setStopBit(int stopBit) {   this.stopBit = stopBit;  }   public int getVerifyBit() {   return verifyBit;  }   public void setVerifyBit(int verifyBit) {   this.verifyBit = verifyBit;  }    public int getRtnDataLen() {   return rtnDataLen;  }   public void setRtnDataLen(int rtnDataLen) {   this.rtnDataLen = rtnDataLen;   this.rtnCount = (int)(Math.ceil((double)rtnDataLen / 8));   System.out.println("rtnCount:" + rtnCount);  }           }

测试类:

package com.ktvm.common.serial; public class TestComm {  public static void main(String[] args) {   testSerial();  }  private static void testSerial() {   final SerialPortCommUtil sp = new SerialPortCommUtil("com1", 9600, 8 , 1, 0);   sp.setRtnDataLen(11);   sp.initSerial();   sp.write(new byte[]{(byte)0xFE, 0x04, 0x00 ,0x00 ,0x00 ,0x03, (byte)0xA4, 0x04});   sp.close();   //定时写入、读取测试   /*final Timer closeTimer = new Timer();   TimerTask task=new TimerTask() {   @Override  public void run() {   count++;   sp.write(new byte[]{(byte)0xFE, 0x04, 0x00 ,0x00 ,0x00 ,0x03, (byte)0xA4, 0x04});   if(count == 2){    closeTimer.cancel();    sp.close();   }  }         };         closeTimer.schedule(task, 1000, 3000);*/  } } 

运行结果如下:

2015-10-24 17:34:45:789  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] SerialCommunication.initSerial 串口通信初始化!

2015-10-24 17:34:45:791  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] SerialCommunication.listPort 列出所有可用的串口

2015-10-24 17:34:45:798 DEBUG [LogUtil.java:40] - COM1, Port currently not owned

2015-10-24 17:34:45:799  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] 选择端口: COM1

2015-10-24 17:34:45:800 DEBUG [LogUtil.java:40] - 端口选择成功,当前端口:COM1,现在实例化 SerialPort:

2015-10-24 17:34:45:803 DEBUG [LogUtil.java:40] - 实例 SerialPort 成功!

2015-10-24 17:34:45:806  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] COM1发送数据Preapre:FE 04 00 00 00 03 A4 04

2015-10-24 17:34:45:807  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] COM1已发送数据:FE 04 00 00 00 03 A4 04

callback:0--readed:8

2015-10-24 17:34:45:833 DEBUG [LogUtil.java:40] - 串口返回数据[len:8]FE 04 06 0A DF 0A E2 0A

2015-10-24 17:34:45:833  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] 返回数据校验成功!

callback:1--readed:11

2015-10-24 17:34:45:841 DEBUG [LogUtil.java:40] - 串口返回数据[len:11]FE 04 06 0A DF 0A E2 0A EC 14 DD

2015-10-24 17:34:45:841  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] 返回数据校验成功!

2015-10-24 17:34:47:808  INFO [LogUtil.java:36] - [DEBUGINFO][SERIAL] 温度监控器已关闭!

已有 0 人发表留言,猛击->> 这里 <<-参与讨论

ITeye推荐

  • —软件人才免语言低担保 赴美带薪读研!—
正文到此结束
Loading...