准备工作
- 首先下载官方提供的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)
隔了几年再看这个代码,发现几个不太好的地方,本来想改的,但是没设备,就算了。
打算改的一个就是初始化那部分,单例对象一次创建,那么初始化感觉没必要写成非静态的,比较好的处理方法是静态块。
这一整个程序在当初的设计就是设计实验,所以定位就是单线程使用,所以不会出现问题,如果需要加上多线程,需要相应的做更改。