Lab: Mouse Control

Introduction

In this lab, you’ll build an alternative computer mouse using any of the USB-native boards (the MKR series, the Due, the Leonardo, the Micro, and the Feather M0 series all fit this requirement). You’ll also learn some techniques for determining when a user takes a physical action. In the Digital Lab and Analog Lab, you learned how to read the input from a digital or analog input, but you didn’t learn anything about how to interpret the stream of data as an event. There are a few everyday physical interactions that are fairly easy to pick out from a stream of data. You’ll see a few of them in this lab.

What You’ll Need to Know

To get the most out of this lab, you should be familiar with the following concepts. You can check how to do so in the links below:

Things You’ll Need

Below are the parts you’ll need for this exercise. Click on any image for a larger view.

A short solderless breadboard with two rows of holes along each side. There are no components mounted on the board.
A solderless breadboard.
An Arduino Leonardo. The USB connector is facing to the left, so that the digital pins are on the top of the image, and the analog pins are on the bottom.
A USB-Native Arduino board. Shown here is an Arduino Leonardo
Three 22AWG solid core hookup wires. Each is about 6cm long. The top one is black; the middle one is red; the bottom one is blue. All three have stripped ends, approximately 4 to 5mm on each end.
22AWG solid core hookup wires.
Resistors. Shown here are 220-ohm resistors. You can tell this because they have two red and one brown band, followed by a gold band.
Resistors. Shown here are 220-ohm resistors.  For this exercise, you’ll need 10-kilohm resistors (10K resistors are brown-black-orange. For more, see this resistor color calculator)
A pushbutton with two wires soldered one to each of the button's contacts.
A pushbutton with two wires soldered one to each of the button’s contacts. Any switch will do the job as well.
Photocell, or light-dependent resistor. This component has a round top and two wire legs. You measure the resistance between the two legs and expose the top to a varying light source in order to vary the resistance between the two legs.
Photocell, or light-dependent resistor Any analog sensor will do the job for this lab.

About mouse control

NOTE: The sketches contained in this lab will cause the Arduino Leonardo to take control of your mouse. Make sure they’re working properly before you add the mouse commands. The example doesn’t introduce the mouse commands until the end of the lab. Instead, messages are printed to the serial monitor to tell you what should happen. When you’ve run this and seen the serial messages occurring when you think they should, then you can add the mouse commands safely.

The sketches here will work on an Uno until you add the mouse commands. So you can test this on an Uno or any board that’s not USB-native simply by commenting out the Mouse.h include at the top of the code below, and any line that says Mouse.begin() or Mouse.move().

Prepare the breadboard

Connect power and ground on the breadboard to power and ground from the microcontroller. On the Arduino module, use the 5V and any of the ground connections:

An Arduino Leonardo on the left connected to a solderless breadboard, right. The Leonardo's 5V output hole is connected to the red column of holes on the far left side of the breadboard. The Leonardo's ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.

An Arduino Leonardo on the left connected to a solderless breadboard, right.

Made with Fritzing

Add a pushbutton

Attach a pushbutton to digital pin 2. Connect one side of the pushbutton to 5 volts, and the other side of the pushbutton to a 10-kilohm resistor. Connect the other end of the resistor to ground. Connect the junction where the pushbutton and the resistor meet to digital pin 2. (For more on this digital input circuit,see the Digital Input Lab)

Schematic drawing of an Arduino Leonardo connected to a pushbutton. The pushbutton is connected on one side to digital pin 2. Thje other side is connected to +5 volts. a 10-kilohm resistor is connected to the junction where the pushbutton meets pion 2. The other side of the resistor is connected to ground.
Schematic view of an Arduino Leonardo connected to a pushbutton.
Breadboard drawing of an Arduino Leonardo connected to a pushbutton. The pushbutton straddles the center divide of the breadboard in rows 20 through 22. A red wire connects row 20 on the right center side to the right side voltage bus. A 10-kilohm resistor connects row 22 on the right center side to row 26 on the same side. A black wire connects from row 26 to the ground bus on the right side. A blue wire connects row 22 on the left center side of the board to digital p
Breadboard view of an Arduino Leonardo connected to a pushbutton.

Add two analog inputs

Attach a photoresistor to 5 volts. Attach a 10-kilohm resistor from the other side of the photoresistor to ground. Attach the junction where the photocell and the resistor meet to analog input 0. Do the same for analog input 1. You can use any variable resistor in place of the photoresistors: flex sensors, force-sensing resistors, or whatever feels good to you.

Schematic drawing of an Arduino Leonardo connected to a pushbutton and two voltage divider inputs. The pushbutton is connected as described in the schematic above. On the left side of the board, two photoresistors are connected to +5 volts. The other sides of the photoresistors are connected to analog inputs A0 and A1, respectively. Two 10-kilohm resistors are also connected to those pins. The other sides of the fixed resistors are connected to ground.
Schematic view of an Arduino Leonardo connected to a pushbutton.
Breadboard drawing of an Arduino Leonardo connected to a pushbutton and two voltage divider inputs. The pushbutton is connected as described in the previous breadboard drawing. Two photoresistors are mounted in the right center section of the breadboard. The first is connected to rows 2 and 5. The second is connected to rows 11 and 14. Rows 2 and 11 are also connected to the right side voltage bus through red wires. Two 10-kilohm resistors are also mounted in the right center section. The first is connected to rows 5 and 9, and the second is connected to rows 14 and 18. Rows 9 and 18 are connected to the right side ground bus. The four resistors thus form two voltage dividers. A blue wire extends from the middle of each divider to the analog pins. The first goes from row 5 to pin A0, and the second from row 14 to pin A1.
Breadboard view of an Arduino Leonardo connected to a pushbutton and two voltage divider circuits.

 

Program the module to read the pushbutton

In the Digital Lab you learned how to read a pushbutton using the digitalRead() command. To tell when a pushbutton is pushed, you need to determine when the button’s state changes from off to on. With the pushbutton wired as you have it here, its state will change from 0 to 1. In order to know that, you need to know not only what the current state of the button is, but you also need to remember the state of the button the previous time you read it.

To do this, set up a global variable to store the button’s previous state. Initialize the button in your program’s setup() function using the pinMode() command. Then, in the loop() function, write a block of code that reads the button and compares its state to the previous state variable. To do this, you need to read the button, check the current button state against the last state, then save the current state of the button in a variable for the next time through the loop like so:

int lastButtonState = LOW;        // state of the button last time you checked

void setup() {
  // make pin 2 an input:
  pinMode(2, INPUT);
}

void loop() {
  // read the pushbutton:
  int buttonState = digitalRead(2);

  // check if the current button state is different than the last state:
  if (buttonState != lastButtonState) {
     // do stuff if it is different here
  }

  // save button state for next comparison:
  lastButtonState = buttonState;
}

If buttonState is not equal to lastButtonState, then the button has changed. Then you want to check if the current state is HIGH. If it is, then you know the button has changed from low to high. That means your user pressed it. Print out a message to that effect.

int lastButtonState = LOW;        // state of the button last time you checked

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  // make pin 2 an input:
  pinMode(2, INPUT);
}

void loop() {
  // read the pushbutton:
  int buttonState = digitalRead(2);

  // if it's changed and it's high, toggle the mouse state:
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      Serial.println("Button was just pressed.");
    }
  }
  // save button state for next comparison:
  lastButtonState = buttonState;
}

Your code should only print out a message when the button changes state. For every button press, you should get one line of code. If you’re getting multiple lines of code for every button press, you did it wrong.

Set up a variable to track whether or not you’re controlling the mouse

This sketch is going to take control of your computer’s mouse when you finish it. If you get it wrong, that can cause real problems, because you won’t be able to control your computer until you unplug the Leonardo. So you want to make sure you’ve got a way to turn off control of the mouse. You’ll use this pushbutton to turn on or off the Leonardo’s control of the mouse.

To do this, set up a global variable called mouseIsActive. Initialize it as false. When the button is pressed, change this variable from false to true, or true to false, depending on its state. This should be a boolean variable. This variable doesn’t actually change anything yet, but later on, you’ll decide whether to issue mouse movement commands depending on whether it’s true or false.

// Global variables:
int lastButtonState = LOW;            // state of the button last time you checked
boolean mouseIsActive = false;    // whether or not the Arduino is controlling the mouse

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  // make pin 2 an input:
  pinMode(2, INPUT);
}

void loop() {
  // read the pushbutton:
  int buttonState = digitalRead(2);

  // if it's changed and it's high, toggle the mouse state:
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      // if mouseIsActive is true, make it false;
      // if it's false, make it true:
      mouseIsActive = !mouseIsActive;
      Serial.print("Mouse control state: ");
      Serial.println(mouseIsActive);
    }
  }
  // save button state for next comparison:
  lastButtonState = buttonState;
}

Read the sensors and decide on a threshold for action

The photoresistors in the circuit above will be used to detect whether the user wants to move left or right. Set them up close to each other in the same light. When the user waves her hand over the top one in the image above, you’ll move the mouse cursor to the left, and when she waves her hand over the bottom one, you’ll move it to the right. Nothing should happen if she doesn’t have her hands over the sensors, or if she has her hands over both sensors.

Since they’re in the same light, they should read identically. So to detect hands, you want to look for a significant difference between the sensors. When there’s a significant difference between them, you’ll make a move. When the top sensor is higher, you’ll move to the left. When the one on the bottom is higher, you’ll move to the right.

Add code to the main loop to read the sensors into two variables, sensor1 and sensor2, with a one-millisecond delay between readings. Then print the results.

// Global variables:
int lastButtonState = LOW;            // state of the button last time you checked
boolean mouseIsActive = false;    // whether or not the Arduino is controlling the mouse

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  // make pin 2 an input:
  pinMode(2, INPUT);
}

void loop() {
  // read the pushbutton:
  int buttonState = digitalRead(2);

  // if it's changed and it's high, toggle the mouse state:
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      // if mouseIsActive is true, make it false;
      // if it's false, make it true:
      mouseIsActive = !mouseIsActive;
      Serial.print("Mouse control state: ");
      Serial.println(mouseIsActive);
    }
  }
  // save button state for next comparison:
  lastButtonState = buttonState;

// read the analog sensors:
  int sensor1 = analogRead(A0);
  delay(1);
  int sensor2 = analogRead(A1);

  // print their values:
  Serial.print(sensor1);
  Serial.print("  ");
  Serial.println(sensor2);
}

Right after this in the main loop, add code to compare the two values. If there is more than 100 points difference, print L or R, depending on which sensor is higher (L for the top sensor, R for the bottom):

 // if there's a significant difference to the right:
  if (sensor1 > sensor2 + 100) {
    Serial.println("L");
  } 

  // if there's a significant difference to the left:
  else if (sensor2 > sensor1 + 100) {
    Serial.println("R");
  }

When you run this sketch now, you should see an L or R every time there’s a significant difference (> 100) between the sensors. When you push the button, you should get a report of the mouse control state, 1 for true and 0 for false. Run it and open the serial monitor to see that happen.

Add mouse control

Now it’s time to add mouse control commands.

NOTE: The sketches contained in this lab will cause the Arduino Leonardo to take control of your mouse. Make sure they’re working properly before you add these commands. The example doesn’t introduce the mouse commands until the end of the lab. Instead, messages are printed to the serial monitor to tell you what should happen. When you’ve run this and seen the serial messages occurring when you think they should, then you can add the mouse commands safely.

The sketches here will work on an Uno until you add the mouse commands. So you can test this on an Uno simply by commenting out any line that says Mouse.begin() or Mouse.move().

If your sketch causes your mouse to misbehave, upload a blank sketch (the default when you choose File -> New) to your Leonardo and you can start again from the beginning.

The Mouse.begin() command is called in the setup. It initializes mouse control from your Leonardo.

The Mouse.move() command has three parameters: the horizontal movement, the vertical movement, and the scroll wheel movement. All movements are relative, so Mouse.move(1,0,0); moves one pixel to the right; Mouse.move(-1,0,0); moves one pixel to the left; Mouse.move(0,1,0); moves one pixel down; and Mouse.move(0,-1,0); moves one pixel up.

At the top of your code, include the Mouse library. Then Modify the setup by adding the command Mouse.begin(). Then, in the loop where you print L or R, add Mouse.move commands to move left or right, as needed.


#include <Mouse.h>                // include the mouse library
// Global variables:
int lastButtonState = LOW;        // state of the button last time you checked
boolean mouseIsActive = false;    // whether or not the Arduino is controlling the mouse

void setup() {
  // initialize mouse control:
  Mouse.begin();
  // initialize serial communication:
  Serial.begin(9600);
  // make pin 2 an input:
  pinMode(2, INPUT);
}

void loop() {
  // read the pushbutton:
  int buttonState = digitalRead(2);

  // if it's changed and it's high, toggle the mouse state:
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      // if mouseIsActive is true, make it false;
      // if it's false, make it true:
      mouseIsActive = !mouseIsActive;
    }
  }
  // save button state for next comparison:
  lastButtonState = buttonState;

  // read the analog sensors:
  int sensor1 = analogRead(A0);
  delay(1);
  int sensor2 = analogRead(A1);

  // print their values. Remove this when you have things working:
  Serial.print(sensor1);
  Serial.print("  ");
  Serial.println(sensor2); 

  // if there's a significant difference to the right:
  if (sensor1 &amp;gt; sensor2 + 100){
    //  Serial.println("L");
    if (mouseIsActive == true) {
      Mouse.move(-1, 0, 0);
    }
  } 

  // if there's a significant difference to the left:
  else if (sensor2 &amp;gt; sensor1 + 100) {
    // Serial.println("R");
    if (mouseIsActive == true) {
      Mouse.move(1, 0, 0);
    }
  }
}

That’s the whole sketch. When you run this, press the button to enable or disable mouse control, then put your hands over the sensors to move the mouse left or right.

The full sketch for this can be found on the phys comp github repository, called MouseMoveSimple
Once you’ve got that working, move on to the Mouse Control With Pushbuttons lab.

Originally written on August 24, 2014 by Benedetta Piantella Simeonidis
Last modified on August 28, 2018 by Tom Igoe