水果做键盘控制跑跑卡丁车

来源:互联网 发布:暴风云微交易源码 编辑:程序博客网 时间:2024/04/27 19:00

视频地址: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,heightsetContentPane(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 对象无效!");}}/** * 数据接收的监听处理函数 */@Overridepublic 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());            } }}@Overridepublic 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>


arduino 板代码:

<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>






0 0
原创粉丝点击