gyroGun prototype using MouseTo library (and some others...)

Hi all,

first thing first: thank you for reading this.

I am trying to write a sketch in order to control mouse position on the screen while moving an accelerometer/ giroscope module. The module is a common MPU6050.

The target application is a sort of "lightgun" (or should i call it an accelGun or gyroGun) for MAME/arcade games: if i move/tilt the "gun", the mouse moves accordingly to a specific position.

I found this great MouseTo library which could be used for absolute mouse movements. I wrote down a simple sketch (with the help of other libraries and sheets of code found here and there) and this is my result up to now:

#include <MouseTo.h>
#include <Wire.h>
#include <I2Cdev.h>
#include <MPU6050.h>
#include <Mouse.h>

#define ZERO  6

MPU6050 mpu;
int16_t ax, ay, az, gx, gy, gz, axZero, ayZero, azZero;

void setup() {
  pinMode(ZERO, INPUT_PULLUP);
  digitalWrite(ZERO, HIGH);  
  Wire.begin();
  Mouse.begin();
  MouseTo.setCorrectionFactor(1);
  MouseTo.setScreenResolution(1366, 768);
  mpu.initialize();
  if (!mpu.testConnection()) {
    while (1);
  }
}

void loop() {

  int zeroState = digitalRead(ZERO);
  int xRes = MouseTo.getScreenResolutionX();
  int yRes = MouseTo.getScreenResolutionY();
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  int vx = map(ax-axZero, -16000, 16000, xRes, 0);
  int vy = map(ay-ayZero, -16000, 16000, yRes, 0);

  if (zeroState== HIGH) {
  MouseTo.setTarget(vx, vy);
  while (MouseTo.move() == false) {}
  }

  if (zeroState == LOW){
    axZero = ax;
    ayZero = ay;
  }

  delay(20);
}

This sketch kinda of works, but the mouse pointer follows "square" trajectories (starting from the 0,0 coordinate of the screen (upper left corner) to the final position instead of remaining in the latest position get (homing in the latest position) ...

any help would be much appreciated!

Hi, the way I wrote the library is that the mouse pointer is returned to 0,0 before each move. This is because the mouse movements are done relatively so to move to an absolute coordinate I need to know the starting coordinates. It could be possible to "home" the mouse only once instead of before each movement but if the pointer was moved using the regular mouse connected to the computer then the actual position will no longer be known. The movements are made one axis at a time so that's why it follows "square" trajectories. I found the movements are more accurate if you only move one axis at a time, rather than diagonally.

Please give more information on why the behavior of the library is not ideal for your application. Is it that the homing process makes the mouse movements too slow?

Hi pert, thanks for the reply. The application I am targetting is a sort of lightgun to be used with arcade shooters, so the movement should not start from 0, 0 every time i move the gyro. In addition, once the position is reached, the pointer returns immediately at 0, 0... it should stay in position (i know this is more a coding issue, but any help would be much appreciated).

Ok, I've made homing (move to 0,0) the mouse before moving to a new target coordinate optional. Please update to the latest version of the MouseTo library and then try this code that I've modified to not home:

#include <MouseTo.h>
#include <Wire.h>
#include <I2Cdev.h>
#include <MPU6050.h>
#include <Mouse.h>

#define ZERO  6

MPU6050 mpu;
int16_t ax, ay, az, gx, gy, gz, axZero, ayZero, azZero;

void setup() {
  pinMode(ZERO, INPUT_PULLUP);
  digitalWrite(ZERO, HIGH);
  Wire.begin();
  Mouse.begin();
  MouseTo.setCorrectionFactor(1);
  MouseTo.setScreenResolution(1366, 768);
  mpu.initialize();
  if (!mpu.testConnection()) {
    while (1);
  }

  // Home the mouse pointer
  MouseTo.setTarget(0, 0);
  while (MouseTo.move() == false) {}
}

void loop() {

  int zeroState = digitalRead(ZERO);
  int xRes = MouseTo.getScreenResolutionX();
  int yRes = MouseTo.getScreenResolutionY();
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  int vx = map(ax - axZero, -16000, 16000, xRes, 0);
  int vy = map(ay - ayZero, -16000, 16000, yRes, 0);

  if (zeroState == HIGH) {
    MouseTo.setTarget(vx, vy, false);  // set new target coordinate without homing
    while (MouseTo.move() == false) {}
  }

  if (zeroState == LOW) {
    axZero = ax;
    ayZero = ay;
  }

  delay(20);
}

Let me know how that works for you. This will only home the mouse pointer once in setup(). That means if the regular mouse connected to the computer is moved after that point then the library will no longer accurately move the mouse to the correct coordinates. There is also a possibility it will drift slightly over time. I'm not sure how critical the mouse pointer accurately reaching the exact target coordinates is in your application. Maybe you can add some code to home the mouse at other convenient times if necessary.

Barito:
In addition, once the position is reached, the pointer returns immediately at 0, 0... it should stay in position (i know this is more a coding issue, but any help would be much appreciated).

The MouseTo library definitely shouldn't do that. Once the pointer reaches the target coordinate it should not move any more until a new target coordinate is set via MouseTo.setTarget(). So is this problem caused by the value received from the MPU6050 causing the code to do a MouseTo.setTarget(0,.0)?

Just updated the library: works as I would expect now! Thank you very much!
Unfortunately my solution is not good enough for the application: the accelerometer is too sensitive and keeping by hand the "gun" makes the crosshatch to shake too much... not sure if it's possible to reduce the sensitivity of such modules via software to have a more smooth movement...

You can definitely add some code to do that. I suspect part of the problem is that you are doing the mouse movement in a blocking way with that while loop. Give this code a try:

#include <MouseTo.h>
#include <Wire.h>
#include <I2Cdev.h>
#include <MPU6050.h>
#include <Mouse.h>

#define ZERO  6

const unsigned int xRes = 1366;
const unsigned int yRes = 768;

MPU6050 mpu;
int16_t ax, ay, az, gx, gy, gz, axZero, ayZero, azZero;

void setup() {
  pinMode(ZERO, INPUT_PULLUP);
  digitalWrite(ZERO, HIGH);
  Wire.begin();
  Mouse.begin();
  MouseTo.setCorrectionFactor(1);
  MouseTo.setScreenResolution(xRes, yRes);
  mpu.initialize();
  if (!mpu.testConnection()) {
    while (1);
  }

  // Home the mouse pointer
  MouseTo.setTarget(0, 0);
  while (MouseTo.move() == false) {}
}

void loop() {
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

  if (digitalRead(ZERO) == HIGH) {
    int vx = map(ax - axZero, -16000, 16000, xRes, 0);
    int vy = map(ay - ayZero, -16000, 16000, yRes, 0);
    MouseTo.setTarget(vx, vy, false);  // set new target coordinate without homing
    MouseTo.move();
  }
  else {
    axZero = ax;
    ayZero = ay;
  }
}

It will constantly poll the MPU6050 and update the target coordinates accordingly instead of waiting for the mouse movement to complete before the next target coordinate is set.

I foresaw an issue with the above code caused by the library completing movement on the X axis before starting movement on the Y axis. I have changed the library to alternate axes now so please update to the latest version of MouseTo before testing the above code.