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推荐