I have built a mouse emulator for a severely disabled person, which has a series of buttons arranged in a + pattern, with the center button being a 'click'. The inputs are read into an Arduino Micro, which then connects through USB to the computer. A processing script, using the Arduino library, responds to these changes by moving the mouse, using the Robot class. I exported an application, for both 32-bit and 64-bit windows, but found that the .exe files were problematic, and instead opted for .bat files using the embedded Java.
This all works perfectly on my computer (Microsoft Surface, Windows 8, 64-bit). But on the client's computer (Windows 7, 32-bit), the results are erratic. Each time I load the program, I get a different result. For example, sometimes up, down, and click work. Sometimes only up and click work. And so forth. The click usually seems to work, but the directions are less reliable, with vertical working more often than horizontal.
I have verified that nothing is wrong with the board. I tried putting in a debug statement which ran println() each time a movement's "if" statement was tripped - I only got printing from directions that worked on that particular run.
I see a number of potential sources for this problem: the differences between 32-bit and 64-bit Windows, possible issues with the Robot class, USB performance issues (though I've tried multiple ports on the client's laptop), etc. The client's laptop has generally poor performance (for example, if I click "Control Panel", it may be a full minute before Control Panel opens), so that may also be related.
Does anyone have other insights on this?
EDIT: As requested, here's a minimal version of the Processing code. The Arduino is just running Firmata:
import processing.serial.*;
import cc.arduino.*;
import java.awt.*;
import java.awt.event.*;
Arduino arduino;
Robot r;
final int upPin=8;
final int downPin=12;
final int leftPin=10;
final int rightPin=11;
final int buttonPin=9;
final int SPEED=8;
boolean arduinoConnected;
int prevButton=Arduino.LOW;
void setup() {
arduinoConnected = (Arduino.list().length>0);
if (arduinoConnected) {
arduino = new Arduino(this, Arduino.list()[Arduino.list().length-1], 57600);
arduino.pinMode(buttonPin, Arduino.INPUT);
arduino.pinMode(upPin, Arduino.INPUT);
arduino.pinMode(downPin, Arduino.INPUT);
arduino.pinMode(leftPin, Arduino.INPUT);
arduino.pinMode(rightPin, Arduino.INPUT);
}
try {
r = new Robot();
}
catch (Exception e) {
e.printStackTrace();
}
}
void draw() {
arduinoConnected = (Arduino.list().length>0);
if (arduinoConnected && arduino==null) setup();
if (arduinoConnected) {
if (arduino.digitalRead(upPin)==Arduino.HIGH) moveMouse(0,0-SPEED);
if (arduino.digitalRead(downPin)==Arduino.HIGH) moveMouse(0,SPEED);
if (arduino.digitalRead(leftPin)==Arduino.HIGH) moveMouse(0-SPEED,0);
if (arduino.digitalRead(rightPin)==Arduino.HIGH) moveMouse(SPEED,0);
int button=arduino.digitalRead(buttonPin);
if (button==Arduino.LOW && prevButton==Arduino.HIGH) clickMouse(InputEvent.BUTTON1_DOWN_MASK);
prevButton=button;
}
}
void moveMouse(int x, int y) {
PointerInfo pinfo = MouseInfo.getPointerInfo();
Point p = pinfo.getLocation();
r.mouseMove((int)p.getX()+x, (int)p.getY()+y);
}
void clickMouse(int whichType) {
r.mousePress(whichType);
r.mouseRelease(whichType);
r.waitForIdle();
}
Related
Recently I've been trying to use a wired Xbox 360 controller to interface with my Arduino Uno (via Processing) that I'm using in a test circuit to control two brushed motors. I came across this video that uses the library to control a single servo motor along with videos made by the library's author. I made a few modifications to the code (from the first link) just to support the two motors, and it works (sort of). The only problem is that it accepts input from my Bluetooth mouse instead of the Xbox controller which are both connected to my laptop's USB ports. I set the text configuration file so that "Button 0" and "Button 2" -which correspond to the A and X buttons respectively- make two pins on the Arduino go high which feed into the bases of two transistors that control the motors. Instead, the left mouse button and scroll wheel button on my Bluetooth mouse controls these outputs.
I'm a bit confused about why this is happening and I've tried a few different things to resolve the issue. I thought that when creating the configuration file (with a program that comes with the library) I accidentally choose my mouse as the input device, so I made another configuration file just to make sure that this wasn't the case, though I'm not exactly sure what in the configuration file indicates the correct device to use. Maybe I'm missing something extremely obvious, I just know that I need a second set of eyes to look things over for me. If you have experience with this library your help would be much appreciated, thank you.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
Configuration File:
Tests the xbox controller with motors.
lMotor Left Motor 1 BUTTON Button 0 0 0.0 0.0
rMotor Right Motor 1 BUTTON Button 2 0 0.0 0.0
/////////////////////////////////////////////////////////////////////////////////////////////////////////
Java from Processing:
import processing.serial.*;
import net.java.games.input.*;
import org.gamecontrolplus.*;
import org.gamecontrolplus.gui.*;
import cc.arduino.*;
import org.firmata.*;
ControlDevice cont;
ControlIO control;
Arduino arduino;
//variables for the "A Button" and "X Button" on the xbox controller
ControlButton aButton;
ControlButton xButton;
//needed variables of type int for background function to work (change window color when buttons are pressed)
int aInt;
int xInt;
void setup() {
size(360, 200);
control = ControlIO.getInstance(this);
cont = control.getMatchedDevice("xboxtest");
if(cont == null) {
println("Error, something went wrong");
System.exit(-1);
}
//println(Arduino.list());
arduino = new Arduino(this, Arduino.list()[0], 57600);
arduino.pinMode(8, Arduino.OUTPUT);
arduino.pinMode(11, Arduino.OUTPUT);
}
//gets the input and is called in the looping function (void draw)
public void getUserInput() {
//rMotor and lMotor are references to the configuration file
aButton = cont.getButton("lMotor");
xButton = cont.getButton("rMotor");
aInt = 0;
xInt = 0;
if (aButton.pressed() == true) {
aInt = 1;
}
if (xButton.pressed() == true) {
xInt = 1;
}
}
void draw() {
getUserInput();
//changes the color of the interactive window when the input changes
background(100 * aInt, 100, 100 * xInt);
arduino.digitalWrite(8, aInt * 255);
arduino.digitalWrite(11, xInt * 255);
}
UPDATE: I was able to fix the problem after looking at some example code provided by the library that I was previously unaware of. In my defense, there is little support for this library and the video that I watched didn't use the line of code that I found in the example. I hope that this benefits someone in the future.
I ended up changing this:
cont = control.getMatchedDevice("xboxtest");
To this:
cont = control.filter(GCP.GAMEPAD).getMatchedDevice("xboxtest");
I got used to my touch pad, that allows to scroll smoothly and very exactly, but I can not to simulate it by Java robot - mousewheel is getting only integer parameters and a scrolling carried by steps. Can I simulate smoothly scrolling in Java?
robot.mouseWheel(a); // int a
The unit of scrolls will always be by "notches of the wheel" (before you ask: that's how the measurement is named in the docs). This is simply how it's implemented by the hardware (to be more specific: the mouse). How many pixels are scrolled per "notch" is nothing but OS-configuration. You can't mess with that with pure java and I wouldn't recommend it, even if it was possible.
What you can do nevertheless is to slow down the speed at which the robot scrolls:
import java.awt.Robot;
public class Test{
public static void main(String[] args)
throws Exception
{
//time to switch to a specific window where the robot ought to be tested
try{ Thread.sleep(2000); }catch(InterruptedException e){}
Robot r = new Robot();
for(int i = 0; i < 20; i++){
//scroll and wait a bit to give the impression of smooth scrolling
r.mouseWheel(1);
try{ Thread.sleep(50); }catch(InterruptedException e){}
}
}
}
Using Forge 1.8.9 in Eclipse (Mars.1 Release (4.5.1)) in local development environment.
The goal is to set a player's location to a predetermined xyz every time they join (or re-join) a world. So if they quit the game, but then come back to the world, they will start in the same location as determined by the code below instead of wherever they left off. Basically, it will work like a lobby, where players start in the same place each time.
The code works using the chat component (e.g. the chat message appears upon joining the game) but I've commented it out, for now. The player simply appears wherever they left off after having left the game from last time around.
Questions are:
1. is the PlayerLoggedInEvent the best event to use, or is there a better event?
2. is setLocationAndAngles the best to use, or should a different set location type event (or move) better?
Thanks in advance. Lots of experience with LAMP stack, but Java and mods are a new interest (obvs). Code is below.
import net.minecraftforge.client.event.RenderPlayerEvent;
//import net.minecraft.util.ChatComponentText;
//import net.minecraft.util.EnumChatFormatting;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
//import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class JoinGameLocation {
#SubscribeEvent
public void SpawningLocation(PlayerLoggedInEvent event){
event.player.setLocationAndAngles(145, 72, 145, 0, 0);
//-----This works when uncommented
//event.player
//.addChatMessage(
// new ChatComponentText(
// EnumChatFormatting.RED + "You joined the game"));
//event.world.setWorldTime(0);
int ticks = 0;
double good_x = 145;
double good_y = 72;
double good_z = 145;
event.player.setLocationAndAngles(good_x, good_y, good_z, 0, 0);
}
}
Use the entity join world and the clone event
#SubscribeEvent
public void onClonePlayer(PlayerEvent.Clone event) {
}
#SubscribeEvent
public void onEntityJoinWorld(EntityJoinWorldEvent event) {
if (event.entity != null && event.entity instanceof EntityPlayer && !event.entity.worldObj.isRemote) {
}
}
The clone event is triggered upon tp, dimension change, etc...
The join world obviously when the world is joined. I suggest you play around with these.
My project is using Processing core jar and the GSVideo library on a OSX 10.8.5 using Eclipse.
I cannot get GSVideo jump(int frame) or jump(float time) to actually redraw the next frames. The image displayed toggles back and forth between frames when I repeatedly press the RIGHT to advance the frame in the example program below. Because the example below works with *.mov, but not *.mpg video I want to ask if there are any known problems with gstreamer advancing frames in MPEG2 video. Or perhaps something's up with either java-gstreamer or GSVideo?
I'm working with video in MPEG2 format.. And there is no problem just to play and pause the MPEG2. It just seems that movie.jump(frameNum or time) functions are not working. I've started looking for an example of frame stepping using playbin2's seek method.
Here is info about the video I'm trying to jump.
stream 0: type: CODEC_TYPE_VIDEO; codec: CODEC_ID_MPEG2VIDEO; duration: 7717710; start time: 433367; timebase: 1/90000; coder tb: 1001/60000;
width: 1920; height: 1080; format: YUV420P; frame-rate: 29.97;
The example code.
import processing.core.*;
import codeanticode.gsvideo.*;
public class FramesTest extends PApplet {
GSPlayer player;
GSMovie movie;
int newFrame = 0;
PFont font;
public void setup() {
size(320, 240);
background(0);
//movie = new GSMovie(this, "station.mov"); // sample works
movie = new GSMovie(this, "myMovie.mpg"); // mpg does not
movie.play();
movie.goToBeginning();
movie.pause();
textSize(24);
}
public void movieEvent(GSMovie movie) {
System.out.println("movie"+ movie.frame());
movie.read();
}
public void draw() {
image(movie, 0, 0, width, height);
fill(240, 20, 30);
text(movie.frame() + " / " + (movie.length() - 1), 10, 30);
}
public void keyPressed() {
if (movie.isSeeking()) return;
if (key == CODED) {
if (keyCode == LEFT) {
if (0 < newFrame) newFrame--;
} else if (keyCode == RIGHT) {
if (newFrame < movie.length() - 1) newFrame++;
}
}
movie.play();
movie.jump(newFrame);
movie.pause();
if (movie.available()){
System.out.println(movie.frame());
movie.read();
}
System.out.println(newFrame);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
PApplet.main(new String[] { FramesTest.class.getName() }); //
}
}
The example code was pulled from here...
http://gsvideo.sourceforge.net/examples/Movie/Frames/Frames.pde
I've searched the internet for a few days and this attempted contact with this forum as well...
https://sourceforge.net/projects/gsvideo/forums
This post seems similar but my problem is not playing (that's fine). I cannot jump to a specific frame.... GStreamer: Play mpeg2
Many thanks to the SO community for any help I might receive.
Update:
To work around the MPEG2 compression issue (described by v.k. below) I am trying to create a gstreamer pipeline to do on-the-fly transcoding to mp4 using either GSVideo Pipeline or with java-gstreamer. The command below works in Ubuntu.
gst-launch-0.10 filesrc location=myMpeg2Video.mpg ! mpegdemux name=demux demux.video_00 ! ffdec_mpeg2video ! queue ! x264enc ! ffdec_h264 ! xvimagesink
But the following GSVideo Pipeline displays an empty gray window :(
pipeline = new GSPipeline(this, "filesrc location=file:/path/movie.mpg ! mpegdemux name=demux demux.video_00 ! ffdec_mpeg2video");
pipeline.play();
as v.k. pointed out, seeking is in general not accurate.
One important thing to note is that development on gsvideo has basically stopped. The main elements of it were ported to the built-in video library in Processing 2.0. I did some work in built-in video to try to improve seeking, and the example Frames in Libraries|video|Movie shows how (to try) to jump to specific frames by indicating a time value. Maybe this helps in your case?
Also if you find a more accurate way of doing seeking as you suggest in your last post, I could include that in video library.
I wrote a bot for controlling racing game using Java Robot. The bot works well for Need For Speed Underground except for key "Down" (Up, Left, Right keys work very well). However my bot can't control Need For Speed Most Wanted.
The bot works fine, but the Game doesn't accept the simulated key events. I did some searching, and found the game is DirectX based. In DirectX the keyboard/mouse events are special. It seems that the game "asks" the keyboard directly, not through Windows. And I try my program in CS, and found it works pretty well.
I program in Windows 7, using Eclipse and Java 1.6. So I want to ask why doesn't Need for Speed Most Wanted accept the simulated key events and how to solve this program? Thank you.
I wrote a bot for controlling racing
game using Java Robot.
What KeyEvents to you generate?
For Java Robot's simulated key events
like "VK_Up,
VK_Down,Vk_Left,VK_Right", the "Need
for Speed Most Wanted" ignored. But,
for keys "A-Z", the game accepted!
Maybe you are trying to generate keyTyped events when you should be using keyPressed and keyReleased?
Here is a simple example that works with the right/left/up down keys. Try entering (1, 2, 3) then backspace to the beginning and enter (0). Then press the playback button.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class RobotPlayback extends JFrame implements KeyListener, ActionListener
{
JTextField textField1;
JTextField textField2;
List playback = new ArrayList();
public RobotPlayback()
{
textField1 = new JTextField(30);
textField1.addKeyListener( this );
getContentPane().add(textField1, BorderLayout.NORTH);
//
JButton button = new JButton("Playback");
button.addActionListener( this );
button.setFocusable(false);
getContentPane().add(button);
//
textField2 = new JTextField(30);
getContentPane().add(textField2, BorderLayout.SOUTH);
}
public void keyPressed(KeyEvent e)
{
playback.add(e);
}
public void keyReleased(KeyEvent e)
{
playback.add(e);
}
public void keyTyped(KeyEvent e) {}
public void actionPerformed(ActionEvent e)
{
Thread playback = new Thread()
{
public void run()
{
playback();
}
};
playback.start();
}
private void playback()
{
textField2.requestFocus();
try
{
Robot robot = new Robot();
robot.setAutoDelay( 200 );
for (int i = 0; i < playback.size();i++)
{
KeyEvent event = (KeyEvent)playback.get(i);
if (event.getID() == KeyEvent.KEY_PRESSED)
robot.keyPress( event.getKeyCode() );
else
robot.keyRelease( event.getKeyCode() );
}
}
catch(Exception exc)
{
System.out.println(exc);
}
setVisible(true);
playback = new ArrayList();
textField1.requestFocus();
}
public static void main(String[] args)
throws Exception
{
JFrame frame = new RobotPlayback();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
I registered the account just in this
morning and 11 reputations only. Not
enough for upvoting
Even with 11 points you should be able to "accept" an answer if it answers your question.
As far as I understand, DirectX doesn't rely on the events generated by Windows to receive input from the keyboard. Thus, firing simulated events won't work for you. I don't think you can do what you want.
The Java Robot has a bug when it comes to arrow keys. It will be pressing the num pad arrows. Currently, it is not possible to press the non num pad arrows.
Ref: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4838497
Though this is an old question, I'll post my answer in case it helps someone out later.
I recently faced the same issue. I was calling keyPress and keyRelease right after the other for each of the left, right, up and down keys, based on some external input. I tried a bunch of games, but the Robot key presses didn't work in any of them. When I tried the same code with the cursor in a text file, the cursor moved correctly.
The problem was that games typically require continuous key presses, and so the keyPress+keyRelease combination didn't cause any effect. So I fixed the issue by calling keyRelease in the opposite direction for each arrow key press.
For instance, if the left arrow key needed to be pressed, I call
keyRelease(KeyEvent.VK_RIGHT);
keyPress(KeyEVent.VK_LEFT);
This keeps the left key pressed till the right arrow key eventually needs to be pressed. There are more optimal ways to do this by tracking the last key press, and so on, but this method seems to work just fine.
I know this is a very old post but my answer might be helpful for someone who is looking for answer
Answer:-
Its because some games uses DirectInput API for reading keyboard scan codes rather than virtual key codes. As java.awt.Robot performs keystrokes via virtual key codes not scan codes therefore keyboard events simulated using java.awt.Robot may have not effect on the game. You have to make Windows native call from your java application using JNI/JNA.
code is available here https://github.com/umer0586/winKeyboad
Keyboard keyboard = new Keyboard();
keyboard.winKeyPress(ScanCode.DIK_UP);
//Thread.sleep(1000);
keyboard.winKeyRelease(ScanCode.DIK_UP);