背景:
15年暑假期间参加ICAN全国物联网大赛,主题是应用于各种气体危险场合的四轴飞行器的大气参数测试。由于需要地面服务器作为单片机数据与上位机的传输,并且要存储进数据库,开始写java usb串口的小程序。因各种事情的搁浅,并没有在大赛之前完成这个项目,最近几天将其简单的修改,实现了基本功能。
准备工作:
java主要发展方向为上位机软件方向,所以底层的开源包较少,且需要C语言写的脚本做铺垫,所以实现java的USB串口通讯过程比较复杂,下面是具体步骤:
1、需要一个RXTX.jar的开源包,切将内置文件按说明放置到指定位置。
说明如下:
1)、把rxtxParallel.dll、rxtxSerial.dll放置在:C:\WINDOWS\system32下。
2)、如果是在开发的时候(JDK),需要把RXTXcomm.jar、rxtxParallel.dll、rxtxSerial.dll拷贝到..\jre...\lib\ext下;
如:D:\Program Files\Java\jre1.6.0_02\lib\ext。
2、DButils包、数据库驱动包。
(两个驱动包。)
3、通信单片机。
4、数据库环境。
大体组成:
主要由面向对象、通信、JDBC、JDBCTools、Swing等几个累组成。通过Swing图形化界面将几个累贯穿起来,就形成了大体框架。主界面如下所示:
图1(主界面)
(由于是事后制作,并没有具体数据类型,只能将名称暂时定义为数据n,单位亦是如此。)
具体实现:
串口通信类:
需要对串口、频率、数据位、校验位、停止位等做相应的匹配,比较复杂。
public class SimpleRead implements Runnable, SerialPortEventListener {
static CommPortIdentifier portId;
//枚举类
static Enumeration portList;
//String Xian = "";
static boolean isOpen = false;
InputStream inputStream;
SerialPort serialPort;
Thread readThread;
public void Srun() {
isOpen = false;
portList = CommPortIdentifier.getPortIdentifiers();
/*不带参数的getPortIdentifiers方法获得一个枚举对象,该对象又包含了系统中管理每个端口的CommPortIdentifier对象。
注意这里的端口不仅仅是指串口,也包括并口。这个方法还可以带参数。getPortIdentifiers(CommPort)获得与已经被应
用程序打开的端口相对应的CommPortIdentifier对象。getPortIdentifier(String portName)获取指定端口名(比如“COM1”)
的CommPortIdentifier对象。*/
System.out.println(Parity);
while (portList.hasMoreElements()) {
portId = (CommPortIdentifier) portList.nextElement();
/*getPortType方法返回端口类型*/
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
/* 找Windows下的第一个串口*/
if (portId.getName().equals(com)) {
/*找Unix-like系统下的第一个串口*/
//if (portId.getName().equals("/dev/term/a")) {
SimpleRead reader = new SimpleRead();
}
}
}
}
public SimpleRead() {
try {
/* open方法打开通讯端口,获得一个CommPort对象。它使程序独占端口。如果端口正被其他应用程序占用,将使用
CommPortOwnershipListener事件机制,传递一个PORT_OWNERSHIP_REQUESTED事件。每个端口都关联一个
InputStream和一个OutputStream。如果端口是用open方法打开的,那么任何的getInputStream都将返回
相同的数据流对象,除非有close被调用。有两个参数,第一个为应用程序名;第二个参数是在端口打开
时阻塞等待的毫秒数。 */
serialPort = (SerialPort) portId.open("SimpleReadApp", 2000);
} catch (PortInUseException e) {}
try {
/*获取端口的输入流对象*/
inputStream = serialPort.getInputStream();
} catch (IOException e) {}
try {
/*注册一个SerialPortEventListener事件来监听串口事件*/
serialPort.addEventListener(this);
} catch (TooManyListenersException e) {}
/*数据可用*/
serialPort.notifyOnDataAvailable(true);
try {
/*设置串口初始化参数,依次是波特率,数据位,停止位和校验*/
/*serialPort.setSerialPortParams(115200,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);*/
serialPort.setSerialPortParams(PinLv,
DataBits,
StopBits,
Parity);
} catch (UnsupportedCommOperationException e) {}
readThread = new Thread(this);
readThread.start();
}
public void run() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {}
}
//串口事件
public void serialEvent(SerialPortEvent event) {
switch(event.getEventType()) {
case SerialPortEvent.BI:/*Break interrupt,通讯中断*/
case SerialPortEvent.OE:/*Overrun error,溢位错误*/
case SerialPortEvent.FE:/*Framing error,传帧错误*/
case SerialPortEvent.PE:/*Parity error,校验错误*/
case SerialPortEvent.CD:/*Carrier detect,载波检测*/
case SerialPortEvent.CTS:/*Clear to send,清除发送*/
case SerialPortEvent.DSR:/*Data set ready,数据设备就绪*/
case SerialPortEvent.RI:/*Ring indicator,响铃指示*/
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*Output buffer is empty,输出缓冲区清空*/
break;
case SerialPortEvent.DATA_AVAILABLE:/*Data available at the serial port,端口有可用数据。读到缓冲数组,输出到终端*/
byte[] readBuffer = new byte[20];
char[] readChar = new char[20];
String readStr="";
String reg = ",";
int numBytes=0;
try {
while (inputStream.available() > 0) {
numBytes= inputStream.read(readBuffer);
}
for(int iii=0;iii<numBytes;iii++){
readStr= readStr+(readBuffer[iii]);//Byte.toString(readBuffer[iii]);
}
String txt = new String(readBuffer,"gbk")+"\r\n";
String [] arr = txt.split(reg);//正则表达式切割
//System.out.printf(s+" ");
OneJFrame.jTextField1.setText(arr[0]); //切割后的字符串在主界面上显示
OneJFrame.jTextField2.setText(arr[1]);
OneJFrame.jTextField3.setText(arr[2]);
OneJFrame.jTextField4.setText(arr[3]);
} catch (IOException e) {}
isOpen = true;
break;
}
}
public void close()
{
if (isOpen)
{
try
{
serialPort.notifyOnDataAvailable(false);
serialPort.removeEventListener();
inputStream.close();
serialPort.close();
isOpen = false;
} catch (IOException ex)
{
//"关闭串口失败";
}
}
}
(PS:串口通信的方法部分借鉴于他人,由于时间太长无法找到出处,得罪之处还望海涵。在此表示感谢。)
JDBC及JDBCTools类:
将串口调试通过,以后的工作变变得简单起来,无非就是一个图形化界面加JDBC数据库的操作。下面进行说明。
JDBCTools:
public static Connection getConnection() throws Exception {
Properties properties = new Properties();
InputStream inStream = JDBCTools.class.getClassLoader()
.getResourceAsStream("jdbc.properties");
properties.load(inStream);
// 1. 准备获取连接的 4 个字符串: user, password, jdbcUrl, driverClass
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String jdbcUrl = properties.getProperty("jdbcUrl");
String driverClass = properties.getProperty("driverClass");
// 2. 加载驱动: Class.forName(driverClass)
Class.forName(driverClass);
// 3. 调用
// DriverManager.getConnection(jdbcUrl, user, password)
// 获取数据库连接
Connection connection = DriverManager.getConnection(jdbcUrl, user,
password);
return connection;
}
public static void releaseDB(ResultSet resultSet, Statement statement,
Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//提交事务
public static void commit(Connection connection){
if(connection != null){
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
<pre name="code" class="java"> JDBC数据库操作类:
<pre name="code" class="java">public void Query(){
<span style="white-space:pre"> </span>//数据库查询。
Connection connection = null;
try {
connection = JDBCTools.getConnection();
String sql = "SELECT * " +
"FROM customers";
Object obj = queryRunner.query(connection, sql,
new MyResultSetHandler());
//System.out.println(obj);
new Select().setVisible(true);
String s = "a\tb\tc\td\t\r\n\r\n";
Select.jTextArea1.setText(s+obj.toString());
} catch (Exception e) {
{JOptionPane.showMessageDialog( null , "查询失败了" ,"很遗憾" , JOptionPane.ERROR_MESSAGE) ;}
} finally{
JDBCTools.releaseDB(null, null, connection);
}
}
public void InsertBlob(){
<span style="white-space:pre"> </span>//存储数据库
Connection connection = null;
PreparedStatement preparedStatement = null;
// System.out.print(connection);
try {
connection = JDBCTools.getConnection();
//System.out.print(connection);
String sql = "INSERT INTO customers(A, B, C, D)"
+ "VALUES("+"\'"+jTextField1.getText()+"\'"+","+"\'"+jTextField2.getText()+"\'"+","
+"\'"+jTextField3.getText()+"\'"+","+"\'"+jTextField4.getText()+"\'"+")";
// System.out.print(sql);
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
//JOptionPane.showMessageDialog( null , "存入成功" ,"恭喜!" , JOptionPane.ERROR_MESSAGE) ;
JOptionPane.showMessageDialog(null, "存入成功");
} catch (Exception e) {
//e.printStackTrace();
{JOptionPane.showMessageDialog( null , "失败了" ,"很遗憾" , JOptionPane.ERROR_MESSAGE) ;}
} finally{
//JDBCTools.commit(connection);
JDBCTools.releaseDB(null, preparedStatement, connection);
}
}
(PS:感谢尚硅谷的教学视频,让我能够自学成长)
这里重点总结一下在调试中出现的小问题:
1、jdbc.properties文件的存放位置
读取jdbc.properties(进入数据库的信息配置文件)时,要注意文件的构造位置,不同的软件有不同的存放方式,那自己做项目用的netbeans来说。当文件在下图位置时
图2(文件信息1)
jdbc.properties文件是不会被加载的,所以系统会一直报告空指针错误,即数据库没有连接上。我们需要将其放在独属的一个包中并进行选择,如下图:
图3(文件信息2)
这样,文件就会被加载了。
2、数据库数据类型问题
自己将四个数据类型全部都设置为varchar2类型(相当于java中的String),当用到insert语句往数据库里面存储数字类型时,是不需要加修饰的,而存储字符串类型时,需要用到‘’单引号括起来,否则会存入失败。如:
<pre name="code" class="sql">insert into customers values(你,你,你,你);
是不可行的,而需要
insert into customers values('你','你','你','你');
所以编写java代码时要格外注意不要忘掉单引号,自己写的代码如下:
String sql = "INSERT INTO customers(A, B, C, D)"
+ "VALUES("+"\'"+jTextField1.getText()+"\'"+","+"\'"+jTextField2.getText()+"\'"+","
+"\'"+jTextField3.getText()+"\'"+","+"\'"+jTextField4.getText()+"\'"+")";
之前遇到只能将数字存入数据库而汉字与字符不可以的情况,所以这里做一下细节的总结。
面向对象类:
简单的一个类,不做过多赘述。
import java.sql.Date;
public class Customer {
private String a;
private String b;
private String c;
private String d;
public Customer(String a,String b,String c,String d){
this.a=a;
this.b=b;
this.c=c;
this.d=d;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
@Override
public String toString() {
return a + "\t" + b + "\t" + c + "\t" + d+"\r\n";
}
}
Swing类:
代码过长,这里不做展示,有兴趣的同学可以对netbeans的图形化界面的制作做一下了解。
这里给大家分享一个关于图形化界面异常处理弹出小框的博客供大家学习。
JOptionPane类提示框的一些常用的方法 - - ITeye技术网站 http://url.cn/9u9uNd
总结:
这个小项目的完成虽然比较简单,但涉及知识面较广,且数据库部分做的并不是很完善,有待改进。作为物联网专业的学生,串口通讯是一个非常关键的媒介,打通了上位机与下位机的通道,数据库、网站展示、大数据分析都将可以实现,这也将我的认知程度提高了一个境界,甚至将大学所学的知识完全贯穿了起来,我相信,在惠普实训结束以后,一个通过惠普EPM实训提高以后的我,下学期的课程设计,将会交给老师一个完美的答卷。
感谢:
从小老师对我来说是高一个档次的职业。对老师尊敬,感恩就好像长在脑子里一样,短短的不到一年,我进步了很多,促使我成长的老师我都一一记得。
建大的汪明、王玉玲、潘春伟老师,我对学习的认识、表达能力薄弱的认知、编程的思想,和他们的直接或间接的教导是分不开的。
惠普的王岩、郭吉楠、张亚冲、代森荣、贾蕊鲜老师,解决了自己之前好多好多不懂的问题,还有对教导专门的教导老师的渴望,java基础有了提升,学到了新的编程语言,我在这里过得很充实,也很满足。
毕竟第一篇博客,感慨有点多,希望大家不要起一身土豆o(^▽^)o。
------------