Example: Accelerometer Mouse

The circuit below uses an Adafruit accelerometer breakout to read movements to control the mouse.   If you tilt the mouse, it moves the cursor.  The momentary buttons serve as the right and left mouse buttons.

The accelerometer samples movement.  Keeping the project flat with respect to the ground, tilting the mouse forward and back moves the cursor in the x direction (horizontal) while turning your wrist around the axis of the USB cable moves it in the y direction (vertical). Two external pushbuttons are used for right and left click. This project uses the half Perma-Proto which fits in an altoids-sized tin. Adafruit carries a nice mint tin-sized board that would work well also.

Build

With the Perma-Proto board, you can use male header to solder into the board then solder onto the Pro Trinket.  I prefer, when possible, to cut some female header into two pieces of 12 to solder onto the board, making a socket for the Pro Trinket. This will make the Pro Trinket sit a bit close to the top of an Altoid tin if you use it as a case. An 8 pin piece of female header may also be soldered onto the circuit board to plug in the accelerometer.  Once the headers are on, you can use hookup wire to make the power and signal connections shown.  The I2C wires of the sensor are connected to A4 and A5 on the Pro Trinket which are the SDA and SCL lines.

Be sure to insulate the bottom (and maybe the top) of the mint tin so electrical connections are not shorted by the metal case.

The code for using the accelerometer with the Pro Trinket and two momentary buttons as a mouse is below. Be sure you have loaded the Pro Trinket Mouse library from the first page of the tutorial.

Download: file
/**************************************************************************/
/*!
    @file     ProTrinketMouseAccel.ino
    @author   Mike Barela for Adafruit Industries
    @license  BSD (see license.txt)

    This is an example of using the Adafruit Pro Trinket with the Adafruit MMA8451 
    Accelerometer breakout board to make a mouse using motion gestures

    Adafruit invests time and resources providing this open source code,
    please support Adafruit and open-source hardware by purchasing
    products from Adafruit!

    @section  HISTORY

    v1.0  - First release 1/25/2015  Mike Barela
*/
/**************************************************************************/

#include <Wire.h>               // I2C Library
#include <Adafruit_MMA8451.h>   // Accelerometer library
#include <Adafruit_Sensor.h>    // Adafruit sensor library
#include <ProTrinketMouse.h>    // Pro Trinket V-USB mouse emulator
#define  DEBUG  0               // Set to 1 for serial console debugging, 0 otherwise

const uint8_t LEFTBUTTON  = 8;  // Left Mouse Button on this Pro Trinket Pin
const uint8_t RIGHTBUTTON = 9;  // Right Mouse Button on Pro Trinket

Adafruit_MMA8451 mma = Adafruit_MMA8451();  // Create an accelerometer object

//Change these values if accelerometer reading are different:
//How far the accerometer is tilted before
//starting to move the mouse:
const int MovementThreshold = 18;

//The average zero acceleration values read
//from the accelerometer for each axis:
const int ZeroXValue = 0;
const int ZeroYValue = 0;
//const int ZeroZValue = 0;

//The maximum (positive) acceleration values read
//from the accelerometer for each axis:
const int MaxXValue = 4096;
const int MaxYValue = 4096;
//const int MaxZValue = 4096;

//The minimum (negative) acceleration values read
//from the accelerometer for each axis:
const int MinXValue = -4096;
const int MinYValue = -4096;
//const int MinZValue = -4096;

//The sign of the mouse movement relative to the acceleration.
//If your cursor is going in the opposite direction you think it
//should go, change the sign for the appropriate axis.
const int XSign = 1;
const int YSign = 1;
//const int ZSign = 1;

//The maximum speed in each axis (x and y)
//that the cursor should move. Set this to a higher or lower
//number if the cursor does not move fast enough or is too fast.
const int MaxMouseMovement = 50;  

//This reduces the 'twitchiness' of the cursor by calling
//a delay function at the end of the main loop.
//There are better way to do this without delaying the whole
//microcontroller, but that is left for another tutorial or project.
const int MouseDelay = 12;
  
void setup(void) {
#if DEBUG  
  Serial.begin(9600);
  Serial.println("Pro Trinket Accelerometer Mouse");
#endif  
  if (! mma.begin()) {  // If the accelerometer cannot be found, flash LED
    pinMode(13, OUTPUT);
    while (1) {         // Flash the Pin 13 LED quickly to indicate an error
      digitalWrite(13, HIGH);
      delay(350);
      digitalWrite(13, LOW);
      delay(350);
    }
  }
  mma.setRange(MMA8451_RANGE_2_G);  // 2G Mode is best for hand gestures
  mma.read();                       // get an initial read 
  
  TrinketMouse.begin();               // Initialize mouse library
  pinMode(LEFTBUTTON,  INPUT_PULLUP); // Left and right mouse button pins initialized
  pinMode(RIGHTBUTTON, INPUT_PULLUP); //   with internal pullup resistors (bring Low with button)
}

void loop() {
  
  mma.read();  //   // Read the 'raw' data in 14-bit counts
#if DEBUG
  Serial.print("X:\t"); Serial.print(mma.x); 
  Serial.print("\tY:\t"); Serial.print(mma.y); 
  Serial.print("\tZ:\t"); Serial.println(mma.z); 
#endif

  processAccelerometer(mma.x,mma.y, mma.z);  // Work with the read data
  
  delay(MouseDelay);  // wait until next reading - was 500 in Adafruit example
}

//Function to process the acclerometer data
//and send mouse movement information via USB
void processAccelerometer(int16_t XReading, int16_t YReading, int16_t ZReading)
{
  //Initialize values for the mouse cursor movement.
  int16_t MouseXMovement = 0;
  int16_t MouseYMovement = 0;
  
  //Calculate mouse movement
  //If the analog X reading is ouside of the zero threshold...
  if( MovementThreshold < abs( XReading - ZeroXValue ) )
  {
    //...calculate X mouse movement based on how far the X acceleration is from its zero value.
    MouseXMovement = XSign * ( ( ( (float)( 2 * MaxMouseMovement ) / ( MaxXValue - MinXValue ) ) * ( XReading - MinXValue ) ) - MaxMouseMovement );
    //it could use some improvement, like making it trigonometric.
  }
  else
  {
    //Within the zero threshold, the cursor does not move in the X.
    MouseXMovement = 0;
  }

  //If the analog Y reading is ouside of the zero threshold... 
  if( MovementThreshold < abs( YReading - ZeroYValue ) )
  {
    //...calculate Y mouse movement based on how far the Y acceleration is from its zero value.
    MouseYMovement = YSign * ( ( ( (float)( 2 * MaxMouseMovement ) / ( MaxYValue - MinYValue ) ) * ( YReading - MinYValue ) ) - MaxMouseMovement );
    //it could use some improvement, like making it trigonometric.
  }
  else
  {
    //Within the zero threshold, the cursor does not move in the Y.
    MouseYMovement = 0;
  }
 
  if(digitalRead(LEFTBUTTON) == LOW) {             // If left button pressed
#if DEBUG
    Serial.println("Left Mouse Button");
#endif
    TrinketMouse.move(0,0,0,MOUSEBTN_LEFT_MASK);   //  tell PC
  }
  else if (digitalRead(RIGHTBUTTON) == LOW) {      // If right button pressed
#if DEBUG
    Serial.println("Right Mouse Button");
#endif
    TrinketMouse.move(0,0,0,MOUSEBTN_RIGHT_MASK);  //  tell PC 
  }
  else {
    TrinketMouse.move(MouseXMovement, MouseYMovement, 0, 0);  // otherwise just move mouse
  }

}

Why not use direct accelerometer movements?

You might be tempted to just read the X and Y accelerometer values and use this to map to screen movements rather than tilt.  I tried, others also.  The thing is that 1) you have to use jerky movements to get accelerations, and  2) "down" will always produce a gravity acceleration of 9.8 meters per second squared.  If "down" changes from the Z axis, you'll get measurements in X and Y which make the mouse skew off to the screen edge.

Can the effects of gravity be subtracted out?  Not just with an accelerometer.  If you get a 9 or 10 degree of freedom (DOF) sensor package, like the ones used for quadcopters and other movable items, there is a math-intensive way to get the movement translated without gravity.  See the Adafruit tutorial AHRS for Adafruit's 9-DOF, 10-DOF, LSM9DS0 Breakouts for details.

This guide was first published on Jan 25, 2015. It was last updated on Jan 25, 2015. This page (Example: Accelerometer Mouse) was last updated on Apr 25, 2019.