视频地址:http://v.youku.com/v_show/id_XODkxMDQwMzU2.htm
操作系统:WIN8 64 位
需要rxtx jar包的支持,我的是64位,32位的百度下载.
rxtx jar包(64位)下载地址:
http://download.csdn.net/detail/h_hongchang/8428621
懒得复制粘贴,直接下载代码文件:http://download.csdn.net/detail/h_hongchang/8440181
使用方法:
1.修改 上位机界面类(SerialCorrespondenceClient)里面的 COMS 变量为你当前arduino的串口;
2.运行界面:
点击"开启监听",
硬件:arduino uno3,连线方式根据代码上调整就好,当然也可以自己定义,不影响上位机.
上位机使用java语言编写,界面功能很简单.类中很多方法没有使用到,是为以后扩展功能留存的.着急回家过年没怎么好好整理代码,大家可以改进.添加新功能.灵敏度稍欠缺,过年回来再优化吧.
有三个类文件:
1.上位机界面;
2.串口通信监听;
3.模拟键盘按键工具类.
1.上位机界面代码:
<span style="font-family:Microsoft YaHei;">package com.fnw.fruitkey;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class SerialCorrespondenceClient extends JFrame{
//********组件变量************
private JButton switchListenBT;//打开/关闭监听按钮
private JLabel statusLB;//状态
//********组件文案变量************
private String switchMsg ="开启监听";//按钮操作文案
private String statusMsg ="当前状态为[关闭]";//当前状态文案
private Color statusColor = Color.red;
//********普通变量************
private Boolean switchFlag = false;//打开或关闭标示,默认关闭
private SerialCorrespondenceController pc;//串口监听
private String COMS = "COM5";//此处修改为当前arduino的串口
/**
* 测试
* @author hongchang.hu
* @function
* @date 2015-2-9 下午4:07:00
* @param @param args
* @return void
*/
public static void main(String[] args) {
SerialCorrespondenceClient sw = new SerialCorrespondenceClient();
sw.Client();
}
public void Client(){
//============模板设置============
JPanel panel=new JPanel();
setSize(250,80);//使其宽度为 width,高度为 height。
setVisible(true);//显示窗体
setDefaultCloseOperation(3);//设置用户在此窗体上发起 "close" 时默认执行的操作。
setResizable(false);//不可调整大小
setLocationRelativeTo(null);//设置窗口相对于指定组件的位置。
//============组件文案============
switchListenBT = new JButton(switchMsg);
statusLB = new JLabel(statusMsg);
//============组件位置============
//statusLB.setBounds(100,350,30,50);//x,y,width,height
setContentPane(panel);
//switchListenBT.setBounds(70,100,100,100);//x,y,width,height
//============组件样式============
statusLB.setForeground(Color.red);
statusLB.setFont(new Font("微软雅黑",Font.BOLD,16));
//============加入组件============
add(switchListenBT);
add(statusLB);
//============为组件添加监听器============
pc = new SerialCorrespondenceController();//准备串口监听
BTListener btListener=new BTListener();
switchListenBT.addActionListener(btListener);
}
//============按钮组件监听器============
class BTListener implements ActionListener {
public void actionPerformed (ActionEvent ae){
//============状态显示============
switchFlag=!switchFlag;//每次点击取反
if(switchFlag){
switchMsg = "关闭监听";
statusMsg = "当前状态为[开启]";
statusColor = Color.green;
pc.openPort(COMS); // 开打arduino连接端口
try {
pc.startRead(0);// 设置为一直监听arduino发送过来的信息
} catch (AWTException e) {
e.printStackTrace();
}
}else{
switchMsg = "开启监听";
statusMsg = "当前状态为[关闭]";
statusColor = Color.red;
pc.closeRead(); // 立即关闭监听
}
switchListenBT.setText(switchMsg);
statusLB.setText(statusMsg);
statusLB.setForeground(statusColor);
}
}
}</span>
2.串口监听代码:
<span style="font-family:Microsoft YaHei;"><span style="font-size:12px;">package com.fnw.fruitkey;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.TooManyListenersException;
public class SerialCorrespondenceController implements Runnable, SerialPortEventListener{
private String appName = "串口通讯";
private int timeout = 2000;//open 端口时的等待时间
private int rate = 9600;//open 端口时的等待时间
private int threadTime = 0;
private Robot robot; //创建一个robot对象
private SetKeyDownUtil sdc;//创建模拟按键对象
private CommPortIdentifier commPort;
private SerialPort serialPort;
private InputStream inputStream;
private OutputStream outputStream;
/**
* @方法名称 :listPort
* @功能描述 :列出所有可用的串口
* @返回值类型 :void
*/
@SuppressWarnings("rawtypes")
public void listPort(){
CommPortIdentifier cpid;//当前串口对象
Enumeration en = CommPortIdentifier.getPortIdentifiers();
System.out.print("列出所有端口:");
while(en.hasMoreElements()){
cpid = (CommPortIdentifier)en.nextElement();
//检测端口类型是否为串口
if(cpid.getPortType() == CommPortIdentifier.PORT_SERIAL){
System.out.println(cpid.getName() + ", " + cpid.getCurrentOwner());
}
}
}
/**
* @方法名称 :write
* @功能描述 :向端口发送数据,请在调用此方法前 先选择端口,并确定SerialPort正常打开!
* @返回值类型 :void
* @param message
* @throws IOException
*/
public void write(String message) throws InterruptedException {
checkPort();
try{
outputStream = new BufferedOutputStream(serialPort.getOutputStream());
outputStream.write(message.getBytes());
log("消息:'"+message+"'发送成功!");
outputStream.close();
}catch(IOException e){
throw new RuntimeException("向端口发送信息时出错:"+e.getMessage());
}
}
/**
* @方法名称 :openPort
* @功能描述 :选择一个端口,比如:COM1 并实例 SerialPort
* @返回值类型 :void
* @param portName
*/
public void openPort(String portName){
/* 打开该指定串口 */
this.commPort = null;
CommPortIdentifier cpid;
Enumeration en = CommPortIdentifier.getPortIdentifiers();
while(en.hasMoreElements()){
cpid = (CommPortIdentifier)en.nextElement();
if(cpid.getPortType() == CommPortIdentifier.PORT_SERIAL && cpid.getName().equals(portName)){
this.commPort = cpid;
break;
}
}
/* 实例 SerialPort*/
if(commPort == null)
log(String.format("无法找到名字为'%1$s'的串口!", portName));
else{
log("当前端口:"+commPort.getName());
try{
// 应用程序名【随意命名】,超时的毫秒数
serialPort = (SerialPort)commPort.open(appName, timeout);
}catch(PortInUseException e){
// 端口已经被占用
throw new RuntimeException(String.format("端口'%1$s'正在使用中!",commPort.getName()));
}
}
}
/**
* @方法名称 :startRead
* @功能描述 :开始监听从端口中接收的数据
* @返回值类型 :void
* @param time 监听程序时间,单位为秒,0 则是一直监听
* @throws AWTException
* @throws InterruptedException
*/
public void startRead(int time) throws AWTException {
robot = new Robot();
sdc = new SetKeyDownUtil();
checkPort();
try{
inputStream = new BufferedInputStream(serialPort.getInputStream());
}catch(IOException e){
throw new RuntimeException("获取端口的InputStream出错:"+e.getMessage());
}
try{
serialPort.addEventListener(this);
// 设置可监听
serialPort.notifyOnDataAvailable(true);
Thread.sleep(2000);
log(String.format("开始监听来自'%1$s'的数据--------------", commPort.getName()));
// 波特率,数据位,停止位 ,无奇偶校验
serialPort.setSerialPortParams(rate,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
}catch(TooManyListenersException e){
throw new RuntimeException(e.getMessage()); //端口监听者过多;
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();//"端口操作命令不支持";
} catch(InterruptedException e){
e.printStackTrace();//延迟出错
}
/* 关闭监听 */
if(time > 0){
this.threadTime = time*1000;
Thread t = new Thread(this);
t.start();
log(String.format("监听程序将在%1$d秒后关闭。。。。", time));
}
}
/**
* @方法名称 :closeRead
* @功能描述 :立即关闭监听
* @返回值类型 :void
* @param time 监听程序时间,单位为秒,0 则是一直监听
* @throws InterruptedException
*/
public void closeRead() {
checkPort();
serialPort.close();
log(String.format("端口'%1$s'监听关闭了!", commPort.getName()));
}
/**
* 日志打印
* @author hongchang.hu
* @function
* @date 2015-2-10 下午2:22:02
* @param @param msg
* @return void
*/
public void log(String msg){
System.out.println(appName+" --> "+msg);
}
/**
* 模拟键盘按键
* @author hongchang.hu
* @function
* @date 2015-2-10 下午2:23:09
* @param
* @return void
* @throws AWTException
* @throws IOException
*/
public void keyboard(String strs) throws AWTException, IOException{
if(strs.equals("up_s")){
sdc.keyPress(robot, KeyEvent.VK_UP); // 上
}else if(strs.equals("up_e")){
sdc.keyRelease(robot, KeyEvent.VK_UP);
}if(strs.equals("down_s")){
sdc.keyPress(robot, KeyEvent.VK_DOWN); // 下
}else if(strs.equals("down_e")){
sdc.keyRelease(robot, KeyEvent.VK_DOWN);
}if(strs.equals("left_s")){
sdc.keyPress(robot, KeyEvent.VK_LEFT); // 左
}else if (strs.equals("left_e")){
sdc.keyRelease(robot, KeyEvent.VK_LEFT);
}if(strs.equals("right_s")){
sdc.keyPress(robot, KeyEvent.VK_RIGHT); // 右
}else if (strs.equals("right_e")){
sdc.keyRelease(robot, KeyEvent.VK_RIGHT);
}else if(strs.equals("shift_s")){
sdc.keyPress(robot, KeyEvent.VK_SHIFT); // SHIFT
}else if (strs.equals("shift_e")){
sdc.keyRelease(robot, KeyEvent.VK_SHIFT);
}else if(strs.equals("ctrl_s")){
sdc.keyPress(robot, KeyEvent.VK_CONTROL); // CTRL
}else if (strs.equals("ctrl_e")){
sdc.keyRelease(robot, KeyEvent.VK_CONTROL);
}
}
/**
* @方法名称 :checkPort
* @功能描述 :检查端口是否正确连接
* @返回值类型 :void
*/
private void checkPort(){
if(commPort == null)
throw new RuntimeException("没有选择端口,请使用 " +"selectPort(String portName) 方法选择端口");
if(serialPort == null){
throw new RuntimeException("SerialPort 对象无效!");
}
}
/**
* 数据接收的监听处理函数
*/
@Override
public void serialEvent(SerialPortEvent arg0){
switch(arg0.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[1024];
String readStr="";//从arduino发送过来的信息
String s2 = "";
try {
while (inputStream.available() > 0) {
inputStream.read(readBuffer);
readStr += new String(readBuffer).trim();
}
keyboard(readStr);//模拟键盘按键
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
@Override
public void run() {
try{
Thread.sleep(threadTime);
serialPort.close();
log(String.format("端口'%1$s'监听关闭了!", commPort.getName()));
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 测试
* @throws AWTException
* */
public static void main(String[] args) throws InterruptedException, AWTException {
SerialCorrespondenceController sp = new SerialCorrespondenceController();
// 列出所有端口
sp.listPort();
//开打相应端口
sp.openPort("COM4");
//设置为一直监听
sp.startRead(0);
// 首次连接后需暂停2秒再继续执行否则数据会有问题
Thread.sleep(2000);
sp.write("我");
//之后发送信息前后间隔至少40ms,否则会将上条返返回信息合并为一条接收(视硬件情况调节)
Thread.sleep(40);
sp.write("你");
Thread.sleep(5000);
sp.write("我们");
}
}</span><span style="font-size:14px;">
</span></span>
3.模拟按键工具类代码:
<span style="font-family:Microsoft YaHei;">package com.fnw.fruitkey;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.KeyEvent;
public class SetKeyDownUtil {
/**
* 测试
* */
public static void main(String[] args) throws Exception{
Robot robot = new Robot(); //创建一个robot对象
Runtime.getRuntime().exec("D:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQ.exe");//打开qq
robot.delay(2000); //等待 2秒
//窗口最大化
keyPressWithAlt(robot, KeyEvent.VK_SPACE); //按下 alt+ 空格
keyPress(robot, KeyEvent.VK_X); //按下x键
keyPress(robot, KeyEvent.VK_ENTER); // 按下 enter 换行
robot.delay(2000); //等待 2秒
keyPressWithCtrl(robot,KeyEvent.VK_A); //按下 ctrl+A 全选
robot.delay(2000); //等待 2秒
keyPress(robot,KeyEvent.VK_DELETE); //清除
}
// shift+ 按键
public static void keyPressWithShift(Robot r, int key) {
r.keyPress(KeyEvent.VK_SHIFT);
r.keyPress(key);
r.keyRelease(key);
r.keyRelease(KeyEvent.VK_SHIFT);
r.delay(100);
}
// ctrl+ 按键
public static void keyPressWithCtrl(Robot r, int key) {
r.keyPress(KeyEvent.VK_CONTROL);
r.keyPress(key);
r.keyRelease(key);
r.keyRelease(KeyEvent.VK_CONTROL);
r.delay(100);
}
// alt+ 按键
public static void keyPressWithAlt(Robot r, int key) {
r.keyPress(KeyEvent.VK_ALT);
r.keyPress(key);
r.keyRelease(key);
r.keyRelease(KeyEvent.VK_ALT);
r.delay(100);
}
//打印出字符串
public static void keyPressString(Robot r, String str){
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();//获取剪切板
Transferable tText = new StringSelection(str);
clip.setContents(tText, null); //设置剪切板内容
keyPressWithCtrl(r, KeyEvent.VK_V);//粘贴
r.delay(100);
}
//单个按键按下
public static void keyPress(Robot r,int key){
r.keyPress(key);
//r.delay(100);
}
//单个按键抬起
public static void keyRelease(Robot r,int key){
r.keyRelease(key);
//r.delay(100);
}
}
</span>
<span style="font-family:Microsoft YaHei;">int buzzer = 12;//蜂鸣器管脚
int LED=7;//LED管脚
int UP = 10;
int DOWN = 8;
int LEFT = 6;
int RIGHT = 4;
int CTRL = 2;
int SHIFT = 12;
/**
* 初始化
*/
void setup() {
//pinMode(buzzer, OUTPUT);
pinMode(7,OUTPUT);
Serial.begin(9600);
}
/**
* 主循环体
*/
void loop() {
int up = readCapacitivePin(UP);
int down = readCapacitivePin(DOWN);
int left = readCapacitivePin(LEFT);
int right = readCapacitivePin(RIGHT);
int ctrl = readCapacitivePin(CTRL);
int shift = readCapacitivePin(SHIFT);
if (up > 2) {
Serial.println("up_s");//按下
}else{
Serial.println("up_e");//抬起
}
delay(50);
if (down > 2) {
Serial.println("down_s");
}else{
Serial.println("down_e");
}
delay(50);
if (left > 2) {
Serial.println("left_s");
}else{
Serial.println("left_e");
}
delay(50);
if (right > 2) {
Serial.println("right_s");
}else{
Serial.println("right_e");
}
delay(50);
if (ctrl > 2) {
Serial.println("ctrl_s");
}else{
Serial.println("ctrl_e");
}
delay(50);
if (shift > 2) {
Serial.println("shift_s");
}else{
Serial.println("shift_e");
}
delay(50);
}
/*
* 读取管脚电容方法
*/
uint8_t readCapacitivePin(int pinToMeasure) {
volatile uint8_t* port;
volatile uint8_t* ddr;
volatile uint8_t* pin;
/*
输入Arduino 端口号 到 AVR 的PORT, PIN, DDR 寄存器
DDR寄存器用来控制数据的方向,1为数据出,0为数据入。
PORT就跟51的Px口一样,直接读或直接写。
PIN是用来读出io口的逻辑电平值。不能对它写操作
*/
byte bitmask;
port = portOutputRegister(digitalPinToPort(pinToMeasure)); //直接读或直接写。
ddr = portModeRegister(digitalPinToPort(pinToMeasure));//用来控制数据的方向,1为数据出,0为数据入。
pin = portInputRegister(digitalPinToPort(pinToMeasure));//读出io口的逻辑电平值。不能对它写操作
bitmask = digitalPinToBitMask(pinToMeasure); //位掩码
// 设置下拉一个输出 (就是按下的时候)
*port &= ~(bitmask);
*ddr |= bitmask;
delay(1);
// 设置上拉一个输入(就是抬起的时候)
*ddr &= ~(bitmask);
*port |= bitmask;
// 检测下拉值
uint8_t cycles = 17;
if (*pin & bitmask) { cycles = 0;}
else if (*pin & bitmask) { cycles = 1;}
else if (*pin & bitmask) { cycles = 2;}
else if (*pin & bitmask) { cycles = 3;}
else if (*pin & bitmask) { cycles = 4;}
else if (*pin & bitmask) { cycles = 5;}
else if (*pin & bitmask) { cycles = 6;}
else if (*pin & bitmask) { cycles = 7;}
else if (*pin & bitmask) { cycles = 8;}
else if (*pin & bitmask) { cycles = 9;}
else if (*pin & bitmask) { cycles = 10;}
else if (*pin & bitmask) { cycles = 11;}
else if (*pin & bitmask) { cycles = 12;}
else if (*pin & bitmask) { cycles = 13;}
else if (*pin & bitmask) { cycles = 14;}
else if (*pin & bitmask) { cycles = 15;}
else if (*pin & bitmask) { cycles = 16;}
*port &= ~(bitmask);
*ddr |= bitmask;
return cycles;
}</span>