背景介绍:

  由于第一次用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. Nginx:fastcgi_param详解

    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;#脚本文件请求的路径 fastcgi_param QUERY_STRI ...

  2. 根据字符串从资源中取出对应的资源ResourceManager.GetObject

    ResourceManager.GetObject 根据名称从资源中取出资源 需要:我有25张 五笔图片名称是a b c d ...y 这样组成的 每张图片名字只有一个名字 我想通过字符串a取出a这张 ...

  3. Ubuntu16.04启动tomcat缓慢问题之解决方案

    问题信息: -May- ::] org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom Creation of Secur ...

  4. java 静态数组 和非静态数组的区别

    区别:1,数组创建时间不同,静态的是一定义出来就创建了,动态的则是定义时不创建,使用时才创建,一定意义上节省了内存空间.2,使用场景不同,一开始就知道数组长度的使用静态初始化,反之,动态初始化. 静态 ...

  5. python matplotlib生成图形

    y=2x+3 import matplotlib.pyplot as plt#约定俗成的写法plt #首先定义两个函数(正弦&余弦) import numpy as np #plt.figur ...

  6. 如何写一个webService接口

    第一次写接口的时候,感觉太过笼统,压根不知道接口是个什么东东,,后来自己也查了好多资料,才发现其实接口可以就认为是一个方法,自己多写几种以后就会发现挺简单的,自己整理了一下资料,纯属增强自己的记忆,也 ...

  7. python gdal ogr osgeo

  8. 搭建Nuxt项目(搭配Element UI、axios)

    使用Nuxt Nuxt.js文档:https://zh.nuxtjs.org/guide/ 开始 初始化Nuxt项目 npx create-nuxt-app <项目名> // or yar ...

  9. centos下如何开放某个端口?

    命令如下: firewall-cmd --permanent --add-port=/tcp (开放22端口) firewall-cmd --reload

  10. AndoridSQLite数据库开发基础教程(4)

    AndoridSQLite数据库开发基础教程(4) 安装SQLiteManager 以下是SQLiteManager的安装步骤: (1)双击下载的.exe文件,弹出SQLiteManager Setu ...