准备工作
- 首先下载官方提供的jar包和.dll文件,dll文件有两个,一个是rxtxSerial,一个是rxtxParallel,分别表示串口和并口。
- 因为我开发串口,所以把rxtxSerial.dll放到了jdk1.8.0_171/jre/bin和jre1.8.0_171/bin目录下,官方说两个都放,我没有去验证只放其中一个有没有差别。直接按照官方的直接放了。
- 接下来将jar包放入工程目录下的lib文件夹,并引用。
PS:在程序开发之前,强烈建议用测试工具测试设备是否已经可用。
程序包括的部分
- 初始化对象以及设置监听的init函数
- 监听消息的函数 serialEvent()(该函数来自实现的接口SerialPortEventListener)
- 消息发送函数 send()
- 消息接收的函数 receive()
- 关闭函数 close()
- 因为消息是16进制字符串,所以还有16进制字符串转byte数组以及byte数组转字符串函数
(这两个函数随便网上copy的,不是我自己写的,只修改了一句)
程序参数
- PORT_NAME = “COM3”,这个参数的参数值看的是设备连接上后的设备管理器端口部分
- BIT_RATE = 9600;
DATA_BITS = SerialPort.DATABITS_8;
STOP_BIT = SerialPort.STOPBITS_1;
PARITY_BIT = SerialPort.PARITY_NONE;
这四个参数分别表示设备的比特率,数据位,停止位,和校验位,在设备使用说明上提供了。 - serialPort,这个参数用来设置串口的参数
- InputStream in;OutputStream out;这两个参数分别用来读写数据
代码部分
初始化部分基本参照官网代码,只是稍作修改。
整个过程
- 根据端口名称获取端口对象
- 判断对象是否被占用
- 判断端口类型,其中1代表串口,如果是串口就打开串口,设置设备参数,设置输入输出流,以及设置数据监听
public void init() {try {CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(PORT_NAME);if (portIdentifier.isCurrentlyOwned()){System.out.println("Error: Port is currently in use");}else if(portIdentifier.getPortType()==1){serialPort = (SerialPort) portIdentifier.open(PORT_NAME,1000);serialPort.setSerialPortParams(BIT_RATE,DATA_BITS,STOP_BIT,PARITY_BIT);in = serialPort.getInputStream();out = serialPort.getOutputStream();serialPort.addEventListener(this);serialPort.notifyOnDataAvailable(true);}else{System.out.println("Error: Only serial ports are handled by this example.");}} catch (Exception e) {e.printStackTrace();}}
serialEvent函数,大概就是用来监听事件,他包括很多事件,但是我只判断一个,是否有是可用数据,如果存在可用数据就接收。
public void serialEvent(SerialPortEvent event) {switch (event.getEventType()){case SerialPortEvent.DATA_AVAILABLE:receive();break;}}
receive函数,用来接收收到的数据,这里比较好理解,通过上面初始化的输入流,将获取到的数据存入byte数组,并且将其转成字符串返回。
public String receive(){byte[] buffer = new byte[128];int data;String result = null;try{int len = 0;while ( ( data = in.read()) > -1 ){buffer[len++] = (byte) data;}byte[] copyValue = new byte[len];System.arraycopy(buffer,0,copyValue,0,len);result = ByteArrayToString(copyValue);}catch ( IOException e ){e.printStackTrace();}return result;}
send函数,send函数将要发送的16进制字符串消息转换成byte数组发送,并且让线程休眠1秒。
public void send(String message){try {byte[] bytes = hexStrToByteArray(message);out.write(bytes);Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}}
官网还有其他地方的收发数据都用到了线程,但是在写的过程中发现并没有使用线程的必要,所以我去掉了线程这个部分。如果发送的数据不是类似我的16进制字符串,完全可以去掉那两个转换代码,所以看情况适当修改。
整个代码
/*** @author:ms.y* @create 2019/1/24-8:48*/
public class CommUtil implements SerialPortEventListener{private static final String PORT_NAME = "COM3";private static final int BIT_RATE = 9600;public static final int DATA_BITS = SerialPort.DATABITS_8;public static final int STOP_BIT = SerialPort.STOPBITS_1;public static final int PARITY_BIT = SerialPort.PARITY_NONE;private static SerialPort serialPort;private static InputStream in;private static OutputStream out;private static CommUtil commUtil;private CommUtil(){}public static CommUtil getInstance(){if(commUtil==null){commUtil = new CommUtil();commUtil.init();}return commUtil;}public void init() {try {CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(PORT_NAME);if (portIdentifier.isCurrentlyOwned()){System.out.println("Error: Port is currently in use");}else if(portIdentifier.getPortType()==1){serialPort = (SerialPort) portIdentifier.open(PORT_NAME,1000);serialPort.setSerialPortParams(BIT_RATE,DATA_BITS,STOP_BIT,PARITY_BIT);in = serialPort.getInputStream();out = serialPort.getOutputStream();serialPort.addEventListener(this);serialPort.notifyOnDataAvailable(true);}else{System.out.println("Error: Only serial ports are handled by this example.");}} catch (Exception e) {e.printStackTrace();}}public void send(String message){try {byte[] bytes = hexStrToByteArray(message);out.write(bytes);Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}}public void serialEvent(SerialPortEvent event) {switch (event.getEventType()){case SerialPortEvent.DATA_AVAILABLE:receive();break;}}public String receive(){byte[] buffer = new byte[128];int data;String result = null;try{int len = 0;while ( ( data = in.read()) > -1 ){buffer[len++] = (byte) data;}byte[] copyValue = new byte[len];System.arraycopy(buffer,0,copyValue,0,len);result = ByteArrayToString(copyValue);}catch ( IOException e ){e.printStackTrace();}return result;}public void close(){try {in.close();out.close();serialPort.notifyOnDataAvailable(false);serialPort.removeEventListener();serialPort.close();} catch (IOException e) {e.printStackTrace();}}//16进制转byte数组public byte[] hexStrToByteArray(String str) {if (str == null) {return null;}if (str.length() == 0) {return new byte[0];}byte[] byteArray = new byte[str.length() / 2];for (int i = 0; i < byteArray.length; i++) {String subStr = str.substring(2 * i, 2 * i + 2);byteArray[i] = ((byte) Integer.parseInt(subStr, 16));}return byteArray;}public String ByteArrayToString(byte[] by) {String str = "";for (int i = 0; i < by.length; i++) {String hex = Integer.toHexString(by[i] & 0xFF);if (hex.length() == 1) {hex = "0" + hex;}str += hex.toUpperCase();}return str;}public static void main ( String[] args ){CommUtil commUtil = CommUtil.getInstance();commUtil.send("8101060108080302FF");commUtil.send("8101060108080301FF");}
}附上官方参考文档地址:[串口通信示例](http://rxtx.qbang.org/wiki/index.php/Examples)
隔了几年再看这个代码,发现几个不太好的地方,本来想改的,但是没设备,就算了。
打算改的一个就是初始化那部分,单例对象一次创建,那么初始化感觉没必要写成非静态的,比较好的处理方法是静态块。
这一整个程序在当初的设计就是设计实验,所以定位就是单线程使用,所以不会出现问题,如果需要加上多线程,需要相应的做更改。