Arduino and accelerometer to control cursor - need to convert code

I am trying to build a simple "mouse" which controls the cursor by movements of the head (which are measured by the accelerometer). For this, I have an Arduino Leonardo (since it can be recognized as a mouse) and an ADXL345.

Since I am new to Arduino's and programming, I wanted to use a code I found on the internet to achieve this. However, this code is optimized for a different accelerometer (memsic 2125). I assume that with certain modifications, the code should also be able to work for my accelerometer.

Is this true? And what exactly do I have to change in the code (or only the assignment of the pins, since my accelerometer has different pins)?

Code: Google Code Archive - Long-term storage for Google Code Project Hosting.

From what I see in the data sheets, the two sensors communicate in very different ways. ADXL is an I2C device and the other outputs a pulse width proportional to acceleration. If you post the code maybe we could find the common parts. I won't follow links to code.

Hi, this is the code:

Details
/

Accelerometer_Mouse
This code reads the Memsic 2125 two-axis accelerometer, converts the pulses output by the 2125 into milli-g's (1/1000 of earth's gravity) and prints a mapped version of them over the serial connection to the computer.output. It then takes these mapped values and uses them for virtual mouse movement, clicking, and scrolling.

The circuit:
X output of accelerometer to digital pin 2
Y output of accelerometer to digital pin 3
+V of accelerometer to +5V
GND of accelerometer to ground
created by David Kerns of The Athenian School on Dec 3 2012, with a large portion of code taken from David A. Mellis' and Tom Igoe's found here: http://www.arduino.cc/en/Tutorial/Memsic2125
/


const int xPin = 2;	 // const for X output of the accelerometer const int yPin = 3;	 // const for Y output of the accelerometer


void setup() {


Serial.begin(9600); // initialize serial communications:

pinMode(xPin, INPUT); // xPin connected to the accelerometer is input pinMode(yPin, INPUT); // yPin connected to the accelerometer is input

Mouse.begin();

Serial.begin(9600); // Set up serial communication at 9600bps Serial.println("ready"); //Print ready once setup loop is finished

}

void loop() {

int pulseX, pulseY; // Variables to read the pulse widths from accelerometer: int accelerationX, accelerationY; // Variables to contain the resulting accelerations

pulseX = pulseIn(xPin,HIGH); // read pulse from x-axes pulseY = pulseIn(yPin,HIGH); // read pulse from y-axes

/ convert the pulse width into acceleration accelerationX and accelerationY are in milli-g's: earth's gravity is 1000 milli-g's, or 1g.

/ accelerationX = ((pulseX / 10) - 500) 8; accelerationY = ((pulseY / 10) - 500) 8;

accelerationX = map(accelerationX, -500, 500, -20, 20); //map the acceleration output to more manageable values 

accelerationY = map(accelerationY, -400, 400, -20, 20); //map the acceleration output to more manageable values

Serial.print(accelerationX); // print the x acceleration result Serial.print("\t"); // print a tab character: Serial.print(accelerationY); // print the y acceleration result Serial.println(); //print a new line delay(50);

if(accelerationX < -17){ // override loop for scrolling, if accelerationX < -17

Mouse.move(0, 0, accelerationY); // mouse scroll function at the rate of accelerationY

} else { // when accelerationX is > -17 (most of the time), do this loop

if ((accelerationY > 4 || accelerationY < -4) // if accelerationY > 4 or < -4 || accelerationX < -4)) // or if accelerationY > 4 and < -4
(accelerationX > 4

{

Mouse.move(-accelerationX, -accelerationY, 0); // move the mouse the negative accelerations(X and Y), do not scroll

}

}

if(accelerationY > 20){ //if the accelerationY value reaches a value of 20,

Mouse.click(); //left click the computer's mouse

}

if (accelerationY > 35){ //if the accelerationY value reaches a value of 35,

Mouse.click(); //left click once Mouse.click(); //left click again (resulting in a double click)

} if (accelerationX > 20){ //if the accelerationX value reaches a value of 20,

Mouse.click(MOUSE_RIGHT); //right click the computer's mouse

}

}

I would first get the ADXL35 wired up and communicating using either I2C or SPI.
Read up on I2C here http://www.gammon.com.au/forum/?id=10896. SPI here http://www.gammon.com.au/forum/?id=10892.
Here is a writeup on using the I2C bus for the ADXL345 (plus other useful links) http://bildr.org/2011/03/adxl345-arduino/. Once you have it talking you can look at the values returned and use them to replace values in the original code to do as you like.

Hi,

I already got the wiring and the code to communicate with the ADXL345 (didn't make the code myself btw).

Here's the code:

/**
 * @file     readADXL345.ino
 * @author   Nico
 * @date     29-10-2013
 * @version  1.0
 * @mainPage
 * Simple sketch for reading the ADXL345 interface board and displaying raw data on the Serial output
 */

/**
 * SVN version control. do not remove
 * $Revision$
 * $Date$
 * $Author$
 */

#ifdef __IN_ECLIPSE__
#include "readADXL345.h"
#endif

#include <ADXL345.h>

//
// pin definitions
//
#define CS            10         // chip select for SD card
#define ADXL345_WORKS   13         // led on indicates ADXL exists
//
// other definitions
//
#define   DELAY_PER_MEASUREMENT 1000L   // one measurement per second
//
// card definitions
//
// ADXL345 Card
//   pin 1   = GND      Connect to GND row
//    pin 2   = VCC      Connect to 5V
//    pin 3   = CS      Not Connected
//   pin 4    = INT1      Not Connected
//    pin 5   = INT2      Not Connected
//    pin 6   = SDO      Not Connected
//    pin 7   = SDA      Connect to A4
//   pin 8   = SCK      Connect to A5

//
// ADXL345 accelerometer
//

ADXL345          accel(0x53);   // accelerometer instance
AccelerometerRaw   rawData;      // structure with the raw (=int) data from ADXL345
//
// global variables
//
/**
 * @name readADXL345()
 * @return uint8_t value of the sensor
 * Reads the sensor returns the value where Bit 0 = Z, Bit 1 = Y, Bit2 = X
 * The value represents the plan that is lying down. This is determined by
 * reading the raw sensor data and determining which value is largest. If negative it is 0
 * else it is a 1
 */
void readADXL345(){
   //
   // read the  raw dat from the accelerometer
   //
   rawData = accel.ReadRawAxis();
}

/**
 * @name setup()
 * initializes the sketch once by power on or Reset
 */
void setup()
{
   //
   // define IO pins if required
   //
   pinMode(CS, OUTPUT);            // SD card select
   pinMode(ADXL345_WORKS, OUTPUT);      // If lit, ADXL345 is connected
   //
   // Check that the accelerometer is in fact connected.
   //
   if(accel.EnsureConnected()){
       digitalWrite(ADXL345_WORKS, HIGH); // If we are connected, light our status LED.
   } else {
       digitalWrite(ADXL345_WORKS, LOW);  // If we are not connected, turn our LED off.
   }
   //
   // Set the range of the accelerometer to a maximum of 2G.
   //
   accel.SetRange(2, true);
   //
   // Tell the accelerometer to start taking measurements.
   //
   accel.EnableMeasurements();
   //
   // open Serial
   //
   Serial.begin(9600);
}

/**
 * @name loop()
 * loop functions is called continuously
 */
void loop()
{
   //
   // read the data
   //
   readADXL345();            // read the raw data from the ADXL345
   //
   // display the data on the Serial
   //
   Serial.print(rawData.XAxis);
   Serial.print(" | ");
   Serial.print(rawData.YAxis);
   Serial.print(" | ");
   Serial.println(rawData.ZAxis);
   //
   // delay some time
   //
   delay(DELAY_PER_MEASUREMENT);
}

Is it possible to combine this code with the other code?

Sure you can combine them. First look at what the old code does. It gets the values for X and Y acceleration (doesn't use Z) and then maps them to ±20 and uses the mapped values to send to the mouse function. Replace the part that reads the values and then modify the mapping. What values are returned by the new accelerometer code? The old code returned ±500(X) and ±400(Y).

I cleaned up the old code and got it so that it would auto format. I could not get it to compile though (I use a UNO and don't have the Mouse library).

//Details
/*

 Accelerometer_Mouse
 This code reads the Memsic 2125 two-axis accelerometer, converts the pulses output by the 2125 into milli-g's (1/1000 of earth's gravity) and prints a mapped version of them over the serial connection to the computer.output. It then takes these mapped values and uses them for virtual mouse movement, clicking, and scrolling.

 The circuit:
 X output of accelerometer to digital pin 2
 Y output of accelerometer to digital pin 3
 +V of accelerometer to +5V
 GND of accelerometer to ground
 created by David Kerns of The Athenian School on Dec 3 2012, with a large portion of code taken from David A. Mellis' and Tom Igoe's found here: http://www.arduino.cc/en/Tutorial/Memsic2125
 */


const int xPin = 2;	 // const for X output of the accelerometer
const int yPin = 3;	 // const for Y output of the accelerometer


void setup() {
  Serial.begin(9600); // initialize serial communications:
  pinMode(xPin, INPUT); // xPin connected to the accelerometer is input pinMode(yPin, INPUT);
  // yPin connected to the accelerometer is input
  Mouse.begin();

  Serial.begin(9600); // Set up serial communication at 9600bps
  Serial.println("ready"); //Print ready once setup loop is finished

}

void loop() {

  int pulseX, pulseY; // Variables to read the pulse widths from accelerometer:
  int accelerationX, accelerationY; // Variables to contain the resulting accelerations

  pulseX = pulseIn(xPin,HIGH); // read pulse from x-axes
  pulseY = pulseIn(yPin,HIGH); // read pulse from y-axes
  // convert the pulse width into acceleration accelerationX and accelerationY are in milli-g's: earth's gravity is 1000 milli-g's, or 1g.
  accelerationX = ((pulseX / 10) - 500) 8;
  accelerationY = ((pulseY / 10) - 500) 8;
  accelerationX = map(accelerationX, -500, 500, -20, 20); //map the acceleration output to more manageable values
  accelerationY = map(accelerationY, -400, 400, -20, 20); //map the acceleration output to more manageable values

  Serial.print(accelerationX); // print the x acceleration result
  Serial.print("\t"); // print a tab character:
  Serial.print(accelerationY); // print the y acceleration result
  Serial.println(); //print a new line delay(50);
  if(accelerationX < -17){ // override loop for scrolling, if accelerationX < -17
    Mouse.move(0, 0, accelerationY); // mouse scroll function at the rate of accelerationY
  }
  else { // when accelerationX is > -17 (most of the time), do this loop
   if (accelerationY > 4 || accelerationY < -4) /*if accelerationY > 4 or < -4 || accelerationX < -4)) // or if accelerationY > 4 and < -4(accelerationX > 4)*/
    {
      Mouse.move(-accelerationX, -accelerationY, 0); // move the mouse the negative accelerations(X and Y), do not scroll
    }
  }

  if(accelerationY > 20){ //if the accelerationY value reaches a value of 20,

    Mouse.click(); //left click the computer's mouse

  }

  if (accelerationY > 35){ //if the accelerationY value reaches a value of 35,

    Mouse.click(); //left click once
    Mouse.click(); //left click again (resulting in a double click)
  }
  if (accelerationX > 20){ //if the accelerationX value reaches a value of 20,
    Mouse.click(MOUSE_RIGHT); //right click the computer's mouse
  }

}

Hi,

thanks for cleaning the code, but what do you actually mean with the code being able to auto format? :stuck_out_tongue:

And.. so first I have to change the mapping in the old code. I have seen that in the first code, the values are called xPin and yPin. In the second one they are XAxis and YAxis. Is that the only thing I'm going to have to change to make the mapping similar?

And after having changed the mapping, I will have to change the values. To do that, I will have to read the values from my accelerometer first. Will do that tomorrow since I don't have the Arduino with me right now.

So after having changed those two things I can put the two codes together? :slight_smile:

In the IDE, auto format is in the Tools menu. It helps with indentation and checks for matching {} and().

  accelerationX = map(accelerationX, -500, 500, -20, 20); //map the acceleration output to more manageable values
  accelerationY = map(accelerationY, -400, 400, -20, 20); //map the acceleration output

This is the mapping I meant. Once you have the ADXL connected and working (providing data) you will put the X data into accelerationX and the Y data to accelerationY and adjust the mapping to get usable numbers for mouse control.

There will be no need for xPin or yPin as the ADXL connects to A4 & A5 (SDA & SCL of the I2C bus). Don't forget the 4.7K (or there about) pull up resistors.

Hi,

Ah. So I will only have to change the +-500 and +- 400 values?

And I assume that for example the value -500 belongs to -90 degrees and 500 to 90 degrees? (not the exact values, but just to get an idea).

So after having changed those values, I have to copy the second code and paste it below the first code? :slight_smile:

Right. After removing the old code that sets up and reads the old accelerometer. So you know the values that the ADXL outputs and the values that the Mouse() function expects then you can use the map() function to transfer acceleration to requested movements. Do you have the ADXL working? What values is the ADXL returning???? Do you know the values of the Mouse() function parameters?

Hi, sorry for the late reply but I'm having some trouble with my sensor. It used to work but now it only returns 0 as values. =( I am trying to solve this problem and will post again once I got it working and have the proper values..

groundfungus:
Right. After removing the old code that sets up and reads the old accelerometer. So you know the values that the ADXL outputs and the values that the Mouse() function expects then you can use the map() function to transfer acceleration to requested movements. Do you have the ADXL working? What values is the ADXL returning???? Do you know the values of the Mouse() function parameters?

Hi, I got the accelerometer working. The values it returns range from 260 to -260, both for x and y.

How do I get to know the values of the Mouse() function parameters?

The best I can do here is refer you to the library reference http://arduino.cc/en/Reference/MouseKeyboar

Hi, I got the thing working. I can control the cursor now, using my sensor. This is my code:

#ifdef __IN_ECLIPSE__
#include "readADXL345.h"
#endif
#include <ADXL345.h>
#include <Wire.h>
#define   delay 20L

ADXL345            accel(0x53);
AccelerometerRaw   rawData;

void readADXL345()
{
rawData = accel.ReadRawAxis();
}

void setup() 
{
accel.SetRange(2, true);
accel.EnableMeasurements();
Serial.begin(9600);
Mouse.begin();
}

void loop()
{
readADXL345();       
Serial.print(rawData.XAxis);
Serial.print(" | ");
Serial.print(rawData.YAxis);
Serial.print(" | ");
Serial.println(rawData.ZAxis);
delay(delay); 

int XValue, YValue, moveX, moveY;
XValue = rawData.XAxis;
YValue = rawData.YAxis;
moveX = XValue/20;
moveY = YValue/20;
Mouse.move(moveX, moveY, 0);
}

The problem is that I can only move the cursor, so I can't do mouse clicks. For mouse clicks, I wanted to use the Z-axis since it is still unused.
The problem is that I don't know how to "control" the Z-axis, since for me, it behaves the same as the Y-axis (different values, but they increase and decrease by the same amount).
I have done some research but still haven't figured out how to move the Z-axis without moving the X and Y axis... ??