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