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

2 篇文章 0 订阅
2 篇文章 0 订阅

视频地址: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>


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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值