背景介绍:

  由于第一次用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 解决过程的更多相关文章

  1. java串口通信丢包

    java串口通信丢包问题 前段时间公司要求做一个java应用和pos串口通信的工具,调试好了好久每次都是只能接收到一包数据后续的数据都丢失了. 经过修改读写的流的缓存大小亲测都正常代码如下: seri ...

  2. java下的串口通信-RXTX

    关于java实现的串口通信,使用的是开源项目RXTX,之前sun公司也有JCL项目,不过已经很久没有更新了,RXTX项目地址:点击打开,但是两个项目的API用法是一样的,只是导入的包不一样而已.简单的 ...

  3. Java串口通信--------基于RXTX (附带资源地址)

    最近帮老师做了一个小项目,一个牧场公司想用传感器收集一些环境信息,记录到数据库里去,然后加以分析查看.这里面和传感器通信用到了串口通信,我也是接触了一下,把用到的东西分享出来. 准备工作: RXTX: ...

  4. Java串口通信具体解释

    序言 说到开源,恐怕非常少有人不挑大指称赞.学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜.然而开源软 ...

  5. Java串口通信详细解释

    前言 说到开源.恐怕非常少有人不挑大指称赞. 学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜. 然而开 ...

  6. Java 串口通信

    在Windows系统下,用Java开发串口通信相关的程序时,需要用到几个文件. (1)win32com.dll 要放在jdk\jre\bin目录下. (2)comm.jar 和javax.comm.p ...

  7. java 串口通信 代码

    下面是我自己实现的串口接收的类,串口发送比较简单,就直接发送就可以了.下面的这个类可以直接使用. package com.boomdts.weather_monitor.util; import ja ...

  8. 【Java】基于RXTX的Java串口通信

    本篇内容参考转载自https://blog.csdn.net/kong_gu_you_lan/article/details/80589859 环境搭建 下载地址:http://fizzed.com/ ...

  9. java 串口通信实现流程

    1.下载64位rxtx for java 链接:http://fizzed.com/oss/rxtx-for-java 2.下载下来的包解压后按照说明放到JAVA_HOME即JAVA的安装路径下面去 ...

随机推荐

  1. CF1207题解

    D 全排列减去坏序列 坏序列分三种,容斥一下就好了 E 比较有意思 \(A=_{i=1}^{100}\{i\},B=_{i=1}^{100}\{i\cdot 2^7\}\),所以\(A_i~xor~ ...

  2. winform如何不卡界面

    快速阅读 如何在winform程序中,让界面不再卡死. 关于委托和AsyncCallback的使用. 界面卡死的原因是因为耗时任务的计算占用了主线程,导致主界面没有办法进行其它操作,比如拖动.造成界面 ...

  3. appium 多线程还是多进程(转)

    https://www.cnblogs.com/zouzou-busy/p/11440175.html 在前面我们都是使用一个机器进行测试,在做app自动化的时候,我们要测不同的机型,也就是兼容性测试 ...

  4. html5中的fieldset/legend元素和keygen元素

    html5中的fieldset/legend元素和keygen元素 一.总结 一句话总结: fieldset/legend元素和figure和figcaption很像,只不过是作用于表单,前者表示内容 ...

  5. html5的figure/figcaption讲解及实例

    html5的figure/figcaption讲解及实例 一.总结 一句话总结: figure元素:用来包着媒体资源,比如图片图表:是一个[媒体组合元素],也就是对其他的媒体元素进行组合,比如:图像. ...

  6. docker 监控之 cadvisor

    我们可以使用多种方法监控容器的运行情况,比如EFK等,但是我们仍旧需要一个资源利用率监控系统.这个时候,google开发的cadvisor就可以帮上我们的忙了. cadvisor是google创建的用 ...

  7. Zygote启动及其作用

    目录 1.Zygote简介 2.Zygote进程如何启动 2.1 init.zygote64_32.rc文件 2.2 查看ps信息 2.3 启动 3.Zygote作用 3.1 启动system_ser ...

  8. Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [spring-mybatis.xml]: Initialization of bean failed

  9. ajax上传图片报错TypeError: 'append' called on an object that does not implement interface Fo

    使用FormData时报错:TypeError: 'append' called on an object that does not implement interface FormData 解决办 ...

  10. Java项目登录报Session Error

    在web.xml文件添加下面红色代码即可,注意:添加代码后,格式化一下代码. <servlet>        <servlet-name>dwr-invoker</se ...