背景介绍:

  由于第一次用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. 【洛谷】P3537 [POI2012]SZA-Cloakroom

    题目描述  有n件物品,每件物品有三个属性a[i], b[i], c[i] (a[i]<b[i]).  再给出q个询问,每个询问由非负整数m, k, s组成,问是否能够选出某些物品使得:  1. ...

  2. 常用spaceclaim脚本

    #创建一个长方体,通过两点来确定一个立方体 #MM表示的是以毫米作为单位 #返回的是一个BlockBody的对象 #本函数还有第三个参数可选,分别代表增加材料,切除材料等等 #默认值为增加材料 注:第 ...

  3. CFD计算过程发散诸多原因分析【转载】

    转载自: http://blog.sina.com.cn/s/blog_5fdfa7e601010rkx.html 今天探讨引起CFD计算过程中发散的一些原因.cfd计算是将描述物理问题的偏微分方程转 ...

  4. 实现一个兼容eleUI form表单的多选组件

    本质上是实现了一个eleUI select组件中的创建条目功能的组件,仅仅是将dropdown的选择框变成了label形式.支持eleUI的form表单校验,同时组件也提供了组件内自定义校验的方法.常 ...

  5. CentOS下载与服务器版安装(VMware)

    1. 下载 首先需要选择一个版本,因为华为云最新只提供了CentOS 7.6,所以要选择CentOS 7版本的. 官网只提供了最新的版本,而且服务器在国外,下载速度贼慢. 不过官方提供了分布在各个地区 ...

  6. 记Oracle中regexp_substr的一次调优(速度提高95.5%)

    项目中需要做一个船舶代理费的功能,针对代理的船进行收费,那么该功能的第一步便是选择进行代理费用信息的录入,在进行船舶选择的时候,发现加载相关船舶信息十分的慢,其主要在sql语句的执行,因为测试的时候数 ...

  7. [转载]运行中的DLL自升级

      最近手头有个需求:dll需要注入到某个进程常驻,该dll具备自我升级能力,当发现新的可用版本时,立即Free自己,加载新的.下面是一个实现方案: 开启一个监听线程,从网络上拉新的可用版本,下载放到 ...

  8. SQLServer 临时表的使用

    临时表在Sqlserver数据库中,是非常重要的,下面就详细介绍SQL数据库中临时表的特点及其使用,仅供参考. 临时表与永久表相似,但临时表存储在tempdb中,当不再使用时会自动删除.临时表有两种类 ...

  9. CentOS 7 yum Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile

    yum install nginx发生的错误 yum install nginx Loaded plugins: fastestmirror, ovl Loading mirror speeds fr ...

  10. ArrayAdapter和ListView

    利用ArrayAdapter向ListView中添加数据 <?xml version="1.0" encoding="utf-8"?> <Li ...