Java串口通信 RXTX 解决过程
背景介绍:
由于第一次用Java与硬件通信,网上查了许多资料,在这进行整理,便于以后学习。本人串口测试是USB串口设备连接电脑,在设备管理器中找到端口名称(也可以通过一些虚拟串口工具模拟)。
下面主要简述获取串口消息返回值的一些问题,在最下面将会附上完整代码。
准备工作:
RXTX包:mfz-rxtx-2.2-20081207-win-x64.zip,解压,RXTXcomm.jar加入项目依赖库里,rxtxParallel.dll和rxtxSerial.dll放入jdk的bin目录下(我使用的jdk1.8)
RXTX工具类编写:
编写基础方法:获取可用端口名,开启端口,发送命令,接受命令,关闭端口
import gnu.io.*; import javax.sound.midi.SoundbankResource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException; import static com.why.rxtx.utill.HexadecimalUtil.get16NumAdd0;
import static com.why.rxtx.utill.HexadecimalUtil.hexStringToByteArray;
import static com.why.rxtx.utill.OrderUtil.retuenLogOrder;
import static com.why.rxtx.utill.OrderUtil.send; /**
* 使用rxtx连接串口工具类
*/
public class RXTXUtil { private static final String DEMONAME = "串口测试"; /**
* 检测系统中可用的端口
*/
private CommPortIdentifier portId;
/**
* 获得系统可用的端口名称列表
*/
private static Enumeration<CommPortIdentifier> portList;
/**
* 输入流
*/
private static InputStream inputStream;
/**
* RS-232的串行口
*/
private static SerialPort serialPort;
/**
* 返回结果
*/
private static String res=null; /**
* 获得系统可用的端口名称列表
* @return 可用端口名称列表
*/
@SuppressWarnings("unchecked")
public static void getSystemPort(){
List<String> systemPorts = new ArrayList<>();
//获得系统可用的端口
portList = CommPortIdentifier.getPortIdentifiers();
while(portList.hasMoreElements()) {
String portName = portList.nextElement().getName();//获得端口的名字
systemPorts.add(portName);
} } /**
* 开启串口
* @param serialPortName 串口名称
* @param baudRate 波特率
* @return 串口对象
*/
public static void openSerialPort(String serialPortName,int baudRate) {
try {
//通过端口名称得到端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(serialPortName);
//打开端口,(自定义名字,打开超时时间)
CommPort commPort = portIdentifier.open(serialPortName, 5000);
//判断是不是串口
if (commPort instanceof SerialPort) {
serialPort = (SerialPort) commPort;
//设置串口参数(波特率,数据位8,停止位1,校验位无)
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
// System.out.println("开启串口成功,串口名称:"+serialPortName);
}else {
//是其他类型的端口
throw new NoSuchPortException();
}
} catch (NoSuchPortException e) {
e.printStackTrace();
} catch (PortInUseException e) {
e.printStackTrace();
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
} } /**
* 向串口发送数据
* @param order 发送的命令
*/
public static void sendData( String order) {
//16进制表示的字符串转换为字节数组
byte[] data =hexStringToByteArray(order);
OutputStream os = null;
try {
os = serialPort.getOutputStream();//获得串口的输出流
os.write(data);
os.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流操作
try {
if (os != null) {
os.close();
os = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 从串口读取数据
* @return 读取的数据
*/
public static String readData() {
//保存串口返回信息
StringBuffer res=new StringBuffer(40);
InputStream is = null;
byte[] bytes = null;
try {
is = serialPort.getInputStream();//获得串口的输入流
int bufflenth = is.available();//获得数据长度
while (bufflenth != 0) {
bytes = new byte[bufflenth];//初始化byte数组
is.read(bytes);
bufflenth = is.available();
}
if(bytes!=null) {
for (int i = 0; i < bytes.length; i++) {
//转换成16进制数(FF)
res.append(get16NumAdd0((bytes[i]&0xff)+"",2));
}
}
System.out.println("res: "+res.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
is = null;
}
} catch(IOException e) {
e.printStackTrace();
}
} return res.toString();
} /**
* 关闭串口
*
*/
public static void closeSerialPort() {
if(serialPort != null) {
serialPort.close();
//System.out.println("关闭了串口:"+serialPort.getName());
serialPort = null;
}
} }
在一些基本方法写完后,便得开始测试串口通信了
于是就开始编写测试代码了
/**
* 串口命令执行
* @param order 命令
* @param portName 端口名
* @param baudRate 波特率
* @return
* @throws UnsupportedEncodingException
*/
public synchronized static String executeOrder(String order,String portName,int baudRate) {
String str="";
if (serialPort==null) {
openSerialPort(portName, baudRate);
}
//发送消息
sendData(order);
//接收消息
String str=readData(); return res;
}
很遗憾上面代码输入命令,端口号等等后,返回结果一直是null, 突然间发现和以前写读写io流还是有一定区别的。原因在于,io流是有文件在那可以随时读写,而串口需要等待返回信息。
然后便开始改写代码。通过上网查询,发现有一个端口监听器,于是有了下面代码
/**
* 串口命令执行
* @param order 命令
* @param portName 端口名
* @param baudRate 波特率
* @return
* @throws UnsupportedEncodingException
*/
public synchronized static String executeOrder(String order,String portName,int baudRate) {
String str="";
if (serialPort==null) {
openSerialPort(portName, baudRate);
}
//发送消息
sendData(order); //设置监听
setListenerToSerialPort( new SerialPortEventListener(){
@Override
public void serialEvent(SerialPortEvent serialPortEvent) {
if(serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {//数据通知
String str=readData();
res=returnCheck(order,str);
}
}
}); return res;
} /**
* 给串口设置监听
* @param listener
*/
public static void setListenerToSerialPort( SerialPortEventListener listener) {
try {
//给串口添加事件监听
serialPort.addEventListener(listener);
} catch (TooManyListenersException e) {
e.printStackTrace();
}
serialPort.notifyOnDataAvailable(true);//串口有数据监听
serialPort.notifyOnBreakInterrupt(true);//中断事件监听 }
经过再次执行,发现返回的依旧是null,于是在监听器里打印串口消息输出,发现是先返回 null,然后再打印串口消息。于是得出结论监听器是异步的,但是我们需要返回值去判断是否执行成功,
显然这不符合要求。于是再做修改,需要把这异步返回值接收到。于是有了下面两种方式:
第一种:放弃监听器,使用循环去读取串口返回信息,通过循环次数设置最大请求时间
/**
* 串口命令执行
* @param order 命令
* @param portName 端口名
* @param baudRate 波特率
* @return
* @throws UnsupportedEncodingException
*/
public synchronized static String executeOrder(String order,String portName,int baudRate) {
String str="";
if (serialPort==null) {
openSerialPort(portName, baudRate);
}
//发送消息
sendData(order);
//代替监听
for (int i = 0; i <1000; i++) {
str=readData();
str=returnCheck(order,str);
if (str!=null){
return str;
}else {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }
第二种:使用监听,通过循环判断是否有串口消息返回,与第一种本质还是相同的
/**
* 串口命令执行
* @param order 命令
* @param portName 端口名
* @param baudRate 波特率
* @return
* @throws UnsupportedEncodingException
*/
public synchronized static String executeOrder(String order,String portName,int baudRate) {
String str="";
if (serialPort==null) {
openSerialPort(portName, baudRate);
}
//发送消息
sendData(order); //设置监听
setListenerToSerialPort( new SerialPortEventListener(){
@Override
public void serialEvent(SerialPortEvent serialPortEvent) {
if(serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {//数据通知
String str=readData();
res=returnCheck(order,str);
}
}
}); //监听是异步请求,如果要获取监听里的内容做返回值,
// 可以通过循环去延迟来获取返回值,
// 设置最大延迟防止死循环
long startTime = System.currentTimeMillis();
while (res==null){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
if ((endTime-startTime)/1000.0>20){//最长20s返回
return res;
}
}
return res;
}
到此串口消息通信就已解决了,下面附上完整代码,包括一些使用到的进制转换方法。
package com.along.outboundmanage.utill; import gnu.io.*; import javax.sound.midi.SoundbankResource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException; import static com.why.rxtx.utill.HexadecimalUtil.get16NumAdd0;
import static com.why.rxtx.utill.HexadecimalUtil.hexStringToByteArray; /**
* 使用rxtx连接串口工具类
*/
public class RXTXUtil { private static final String DEMONAME = "串口测试"; /**
* 检测系统中可用的端口
*/
private CommPortIdentifier portId;
/**
* 获得系统可用的端口名称列表
*/
private static Enumeration<CommPortIdentifier> portList;
/**
* 输入流
*/
private static InputStream inputStream;
/**
* RS-232的串行口
*/
private static SerialPort serialPort;
/**
* 返回结果
*/
private static String res=null;
/**
* 串口命令执行
* @param order 命令
* @param portName 端口名
* @param baudRate 波特率
* @return
* @throws UnsupportedEncodingException
*/
public synchronized static String executeOrder(String order,String portName,int baudRate) {
String str="";
if (serialPort==null) {
openSerialPort(portName, baudRate);
}
//发送消息
sendData(order);
//代替监听
/*for (int i = 0; i <1000; i++) {
str=readData(); if (str!=null){
return str;
}else {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}*/
//设置监听
setListenerToSerialPort( new SerialPortEventListener(){
@Override
public void serialEvent(SerialPortEvent serialPortEvent) {
if(serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {//数据通知
String str=readData();
res=returnCheck(order,str);
}
}
}); //监听是异步请求,如果要获取监听里的内容做返回值,
// 可以通过循环去延迟来获取返回值,
// 设置最大延迟防止死循环
long startTime = System.currentTimeMillis();
while (res==null){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
if ((endTime-startTime)/1000.0>20){//最长20s返回
return res;
}
}
return res;
} /**
* 获得系统可用的端口名称列表
* @return 可用端口名称列表
*/
@SuppressWarnings("unchecked")
public static void getSystemPort(){
List<String> systemPorts = new ArrayList<>();
//获得系统可用的端口
portList = CommPortIdentifier.getPortIdentifiers();
while(portList.hasMoreElements()) {
String portName = portList.nextElement().getName();//获得端口的名字
systemPorts.add(portName);
} } /**
* 开启串口
* @param serialPortName 串口名称
* @param baudRate 波特率
* @return 串口对象
*/
public static void openSerialPort(String serialPortName,int baudRate) {
try {
//通过端口名称得到端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(serialPortName);
//打开端口,(自定义名字,打开超时时间)
CommPort commPort = portIdentifier.open(serialPortName, 5000);
//判断是不是串口
if (commPort instanceof SerialPort) {
serialPort = (SerialPort) commPort;
//设置串口参数(波特率,数据位8,停止位1,校验位无)
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
// System.out.println("开启串口成功,串口名称:"+serialPortName);
}else {
//是其他类型的端口
throw new NoSuchPortException();
}
} catch (NoSuchPortException e) {
e.printStackTrace();
} catch (PortInUseException e) {
e.printStackTrace();
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
} } /**
* 向串口发送数据
* @param order 发送的命令
*/
public static void sendData( String order) {
//16进制表示的字符串转换为字节数组
byte[] data =hexStringToByteArray(order);
OutputStream os = null;
try {
os = serialPort.getOutputStream();//获得串口的输出流
os.write(data);
os.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流操作
try {
if (os != null) {
os.close();
os = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 从串口读取数据
* @return 读取的数据
*/
public static String readData() {
//保存串口返回信息
StringBuffer res=new StringBuffer(40);
InputStream is = null;
byte[] bytes = null;
try {
is = serialPort.getInputStream();//获得串口的输入流
int bufflenth = is.available();//获得数据长度
while (bufflenth != 0) {
bytes = new byte[bufflenth];//初始化byte数组
is.read(bytes);
bufflenth = is.available();
}
if(bytes!=null) {
for (int i = 0; i < bytes.length; i++) {
//转换成16进制数(FF)
res.append(get16NumAdd0((bytes[i]&0xff)+"",2));
}
}
System.out.println("res: "+res.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
is = null;
}
} catch(IOException e) {
e.printStackTrace();
}
} return res.toString();
} /**
* 关闭串口
*
*/
public static void closeSerialPort() {
if(serialPort != null) {
serialPort.close();
//System.out.println("关闭了串口:"+serialPort.getName());
serialPort = null;
}
} /**
* 给串口设置监听
* @param listener
*/
public static void setListenerToSerialPort( SerialPortEventListener listener) {
try {
//给串口添加事件监听
serialPort.addEventListener(listener);
} catch (TooManyListenersException e) {
e.printStackTrace();
}
serialPort.notifyOnDataAvailable(true);//串口有数据监听
serialPort.notifyOnBreakInterrupt(true);//中断事件监听 }
}
import org.thymeleaf.util.StringUtils; /**
* 进制转换
* */
public class HexadecimalUtil { /**
* 十六进制转十进制
*
* @param num
* @return
*/
public static Integer get10HexNum(String num) {
if (num.contains("0X")) {
num = num.replace("0X", "");
}
return Integer.parseInt(num.substring(0), 16);
} /**
* 十进制转十六进制
*
* @param num
* @return
*/
public static String get16Num(Object num) { return Integer.toHexString(Integer.parseInt(num + ""));
} /**
* 十进制转十六进制,设置长度,不足补0
*
* @param num
* @return
*/
public static String get16NumAdd0(String num, int len) {
String str = Integer.toHexString(Integer.parseInt(num)).toUpperCase();
String res = "";
if (len >= str.length()) {
res = StringUtils.repeat("0", (len - str.length())) + str;
} else {
return str;
}
return res;
} //num & 0xff
public static int low8(Object num) {
return Integer.parseInt(num + "") & 0xff;
} //获取高四位
public static int getHeight4(byte data) {
int height;
height = ((data & 0xf0) >> 4);
return height;
} /**
* 16进制表示的字符串转换为字节数组
*
* @param hexString 16进制表示的字符串
* @return byte[] 字节数组
*/
public static byte[] hexStringToByteArray(String hexString) {
hexString = hexString.replaceAll(" ", "");
int len = hexString.length();
byte[] bytes = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
// 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节
bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
.digit(hexString.charAt(i + 1), 16));
}
return bytes;
}
}
Java串口通信 RXTX 解决过程的更多相关文章
- java串口通信丢包
java串口通信丢包问题 前段时间公司要求做一个java应用和pos串口通信的工具,调试好了好久每次都是只能接收到一包数据后续的数据都丢失了. 经过修改读写的流的缓存大小亲测都正常代码如下: seri ...
- java下的串口通信-RXTX
关于java实现的串口通信,使用的是开源项目RXTX,之前sun公司也有JCL项目,不过已经很久没有更新了,RXTX项目地址:点击打开,但是两个项目的API用法是一样的,只是导入的包不一样而已.简单的 ...
- Java串口通信--------基于RXTX (附带资源地址)
最近帮老师做了一个小项目,一个牧场公司想用传感器收集一些环境信息,记录到数据库里去,然后加以分析查看.这里面和传感器通信用到了串口通信,我也是接触了一下,把用到的东西分享出来. 准备工作: RXTX: ...
- Java串口通信具体解释
序言 说到开源,恐怕非常少有人不挑大指称赞.学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜.然而开源软 ...
- Java串口通信详细解释
前言 说到开源.恐怕非常少有人不挑大指称赞. 学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜. 然而开 ...
- Java 串口通信
在Windows系统下,用Java开发串口通信相关的程序时,需要用到几个文件. (1)win32com.dll 要放在jdk\jre\bin目录下. (2)comm.jar 和javax.comm.p ...
- java 串口通信 代码
下面是我自己实现的串口接收的类,串口发送比较简单,就直接发送就可以了.下面的这个类可以直接使用. package com.boomdts.weather_monitor.util; import ja ...
- 【Java】基于RXTX的Java串口通信
本篇内容参考转载自https://blog.csdn.net/kong_gu_you_lan/article/details/80589859 环境搭建 下载地址:http://fizzed.com/ ...
- java 串口通信实现流程
1.下载64位rxtx for java 链接:http://fizzed.com/oss/rxtx-for-java 2.下载下来的包解压后按照说明放到JAVA_HOME即JAVA的安装路径下面去 ...
随机推荐
- 如何在Fluent使用非牛顿流体【转载】
转载自:http://blog.sciencenet.cn/blog-267817-747504.html 1.非牛顿流体:剪应力与剪切应变率之间满足线性关系的流体称为牛顿流体,而把不满足线性关系的流 ...
- 批量更新表注释 mysql
-- 生成更新语句 SELECT CONCAT( 'ALTER TABLE ', T2.table_name, ' COMMENT ''', T1.TABLE_COMMENT, ''';' ) sql ...
- 【TensorFlow官方文档】MNIST机器学习入门
MNIST是一个入门级的计算机视觉数据集,它包含各种手写数字图片:它也包含每一张图片对应的标签,告诉我们这个是数字几.比如,下面这四张图片的标签分别是5,0,4,1. 从一个很简单的数学模型开始:训练 ...
- 2018-2019-2 网络对抗技术 20165322 Exp7 网络欺诈防范
2018-2019-2 网络对抗技术 20165322 Exp7 网络欺诈防范 目录 实验原理 实验内容与步骤 简单应用SET工具建立冒名网站 ettercap DNS spoof 结合应用两种技术, ...
- #C++初学记录(遍历)
hide handkerchief Problem Description The Children's Day has passed for some days .Has you remembere ...
- css去除图片间隙
问题如下图 把图片转换成块状元素即可 .img{ display: block; } 解决后:
- python中whl的讲解
whl 格式:这是一个压缩包,在其中包含了py文件,以及经过编译的pyd文件. 这个格式可以使文件在不具备编译环境的情况下,选择合适自己的python环境进行安装. 安装方法如下 进入命令行输入:pi ...
- 01_tf和numpy的区别
import numpy as npimport tensorflow as tf # 这里是为了演示numpy和tf的区别.np.random.seed(43) x_data = np.random ...
- [转]Myeclipse四种方式发布项目
原文链接: myeclipse四种方式发布项目
- python 设计模式之工厂模式 Factory Pattern (简单工厂模式,工厂方法模式,抽象工厂模式)
十一回了趟老家,十一前工作一大堆忙成了狗,十一回来后又积累了一大堆又 忙成了狗,今天刚好抽了一点空开始写工厂方法模式 我看了<Head First 设计模式>P109--P133 这25页 ...