最终效果如下图:
1、把rxtxParallel.dll、rxtxSerial.dll拷贝到:C:\WINDOWS\system32下。
2、RXTXcomm.jar 添加到项目类库中。
package serialPort;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
/**串口服务类,提供打开、关闭串口,读取、发送串口数据等服务
*/
public class SerialTool {
private static SerialTool serialTool = null;
static {
//在该类被ClassLoader加载时就初始化一个SerialTool对象
if (serialTool == null) {
serialTool = new SerialTool();
}
}
//私有化SerialTool类的构造方法,不允许其他类生成SerialTool对象
private SerialTool() {}
/**
* 获取提供服务的SerialTool对象
* @return serialTool
*/
public static SerialTool getSerialTool() {
if (serialTool == null) {
serialTool = new SerialTool();
}
return serialTool;
}
/**
* 查找所有可用端口
* @return 可用端口名称列表
*/
public static final List<String> findPort() {
//获得当前所有可用串口
@SuppressWarnings("unchecked")
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
List<String> portNameList = new ArrayList<>();
//将可用串口名添加到List并返回该List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
/**
* 打开串口
* @param portName 端口名称
* @param baudrate 波特率
* @return 串口对象
* @throws UnsupportedCommOperationException
* @throws PortInUseException
* @throws NoSuchPortException
*/
public static final SerialPort openPort(String portName, int baudrate) throws UnsupportedCommOperationException, PortInUseException, NoSuchPortException {
//通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
//打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 2000);
//判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
//设置一下串口的波特率等参数
serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
return serialPort;
}
return null;
}
/**
* 关闭串口
* @param serialport 待关闭的串口对象
*/
public static void closePort(SerialPort serialPort) {
if (serialPort != null) {
serialPort.close();
serialPort = null;
}
}
/**
* 往串口发送数据
* @param serialPort 串口对象
* @param order 待发送数据
* @throws IOException
*/
public static void sendToPort(SerialPort serialPort, byte[] order) throws IOException {
OutputStream out = null;
out = serialPort.getOutputStream();
out.write(order);
out.flush();
out.close();
}
/**
* 从串口读取数据
* @param serialPort 当前已建立连接的SerialPort对象
* @return 读取到的数据
* @throws IOException
*/
public static byte[] readFromPort(SerialPort serialPort) throws IOException {
InputStream in = null;
byte[] bytes = null;
try {
in = serialPort.getInputStream();
int bufflenth = in.available(); //获取buffer里的数据长度
while (bufflenth != 0) {
bytes = new byte[bufflenth]; //初始化byte数组为buffer中数据的长度
in.read(bytes);
bufflenth = in.available();
}
} catch (IOException e) {
throw e;
} finally {
if (in != null) {
in.close();
in = null;
}
}
return bytes;
}
/**添加监听器
* @param port 串口对象
* @param listener 串口监听器
* @throws TooManyListenersException
*/
public static void addListener(SerialPort port, SerialPortEventListener listener) throws TooManyListenersException {
//给串口添加监听器
port.addEventListener(listener);
//设置当有数据到达时唤醒监听接收线程
port.notifyOnDataAvailable(true);
//设置当通信中断时唤醒中断线程
port.notifyOnBreakInterrupt(true);
}
}
package serialPort;
import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TooManyListenersException;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
/**
* 监测数据显示类
* @author Zhong
*
*/
public class SerialView extends JFrame {
/**
*/
private static final long serialVersionUID = 1L;
//设置window的icon
Toolkit toolKit = getToolkit();
Image icon = toolKit.getImage(SerialView.class.getResource("computer.png"));
DateTimeFormatter df= DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss.SSS");
private JComboBox<String> commChoice;
private JComboBox<String> bpsChoice;
private JButton openSerialButton;
private JButton sendButton;
private JTextArea sendArea;
private JTextArea receiveArea;
private JButton closeSerialButton;
private List<String> commList = null; //保存可用端口号
private SerialPort serialPort = null; //保存串口对象
/**类的构造方法
* @param client
*/
public SerialView() {
init();
TimerTask task = new TimerTask() {
@Override
public void run() {
commList = SerialTool.findPort(); //程序初始化时就扫描一次有效串口
//检查是否有可用串口,有则加入选项中
if (commList == null || commList.size()<1) {
JOptionPane.showMessageDialog(null, "没有搜索到有效串口!", "错误", JOptionPane.INFORMATION_MESSAGE);
}else{
commChoice.removeAllItems();
for (String s : commList) {
commChoice.addItem(s);
}
}
}
};
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, 0, 10000);
listen();
}
/**
*/
private void listen(){
//打开串口连接
openSerialButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//获取串口名称
String commName = (String) commChoice.getSelectedItem();
//获取波特率
String bpsStr = (String) bpsChoice.getSelectedItem();
//检查串口名称是否获取正确
if (commName == null || commName.equals("")) {
JOptionPane.showMessageDialog(null, "没有搜索到有效串口!", "错误", JOptionPane.INFORMATION_MESSAGE);
}else {
//检查波特率是否获取正确
if (bpsStr == null || bpsStr.equals("")) {
JOptionPane.showMessageDialog(null, "波特率获取错误!", "错误", JOptionPane.INFORMATION_MESSAGE);
}else {
//串口名、波特率均获取正确时
int bps = Integer.parseInt(bpsStr);
try {
//获取指定端口名及波特率的串口对象
serialPort = SerialTool.openPort(commName, bps);
SerialTool.addListener(serialPort, new SerialListener());
if(serialPort==null) return;
//在该串口对象上添加监听器
closeSerialButton.setEnabled(true);
sendButton.setEnabled(true);
openSerialButton.setEnabled(false);
String time=df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()),ZoneId.of("Asia/Shanghai")));
receiveArea.append(time+" ["+serialPort.getName().split("/")[3]+"] : "+" 连接成功..."+"\r\n");
receiveArea.setCaretPosition(receiveArea.getText().length());
} catch (UnsupportedCommOperationException | PortInUseException | NoSuchPortException | TooManyListenersException e1) {
e1.printStackTrace();
}
}
}
}
});
//发送数据
sendButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(!sendButton.isEnabled())return;
String message= sendArea.getText();
//"FE0400030001D5C5"
try {
SerialTool.sendToPort(serialPort, hex2byte(message));
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
//关闭串口连接
closeSerialButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(!closeSerialButton.isEnabled())return;
SerialTool.closePort(serialPort);
String time=df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()),ZoneId.of("Asia/Shanghai")));
receiveArea.append(time+" ["+serialPort.getName().split("/")[3]+"] : "+" 断开连接"+"\r\n");
receiveArea.setCaretPosition(receiveArea.getText().length());
openSerialButton.setEnabled(true);
closeSerialButton.setEnabled(false);
sendButton.setEnabled(false);
}
});
}
/**
* 主菜单窗口显示;
* 添加JLabel、按钮、下拉条及相关事件监听;
*/
private void init() {
this.setBounds(WellcomView.LOC_X, WellcomView.LOC_Y, WellcomView.WIDTH, WellcomView.HEIGHT);
this.setTitle("串口调试");
this.setIconImage(icon);
this.setBackground(Color.gray);
this.setLayout(null);
Font font =new Font("微软雅黑", Font.BOLD, 16);
receiveArea=new JTextArea(18, 30);
receiveArea.setEditable(false);
JScrollPane receiveScroll = new JScrollPane(receiveArea);
receiveScroll.setBorder(new TitledBorder("接收区"));
//滚动条自动出现 FE0400030001D5C5
receiveScroll.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
receiveScroll.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
receiveScroll.setBounds(52, 20, 680,340);
this.add(receiveScroll);
JLabel chuankou=new JLabel(" 串口选择: ");
chuankou.setFont(font);
chuankou.setBounds(50, 380, 100,50);
this.add(chuankou);
JLabel botelv=new JLabel(" 波 特 率: ");
botelv.setFont(font);
botelv.setBounds(290, 380, 100,50);
this.add(botelv);
//添加串口选择选项
commChoice = new JComboBox<String>(); //串口选择(下拉框)
commChoice.setBounds(145, 390, 100, 30);
this.add(commChoice);
//添加波特率选项
bpsChoice = new JComboBox<String>(); //波特率选择
bpsChoice.setBounds(380, 390, 100, 30);
bpsChoice.addItem("1500");
bpsChoice.addItem("2400");
bpsChoice.addItem("4800");
bpsChoice.addItem("9600");
bpsChoice.addItem("14400");
bpsChoice.addItem("19500");
bpsChoice.addItem("115500");
this.add(bpsChoice);
//添加打开串口按钮
openSerialButton = new JButton("连接");
openSerialButton.setBounds(540, 390, 80, 30);
openSerialButton.setFont(font);
openSerialButton.setForeground(Color.darkGray);
this.add(openSerialButton);
//添加关闭串口按钮
closeSerialButton = new JButton("关闭");
closeSerialButton.setEnabled(false);
closeSerialButton.setBounds(650, 390, 80, 30);
closeSerialButton.setFont(font);
closeSerialButton.setForeground(Color.darkGray);
this.add(closeSerialButton);
sendArea=new JTextArea(30,20);
JScrollPane sendScroll = new JScrollPane(sendArea);
sendScroll.setBorder(new TitledBorder("发送区"));
//滚动条自动出现
sendScroll.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
sendScroll.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
sendScroll.setBounds(52, 450, 500,100);
this.add(sendScroll);
sendButton = new JButton("发 送");
sendButton.setBounds(610, 520, 120, 30);
sendButton.setFont(font);
sendButton.setForeground(Color.darkGray);
sendButton.setEnabled(false);
this.add(sendButton);
this.setResizable(false); //窗口大小不可更改
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
/**字符串转16进制
* @param hex
* @return
*/
private byte[] hex2byte(String hex) {
String digital = "0123456789ABCDEF";
String hex1 = hex.replace(" ", "");
char[] hex2char = hex1.toCharArray();
byte[] bytes = new byte[hex1.length() / 2];
byte temp;
for (int p = 0; p < bytes.length; p++) {
temp = (byte) (digital.indexOf(hex2char[2 * p]) * 16);
temp += digital.indexOf(hex2char[2 * p + 1]);
bytes[p] = (byte) (temp & 0xff);
}
return bytes;
}
/**字节数组转16进制
* @param b
* @return
*/
private String printHexString(byte[] b) {
StringBuffer sbf=new StringBuffer();
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sbf.append(hex.toUpperCase()+" ");
}
return sbf.toString().trim();
}
/**
* 以内部类形式创建一个串口监听类
* @author zhong
*/
class SerialListener implements SerialPortEventListener {
/**
* 处理监控到的串口事件
*/
public void serialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()) {
case SerialPortEvent.BI: // 10 通讯中断
JOptionPane.showMessageDialog(null, "与串口设备通讯中断", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.OE: // 7 溢位(溢出)错误
break;
case SerialPortEvent.FE: // 9 帧错误
break;
case SerialPortEvent.PE: // 8 奇偶校验错误
break;
case SerialPortEvent.CD: // 6 载波检测
break;
case SerialPortEvent.CTS: // 3 清除待发送数据
break;
case SerialPortEvent.DSR: // 4 待发送数据准备好了
break;
case SerialPortEvent.RI: // 5 振铃指示
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 输出缓冲区已清空
break;
case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用数据
String time=df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()),ZoneId.of("Asia/Shanghai")));
byte[] data;//FE0400030001D5C5
try {
data = SerialTool.readFromPort(serialPort);
receiveArea.append(time+" ["+serialPort.getName().split("/")[3]+"] : "+ printHexString(data)+"\r\n");
receiveArea.setCaretPosition(receiveArea.getText().length());
} catch (IOException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
}
package serialPort;
import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* @author bh
* 如果运行过程中抛出 java.lang.UnsatisfiedLinkError 错误,
* 请将rxtx解压包中的 rxtxParallel.dll,rxtxSerial.dll 这两个文件复制到 C:\Windows\System32 目录下即可解决该错误。
*/
public class WellcomView {
/** 程序界面宽度*/
public static final int WIDTH = 800;
/** 程序界面高度*/
public static final int HEIGHT = 620;
/** 程序界面出现位置(横坐标) */
public static final int LOC_X = 200;
/** 程序界面出现位置(纵坐标)*/
public static final int LOC_Y = 70;
private JFrame jFrame;
/**主方法
* @param args //
*/
public static void main(String[] args) {
new WellcomView();
}
public WellcomView() {
init();
listen();
}
/**
*/
private void listen() {
//添加键盘监听器
jFrame.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_ENTER) { //当监听到用户敲击键盘enter键后执行下面的操作
jFrame.setVisible(false); //隐去欢迎界面
new SerialView(); //主界面类(显示监控数据主面板)
}
}
});
}
/**
* 显示主界面
*/
private void init() {
jFrame=new JFrame("串口调试");
jFrame.setBounds(LOC_X, LOC_Y, WIDTH, HEIGHT); //设定程序在桌面出现的位置
jFrame.setLayout(null);
//设置window的icon(这里我自定义了一下Windows窗口的icon图标,因为实在觉得哪个小咖啡图标不好看 = =)
Toolkit toolKit = jFrame.getToolkit();
Image icon = toolKit.getImage(WellcomView.class.getResource("computer.png"));
jFrame.setIconImage(icon);
jFrame.setBackground(Color.white); //设置背景色
JLabel huanyin=new JLabel("欢迎使用串口调试工具");
huanyin.setBounds(170, 80,600,50);
huanyin.setFont(new Font("微软雅黑", Font.BOLD, 40));
jFrame.add(huanyin);
JLabel banben=new JLabel("Version:1.0 Powered By:cyq");
banben.setBounds(180, 390,500,50);
banben.setFont(new Font("微软雅黑", Font.ITALIC, 26));
jFrame.add(banben);
JLabel enter=new JLabel("————点击Enter键进入主界面————");
enter.setBounds(100, 480,600,50);
enter.setFont(new Font("微软雅黑", Font.BOLD, 30));
jFrame.add(enter);
jFrame.setResizable(false); //窗口大小不可更改
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true); //显示窗口
}
}
无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。