基于Java编写串口通信工具

最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^

一:

首先,你需要下载一个额外的支持Java串口通信操作的jar包,由于java.comm比较老了,而且不支持64位系统,这里推荐Rxtx这个jar包(32位/64位均支持)。

官方下载地址:http://fizzed.com/oss/rxtx-for-java (注:可能需要FQ才能下载)

不能FQ的童鞋,可以在这里下载:

http://xiazai.jb51.net/201612/yuanma/javamfzrxtx(jb51.net).rar(32位)

http://xiazai.jb51.net/201612/yuanma/javamfzrxtx(jb51.net).rar(64位)

二:

下载解压jar包并在 Java Build Path 下引入:

捕获

注:如果运行过程中抛出 java.lang.UnsatisfiedLinkError 错误,请将rxtx解压包中的 rxtxParallel.dll,rxtxSerial.dll 这两个文件复制到 C:\Windows\System32 目录下即可解决该错误。

三:

关于该jar包的使用,我写了一个SerialTool.java类,该类提供关于串口通信的各简单服务,代码如下(注意该类位于 serialPort 包里):

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.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;
import serialException.*;

/**
 * 串口服务类,提供打开、关闭串口,读取、发送串口数据等服务(采用单例设计模式)
 * @author zhong
 *
 */
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 ArrayList<String> findPort() {

 //获得当前所有可用串口
 Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers(); 

 ArrayList<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 SerialPortParameterFailure 设置串口参数失败
 * @throws NotASerialPort 端口指向设备不是串口类型
 * @throws NoSuchPort 没有该端口对应的串口设备
 * @throws PortInUse 端口已被占用
 */
 public static final SerialPort openPort(String portName, int baudrate) throws SerialPortParameterFailure, NotASerialPort, NoSuchPort, PortInUse {

 try {

  //通过端口名识别端口
  CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);

  //打开端口,并给端口名字和一个timeout(打开操作的超时时间)
  CommPort commPort = portIdentifier.open(portName, 2000);

  //判断是不是串口
  if (commPort instanceof SerialPort) {

  SerialPort serialPort = (SerialPort) commPort;

  try {
   //设置一下串口的波特率等参数
   serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
  } catch (UnsupportedCommOperationException e) {
   throw new SerialPortParameterFailure();
  }

  //System.out.println("Open " + portName + " sucessfully !");
  return serialPort;

  }
  else {
  //不是串口
  throw new NotASerialPort();
  }
 } catch (NoSuchPortException e1) {
  throw new NoSuchPort();
 } catch (PortInUseException e2) {
  throw new PortInUse();
 }
 }

 /**
 * 关闭串口
 * @param serialport 待关闭的串口对象
 */
 public static void closePort(SerialPort serialPort) {
 if (serialPort != null) {
  serialPort.close();
  serialPort = null;
 }
 }

 /**
 * 往串口发送数据
 * @param serialPort 串口对象
 * @param order 待发送数据
 * @throws SendDataToSerialPortFailure 向串口发送数据失败
 * @throws SerialPortOutputStreamCloseFailure 关闭串口对象的输出流出错
 */
 public static void sendToPort(SerialPort serialPort, byte[] order) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure {

 OutputStream out = null;

 try {

  out = serialPort.getOutputStream();
  out.write(order);
  out.flush();

 } catch (IOException e) {
  throw new SendDataToSerialPortFailure();
 } finally {
  try {
  if (out != null) {
   out.close();
   out = null;
  }
  } catch (IOException e) {
  throw new SerialPortOutputStreamCloseFailure();
  }
 }

 }

 /**
 * 从串口读取数据
 * @param serialPort 当前已建立连接的SerialPort对象
 * @return 读取到的数据
 * @throws ReadDataFromSerialPortFailure 从串口读取数据时出错
 * @throws SerialPortInputStreamCloseFailure 关闭串口对象输入流出错
 */
 public static byte[] readFromPort(SerialPort serialPort) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure {

 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 new ReadDataFromSerialPortFailure();
 } finally {
  try {
  if (in != null) {
   in.close();
   in = null;
  }
  } catch(IOException e) {
  throw new SerialPortInputStreamCloseFailure();
  }

 }

 return bytes;

 }

 /**
 * 添加监听器
 * @param port 串口对象
 * @param listener 串口监听器
 * @throws TooManyListeners 监听类对象过多
 */
 public static void addListener(SerialPort port, SerialPortEventListener listener) throws TooManyListeners {

 try {

  //给串口添加监听器
  port.addEventListener(listener);
  //设置当有数据到达时唤醒监听接收线程
  port.notifyOnDataAvailable(true);
  //设置当通信中断时唤醒中断线程
  port.notifyOnBreakInterrupt(true);

 } catch (TooManyListenersException e) {
  throw new TooManyListeners();
 }
 }

}

注:该类方法中 throw 的 Exception 都是我自定义的 Exception,之所以这么做是为了方便在主程序中进行相应处理,下面贴其中一个Exception出来给大家做下说明:

(注意我所有自定义的 Exception 都放在 serialException 包里)

package serialException;

public class SerialPortParameterFailure extends Exception {
 /**
 *
 */
 private static final long serialVersionUID = 1L;

 public SerialPortParameterFailure() {}

 @Override
 public String toString() {
 return "设置串口参数失败!打开串口操作未完成!";
 }

}

每个自定义的Exception类我都重写了它的 toString() 方法,便于主程序捕捉到该Exception后打印对应的错误信息

其中在serialException包里还有一个专门将接收到的Exception对象内的错误信息提取出来转换成字符串并返回的类,代码如下:

package serialException;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 负责将传入的Exception中的错误信息提取出来并转换成字符串;
 * @author zhong
 *
 */
public class ExceptionWriter {

 /**
 * 将Exception中的错误信息封装到字符串中并返回该字符串
 * @param e 包含错误的Exception
 * @return 错误信息字符串
 */
 public static String getErrorInfoFromException(Exception e) { 

  StringWriter sw = null;
  PrintWriter pw = null;

  try {
  sw = new StringWriter();
  pw = new PrintWriter(sw);
  e.printStackTrace(pw);
  return "\r\n" + sw.toString() + "\r\n"; 

  } catch (Exception e2) {
  return "出错啦!未获取到错误信息,请检查后重试!";
  } finally {
  try {
   if (pw != null) {
   pw.close();
   }
   if (sw != null) {
   sw.close();
   }
  } catch (IOException e1) {
   e1.printStackTrace();
  }
  }
 }
}

四:

主程序类的使用,Client.java里含有程序的入口地址(main方法),它的作用是显示一个欢迎界面并调用DataView.java这个类进行实际的串口数据显示。

Client.java代码如下:

package serialPort;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Label;
import java.awt.Panel;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JOptionPane;

import serialException.ExceptionWriter;

/**
 * 主程序
 * @author zhong
 *
 */
public class Client extends Frame{

 /**
 *
 */
 private static final long serialVersionUID = 1L;

 /**
 * 程序界面宽度
 */
 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;

 Color color = Color.WHITE;
 Image offScreen = null; //用于双缓冲

 //设置window的icon(这里我自定义了一下Windows窗口的icon图标,因为实在觉得哪个小咖啡图标不好看 = =)
 Toolkit toolKit = getToolkit();
 Image icon = toolKit.getImage(Client.class.getResource("computer.png"));

 //持有其他类
 DataView dataview = new DataView(this); //主界面类(显示监控数据主面板)

 /**
 * 主方法
 * @param args //
 */
 public static void main(String[] args) {
 new Client().launchFrame();
 }

 /**
 * 显示主界面
 */
 public void launchFrame() {
 this.setBounds(LOC_X, LOC_Y, WIDTH, HEIGHT); //设定程序在桌面出现的位置
 this.setTitle("CDIO工程项目"); //设置程序标题
 this.setIconImage(icon);
 this.setBackground(Color.white); //设置背景色

 this.addWindowListener(new WindowAdapter() {
  //添加对窗口状态的监听
  public void windowClosing(WindowEvent arg0) {
  //当窗口关闭时
  System.exit(0); //退出程序
  }

 });

 this.addKeyListener(new KeyMonitor()); //添加键盘监听器
 this.setResizable(false); //窗口大小不可更改
 this.setVisible(true); //显示窗口

 new Thread(new RepaintThread()).start(); //开启重画线程
 }

 /**
 * 画出程序界面各组件元素
 */
 public void paint(Graphics g) {
 Color c = g.getColor();

 g.setFont(new Font("微软雅黑", Font.BOLD, 40));
 g.setColor(Color.black);
 g.drawString("欢迎使用上位机实时监控系统", 45, 190);

 g.setFont(new Font("微软雅黑", Font.ITALIC, 26));
 g.setColor(Color.BLACK);
 g.drawString("Version:1.0 Powered By:ZhongLei", 280, 260);

 g.setFont(new Font("微软雅黑", Font.BOLD, 30));
 g.setColor(color);
 g.drawString("————点击Enter键进入主界面————", 100, 480);
 //使文字 "————点击Enter键进入主界面————" 黑白闪烁
 if (color == Color.WHITE) color = Color.black;
 else if (color == color.BLACK) color = Color.white;

 }

 /**
 * 双缓冲方式重画界面各元素组件
 */
 public void update(Graphics g) {
 if (offScreen == null) offScreen = this.createImage(WIDTH, HEIGHT);
 Graphics gOffScreen = offScreen.getGraphics();
 Color c = gOffScreen.getColor();
 gOffScreen.setColor(Color.white);
 gOffScreen.fillRect(0, 0, WIDTH, HEIGHT); //重画背景画布
 this.paint(gOffScreen); //重画界面元素
 gOffScreen.setColor(c);
 g.drawImage(offScreen, 0, 0, null); //将新画好的画布“贴”在原画布上
 }

 /*
 * 内部类形式实现对键盘事件的监听
 */
 private class KeyMonitor extends KeyAdapter {

 public void keyReleased(KeyEvent e) {
  int keyCode = e.getKeyCode();
  if (keyCode == KeyEvent.VK_ENTER) { //当监听到用户敲击键盘enter键后执行下面的操作
  setVisible(false); //隐去欢迎界面
  dataview.setVisible(true); //显示监测界面
  dataview.dataFrame(); //初始化监测界面
  }
 }

 }

 /*
 * 重画线程(每隔250毫秒重画一次)
 */
 private class RepaintThread implements Runnable {
 public void run() {
  while(true) {
  repaint();
  try {
   Thread.sleep(250);
  } catch (InterruptedException e) {
   //重画线程出错抛出异常时创建一个Dialog并显示异常详细信息
   String err = ExceptionWriter.getErrorInfoFromException(e);
   JOptionPane.showMessageDialog(null, err, "错误", JOptionPane.INFORMATION_MESSAGE);
   System.exit(0);
  }
  }
 }

 }

}

运行截图:

注:实际运行过程中最下面的“点击Enter键进入主界面”有一个一闪一闪的效果(是通过每隔一段时间重画一次界面,让这句话以白黑两色反复交替出现实现的),双缓冲方式利于解决重画时界面闪烁的问题(如果不使用双缓冲方式的话相当于每次重画时是在旧界面上一点一点画上新东西,而双缓冲实质上是通过先在内存中直接画好一张新界面图,然后一次性直接用新界面覆盖掉旧界面)

DataView.java代码如下:(该类用于实时显示串口数据)

简单说明:

硬件设备每隔一段时间通过串口发送一次数据到计算机,该串口工具成功连接至硬件设备并添加监听后,会在每次接收到数据时解析数据并更新界面;

你在使用时很可能需求跟我不一样,该类仅供参考,实际使用中你很可能需要重新制作数据显示界面以及数据解析方式

package serialPort;

import java.awt.Button;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Label;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.TooManyListenersException;

import javax.swing.JOptionPane;

import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import serialException.*;

/**
 * 监测数据显示类
 * @author Zhong
 *
 */
public class DataView extends Frame {

 /**
 *
 */
 private static final long serialVersionUID = 1L;

 Client client = null;

 private List<String> commList = null; //保存可用端口号
 private SerialPort serialPort = null; //保存串口对象

 private Font font = new Font("微软雅黑", Font.BOLD, 25);

 private Label tem = new Label("暂无数据", Label.CENTER); //温度
 private Label hum = new Label("暂无数据", Label.CENTER); //湿度
 private Label pa = new Label("暂无数据", Label.CENTER); //压强
 private Label rain = new Label("暂无数据", Label.CENTER); //雨量
 private Label win_sp = new Label("暂无数据", Label.CENTER); //风速
 private Label win_dir = new Label("暂无数据", Label.CENTER); //风向

 private Choice commChoice = new Choice(); //串口选择(下拉框)
 private Choice bpsChoice = new Choice(); //波特率选择

 private Button openSerialButton = new Button("打开串口");

 Image offScreen = null; //重画时的画布

 //设置window的icon
 Toolkit toolKit = getToolkit();
 Image icon = toolKit.getImage(DataView.class.getResource("computer.png"));

 /**
 * 类的构造方法
 * @param client
 */
 public DataView(Client client) {
 this.client = client;
 commList = SerialTool.findPort(); //程序初始化时就扫描一次有效串口
 }

 /**
 * 主菜单窗口显示;
 * 添加Label、按钮、下拉条及相关事件监听;
 */
 public void dataFrame() {
 this.setBounds(client.LOC_X, client.LOC_Y, client.WIDTH, client.HEIGHT);
 this.setTitle("CDIO工程项目");
 this.setIconImage(icon);
 this.setBackground(Color.white);
 this.setLayout(null);

 this.addWindowListener(new WindowAdapter() {
  public void windowClosing(WindowEvent arg0) {
  if (serialPort != null) {
   //程序退出时关闭串口释放资源
   SerialTool.closePort(serialPort);
  }
  System.exit(0);
  }

 });

 tem.setBounds(140, 103, 225, 50);
 tem.setBackground(Color.black);
 tem.setFont(font);
 tem.setForeground(Color.white);
 add(tem);

 hum.setBounds(520, 103, 225, 50);
 hum.setBackground(Color.black);
 hum.setFont(font);
 hum.setForeground(Color.white);
 add(hum);

 pa.setBounds(140, 193, 225, 50);
 pa.setBackground(Color.black);
 pa.setFont(font);
 pa.setForeground(Color.white);
 add(pa);

 rain.setBounds(520, 193, 225, 50);
 rain.setBackground(Color.black);
 rain.setFont(font);
 rain.setForeground(Color.white);
 add(rain);

 win_sp.setBounds(140, 283, 225, 50);
 win_sp.setBackground(Color.black);
 win_sp.setFont(font);
 win_sp.setForeground(Color.white);
 add(win_sp);

 win_dir.setBounds(520, 283, 225, 50);
 win_dir.setBackground(Color.black);
 win_dir.setFont(font);
 win_dir.setForeground(Color.white);
 add(win_dir);

 //添加串口选择选项
 commChoice.setBounds(160, 397, 200, 200);
 //检查是否有可用串口,有则加入选项中
 if (commList == null || commList.size()<1) {
  JOptionPane.showMessageDialog(null, "没有搜索到有效串口!", "错误", JOptionPane.INFORMATION_MESSAGE);
 }
 else {
  for (String s : commList) {
  commChoice.add(s);
  }
 }
 add(commChoice);

 //添加波特率选项
 bpsChoice.setBounds(526, 396, 200, 200);
 bpsChoice.add("1200");
 bpsChoice.add("2400");
 bpsChoice.add("4800");
 bpsChoice.add("9600");
 bpsChoice.add("14400");
 bpsChoice.add("19200");
 bpsChoice.add("115200");
 add(bpsChoice);

 //添加打开串口按钮
 openSerialButton.setBounds(250, 490, 300, 50);
 openSerialButton.setBackground(Color.lightGray);
 openSerialButton.setFont(new Font("微软雅黑", Font.BOLD, 20));
 openSerialButton.setForeground(Color.darkGray);
 add(openSerialButton);
 //添加打开串口按钮的事件监听
 openSerialButton.addActionListener(new ActionListener() {

  public void actionPerformed(ActionEvent e) {

  //获取串口名称
  String commName = commChoice.getSelectedItem();
  //获取波特率
  String bpsStr = 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());
    //监听成功进行提示
    JOptionPane.showMessageDialog(null, "监听成功,稍后将显示监测数据!", "提示", JOptionPane.INFORMATION_MESSAGE);

   } catch (SerialPortParameterFailure | NotASerialPort | NoSuchPort | PortInUse | TooManyListeners e1) {
    //发生错误时使用一个Dialog提示具体的错误信息
    JOptionPane.showMessageDialog(null, e1, "错误", JOptionPane.INFORMATION_MESSAGE);
   }
   }
  }

  }
 });

 this.setResizable(false);

 new Thread(new RepaintThread()).start(); //启动重画线程

 }

 /**
 * 画出主界面组件元素
 */
 public void paint(Graphics g) {
 Color c = g.getColor();

 g.setColor(Color.black);
 g.setFont(new Font("微软雅黑", Font.BOLD, 25));
 g.drawString(" 温度: ", 45, 130);

 g.setColor(Color.black);
 g.setFont(new Font("微软雅黑", Font.BOLD, 25));
 g.drawString(" 湿度: ", 425, 130);

 g.setColor(Color.black);
 g.setFont(new Font("微软雅黑", Font.BOLD, 25));
 g.drawString(" 压强: ", 45, 220);

 g.setColor(Color.black);
 g.setFont(new Font("微软雅黑", Font.BOLD, 25));
 g.drawString(" 雨量: ", 425, 220);

 g.setColor(Color.black);
 g.setFont(new Font("微软雅黑", Font.BOLD, 25));
 g.drawString(" 风速: ", 45, 310);

 g.setColor(Color.black);
 g.setFont(new Font("微软雅黑", Font.BOLD, 25));
 g.drawString(" 风向: ", 425, 310);

 g.setColor(Color.gray);
 g.setFont(new Font("微软雅黑", Font.BOLD, 20));
 g.drawString(" 串口选择: ", 45, 410);

 g.setColor(Color.gray);
 g.setFont(new Font("微软雅黑", Font.BOLD, 20));
 g.drawString(" 波特率: ", 425, 410);

 }

 /**
 * 双缓冲方式重画界面各元素组件
 */
 public void update(Graphics g) {
 if (offScreen == null) offScreen = this.createImage(Client.WIDTH, Client.HEIGHT);
 Graphics gOffScreen = offScreen.getGraphics();
 Color c = gOffScreen.getColor();
 gOffScreen.setColor(Color.white);
 gOffScreen.fillRect(0, 0, Client.WIDTH, Client.HEIGHT); //重画背景画布
 this.paint(gOffScreen); //重画界面元素
 gOffScreen.setColor(c);
 g.drawImage(offScreen, 0, 0, null); //将新画好的画布“贴”在原画布上
 }

 /*
 * 重画线程(每隔30毫秒重画一次)
 */
 private class RepaintThread implements Runnable {
 public void run() {
  while(true) {
  //调用重画方法
  repaint();

  //扫描可用串口
  commList = SerialTool.findPort();
  if (commList != null && commList.size()>0) {

   //添加新扫描到的可用串口
   for (String s : commList) {

   //该串口名是否已存在,初始默认为不存在(在commList里存在但在commChoice里不存在,则新添加)
   boolean commExist = false; 

   for (int i=0; i<commChoice.getItemCount(); i++) {
    if (s.equals(commChoice.getItem(i))) {
    //当前扫描到的串口名已经在初始扫描时存在
    commExist = true;
    break;
    }
   }

   if (commExist) {
    //当前扫描到的串口名已经在初始扫描时存在,直接进入下一次循环
    continue;
   }
   else {
    //若不存在则添加新串口名至可用串口下拉列表
    commChoice.add(s);
   }
   }

   //移除已经不可用的串口
   for (int i=0; i<commChoice.getItemCount(); i++) {

   //该串口是否已失效,初始默认为已经失效(在commChoice里存在但在commList里不存在,则已经失效)
   boolean commNotExist = true; 

   for (String s : commList) {
    if (s.equals(commChoice.getItem(i))) {
    commNotExist = false;
    break;
    }
   }

   if (commNotExist) {
    //System.out.println("remove" + commChoice.getItem(i));
    commChoice.remove(i);
   }
   else {
    continue;
   }
   }

  }
  else {
   //如果扫描到的commList为空,则移除所有已有串口
   commChoice.removeAll();
  }

  try {
   Thread.sleep(30);
  } catch (InterruptedException e) {
   String err = ExceptionWriter.getErrorInfoFromException(e);
   JOptionPane.showMessageDialog(null, err, "错误", JOptionPane.INFORMATION_MESSAGE);
   System.exit(0);
  }
  }
 }

 }

 /**
 * 以内部类形式创建一个串口监听类
 * @author zhong
 *
 */
 private 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 溢位(溢出)错误

  case SerialPortEvent.FE: // 9 帧错误

  case SerialPortEvent.PE: // 8 奇偶校验错误

  case SerialPortEvent.CD: // 6 载波检测

  case SerialPortEvent.CTS: // 3 清除待发送数据

  case SerialPortEvent.DSR: // 4 待发送数据准备好了

  case SerialPortEvent.RI: // 5 振铃指示

  case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 输出缓冲区已清空
   break;

  case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用数据

   //System.out.println("found data");
   byte[] data = null;

   try {
   if (serialPort == null) {
    JOptionPane.showMessageDialog(null, "串口对象为空!监听失败!", "错误", JOptionPane.INFORMATION_MESSAGE);
   }
   else {
    data = SerialTool.readFromPort(serialPort); //读取数据,存入字节数组
    //System.out.println(new String(data));

   // 自定义解析过程,你在实际使用过程中可以按照自己的需求在接收到数据后对数据进行解析
    if (data == null || data.length < 1) { //检查数据是否读取正确
    JOptionPane.showMessageDialog(null, "读取数据过程中未获取到有效数据!请检查设备或程序!", "错误", JOptionPane.INFORMATION_MESSAGE);
    System.exit(0);
    }
    else {
    String dataOriginal = new String(data); //将字节数组数据转换位为保存了原始数据的字符串
    String dataValid = ""; //有效数据(用来保存原始数据字符串去除最开头*号以后的字符串)
    String[] elements = null; //用来保存按空格拆分原始字符串后得到的字符串数组
    //解析数据
    if (dataOriginal.charAt(0) == '*') { //当数据的第一个字符是*号时表示数据接收完成,开始解析
     dataValid = dataOriginal.substring(1);
     elements = dataValid.split(" ");
     if (elements == null || elements.length < 1) { //检查数据是否解析正确
     JOptionPane.showMessageDialog(null, "数据解析过程出错,请检查设备或程序!", "错误", JOptionPane.INFORMATION_MESSAGE);
     System.exit(0);
     }
     else {
     try {
      //更新界面Label值
      /*for (int i=0; i<elements.length; i++) {
      System.out.println(elements[i]);
      }*/
      //System.out.println("win_dir: " + elements[5]);
      tem.setText(elements[0] + " ℃");
      hum.setText(elements[1] + " %");
      pa.setText(elements[2] + " hPa");
      rain.setText(elements[3] + " mm");
      win_sp.setText(elements[4] + " m/s");
      win_dir.setText(elements[5] + " °");
     } catch (ArrayIndexOutOfBoundsException e) {
      JOptionPane.showMessageDialog(null, "数据解析过程出错,更新界面数据失败!请检查设备或程序!", "错误", JOptionPane.INFORMATION_MESSAGE);
      System.exit(0);
     }
     }
    }
    }

   }   

   } catch (ReadDataFromSerialPortFailure | SerialPortInputStreamCloseFailure e) {
   JOptionPane.showMessageDialog(null, e, "错误", JOptionPane.INFORMATION_MESSAGE);
   System.exit(0); //发生读取错误时显示错误信息后退出系统
   } 

   break;

  }

 }

 }

}

运行截图:

整个项目源码打包下载:http://xiazai.jb51.net/201612/yuanma/javaserialMonitor(jb51.net).rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 使用Java实现串口通信

    1.介绍 使用Java实现的串口通信程序,支持十六进制数据的发送与接收. 源码下载地址:http://download.csdn.net/detail/kong_gu_you_lan/9611343 效果图如下: 2.RXTXcomm Java串口通信依赖的jar包RXTXcomm.jar 下载地址:http://download.csdn.net/detail/kong_gu_you_lan/9611334 内含32位与64位版本 使用方法: 拷贝 RXTXcomm.jar 到 JAVA_HO

  • javascript基础知识讲解

    本篇适合javascript新手或者学了前端一段时间,对js概念不清晰的同学~~. 学习目的 本文针对javascript基础薄弱的同学,可以加深对javascript的理解. 本文将讲述以下几点对于初学者开说javascript(有的是大部分语言都有的)的坑 讲解内容如下: 1. 连等 2. i++ 3. 包装对象 4. 引用类型 5. && 与 || 讲解部分 1. 连等 小试牛刀 连等是常见的表达式,但是并不是所有情况都适合连等,连等只适用于字面量并不适用于引用类型. // 字面量连

  • javascript常用经典算法详解

    阅读目录 冒泡排序 插入排序 希尔排序 归并排序 快速排序 选择排序 奇偶排序 总结 前言:在前端大全中看到这句话,以此共勉.基础决定你可能达到的高度, 而业务决定了你的最低瓶颈 其实javascript算法在平时的编码中用处不大,不过不妨碍我们学习它,学习一下这些算法的思想,锻炼一下自己的思维模式. 本文不会每种方法都介绍一下,只介绍一下七种,纯属为了学习而学习,如果觉得代码不是很好理解,可以将数组里面的内容代入函数里面. 不过刚开始理解的时候确实挺头疼的.废话少说,搞起来!! 冒泡排序 原理

  • JavaScript BASE64算法实现(完美解决中文乱码)

    JavaScript 的 BASE64 算法 var BASE64={ enKey: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', deKey: new Array( -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -

  • java 串口通信详细及简单实例

    java 实现串口通信 最近做了一个与硬件相关的项目,刚开始听说用java和硬件打交道,着实下了一大跳.java也可以操作硬件? 后来接触到是用java通过串口通信控制硬件感觉使用起来还不错,也很方便. 特拿出来和大家一起分享一下. 准备工作: 首先到SUN官网下载一个zip包:javacomm20-win32.zip 其中重要的有这几个文件: win32com.dll comm.jar javax.comm.properties 按照说明配置好环境,如下: 将win32com.dll复制到<J

  • Java int与integer的对比区别

    Java int与 integer区别: int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象 1.Java 中的数据类型分为基本数据类型和复杂数据类型 int 是前者而integer 是后者(也就是一个类):因此在类进行初始化时int类的变量初始为0.而Integer的变量则初始化为null. 2.初始化时: int i =1:Integer i= new Integer(1);(要把int

  • java 数据类型有哪些取值范围多少

    java 数据类型: 在Java中,数据类型分为两大种:基本数据类型(值类型)和包装类型(引用数据类型).基本数据类型不是对象,不能调用toString().hashCode().getClass().equals()等方法. 8种基本数据类型-----8种包装类型  整型: byte Byte [-128,127] 1个字节([-2的7次方,2的7次方-1]) 一个字节有8位 short Short [-32768,32767] 2个字节([-2的15次方,2的15次方-1]) 2*8-1 i

  • Java图片裁剪和生成缩略图的实例方法

    一.缩略图 在浏览相册的时候,可能需要生成相应的缩略图. 直接上代码: public class ImageUtil { private Logger log = LoggerFactory.getLogger(getClass()); private static String DEFAULT_PREVFIX = "thumb_"; private static Boolean DEFAULT_FORCE = false;//建议该值为false /** * <p>Tit

  • 基于Java编写串口通信工具

    最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^ 一: 首先,你需要下载一个额外的支持Java串口通信操作的jar包,由于java.comm比较老了,而且不支持64位系统,这里推荐Rxtx这个jar包(32位/64位均支持). 官方下载地址:http://fizzed.com/oss/rxtx-for-java (注:可能需要FQ才能下载) 不能FQ的童鞋,可以在这里下载: http://xiazai.jb51

  • 基于java编写局域网多人聊天室

    由于需要制作网络计算机网络课程设计,并且不想搞网络布线或者局域网路由器配置等等这种完全搞不懂的东西,最后决定使用socket基于java编写一个局域网聊天室: 关于socket以及网络编程的相关知识详见我另一篇文章:Java基于socket编程 程序基于C/S结构,即客户端服务器模式. 服务器: 默认ip为本机ip 需要双方确定一个端口号 可设置最大连接人数 可启动与关闭 界面显示在线用户人以及姓名(本机不在此显示) 客户端: 需要手动设置服务器ip地址(局域网) 手动设置端口号 输入姓名 可连

  • Python基于Tkinter编写crc校验工具

    本篇文章,完全是用来记录代码用的,目的是使用Python,基于Tkinter编写crc校验工具. # -*- coding: utf-8 -*- import Tkinter import tkFileDialog WIDTH = 16 TOPBIT = (1 << (WIDTH - 1)) crcTable = {} class FindLocation(object): def __init__(self): #创建主窗口,用于容纳其它组件 self.root = Tkinter.Tk()

  • C#串口通信工具类的封装

    本文实例为大家分享了C#串口通信工具类的封装代码,供大家参考,具体内容如下  1.SerialPortHelper串口工具类封装 using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers;   namespace public.Util {

  • 基于Java编写简易的算式测试程序

    目录 1.程序功能 2.实现思路 3.具体代码 4.程序执行结果 1.程序功能 这个程序可以自动生成指定数量的加减乘 ”三则运算“ 题目,用户可以进行回答,在答题完毕后,会显示答对题数.准确率和所用时间.这不写一个测测亲戚家小孩 2.实现思路 首先,我们新建一个名为 Calculate 的 class 文件,然后就可以敲我们的代码啦.接着,我们先写一个 main 函数,然后第一步是创建一个 Scanner 对象,用来接受用户输入的题目数量.下一步我们定义几个变量来保存程序的信息,即 result

  • 基于Python编写微信清理工具的示例代码

    目录 主要功能 运行环境 核心代码 完整代码 前几天网上找了一款 PC 端微信自动清理工具,用了一下,电脑释放了 30GB 的存储空间,而且不会删除文字的聊天记录,很好用,感觉很多人都用得到,就在此分享一下,而且是用 Python 写的,喜欢 Python 的小伙伴可以探究一下. 主要功能 它可以自动删除 PC 端微信自动下载的大量文件.视频.图片等数据内容,释放几十 G 的空间占用,而且不会删除文字的聊天记录,可以放心使用. 工作以后,微信的群聊实在太多了,动不动就被拉入一个群中,然后群聊里大

  • QT编写tcp通信工具(Client篇)

    本文实例为大家分享了QT编写tcp通信工具的具体实现代码,Client篇,供大家参考,具体内容如下 1.说明 使用qt写一个类似网上常见的网络调试工具.此篇为Client端.下一遍再写Server端. 2.基本流程 Client端相对简单:创建QTcpSocket对象,为对象的readyRead,error,connected(可选)分别写槽函数,以处理读数据,错误,连接成功三个事件. 连接使用对象的connectToHost方法,断开使用disconnectFromHost方法. 程序不做编码

  • 基于Java编写一个简单的风控组件

    目录 一.背景 1.为什么要做风控 2.为什么要自己写风控 3.其它要求 二.思路 1.风控规则的实现 2.调用方式的实现 三.具体实现 1.风控计数规则实现 2.注解的实现 四.测试一下 1.写法 2.Debug看看 一.背景 1.为什么要做风控 这不得拜产品大佬所赐 目前我们业务有使用到非常多的AI能力,如ocr识别.语音测评等,这些能力往往都比较费钱或者费资源,所以在产品层面也希望我们对用户的能力使用次数做一定的限制,因此风控是必须的! 2.为什么要自己写风控 那么多开源的风控组件,为什么

  • QT编写tcp通信工具(Server端)

    本文实例为大家分享了QT编写Server端的tcp通信工具的具体代码,供大家参考,具体内容如下 1.说明 使用qt写一个类似网上常见的网络调试工具.此篇为Server端.Client端在上一篇. 2.基本流程 新建QTcpServer对象,为其newConnection信号写槽函数.此为新的Client连接信号,在其对应槽函数里使用nextPendingConnection方法获取Client对象,并为Client添加readyRead(读数据),disconnected(断开连接)两个信号写槽

随机推荐