Introduction
In this lab, you’ll build an alternative computer mouse using an Arduino Leonardo using pushbuttons to move the mouse left, right, up and down. You’ll see the difference between reading a digital input continually and reading for a change of state.
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
Figures 1-5 show the parts you’ll need for this exercise. Click on any image for a larger view.
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 simply by commenting out any line that says Mouse.begin() or Mouse.move().
Note on Mouse, Keyboard, and Serial Ports
The Mouse and Keyboard libraries on the SAMD boards (MKRZero, MKR1xxx, Nano 33IoT) have an unusual behavior: using them changes the serial port enumeration. When you include the Keyboard library in a sketch, your board’s serial port number will change. For example, on MacOS, if the port number is /dev/cu.usbmodem14101
, then adding the Keyboard library will change it to /dev/cu.usbmodem14102
. Removing the Keyboard library will change it back to /dev/cu.usbmodem14101
. Similarly, if you double-tap the reset button to put the board in bootloader mode, the serial port will re-enumerate to its original number.
Windows and HID Devices
You may have trouble getting these sketches to work on Windows. On Windows, the default Arduino drivers must be uninstalled so the system can recognize the Arduino as both serial device and HID device. Read this issue and follow the instructions there.
Recovering From a Runaway HID (Mouse or Keyboard) Sketch
Programs which control your keyboard and mouse can make development difficult or impossible if you make a mistake in your code. If you create a mouse or keyboard example that doesn’t do what you want it to, and you need to reprogram your microcontroller to stop the program, do this:
Open a new blank sketch.
This sketch is your recovery:
void setup() {
}
void loop() {
}
Programming the board with this blank sketch removes your mistaken sketch and gives you control again. To do this, however, you need the current sketch to stop running. So:
Put the microcontroller in bootloader mode and upload the blank sketch
On the SAMD boards, you can do this by double-tapping the reset button. The on-board LED will begin glowing, and the bootloader will start so that you get a serial port enumeration, but the sketch won’t run. On the Leonardo and other 32U4-based boards, hold the reset down until you’ve given the upload command. The 32U4 bootloader will take a few seconds before it starts the sketch, so the uploader can take command and load your blank sketch.
Once you’ve got the blank sketch on the board, review the logic of your mistaken Keyboard or Mouse sketch and find the problem before uploading again.
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 as shown below in Figure 6. If using the Arduino Nano connect the 3.3V and ground pins according to Figure 7.
Figure 7. Breadboard view of an Arduino Nano connected to a breadboard. The +3.3 volts and ground pins of the Arduino are connected by red and black wires, respectively, to the left side rows of the breadboard. +3.3 volts is connected to the left outer side row (the voltage bus) and ground is connected to the left inner side row (the ground bus). The side rows on the left are connected to the side rows on the right using red and black wires, respectively, creating a voltage bus and a ground bus on both sides of the board.
Made with Fritzing
Add a pushbutton
Attach a pushbutton to digital pin 2 as shown below in Figure 9. See Figure 10 for Arduino Nano. See Figure 8 for the schematic view. 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).
Figure 10. Breadboard view of an Arduino Nano connected to a pushbutton. The +3.3 volts and ground pins of the Arduino are connected by red and black wires, respectively, to the left side rows of the breadboard. +3.3 volts is connected to the left outer side row (the voltage bus) and ground is connected to the left inner side row (the ground bus). The side rows on the left are connected to the side rows on the right using red and black wires, respectively, creating a voltage bus and a ground bus on both sides of the board. The pushbutton is mounted across the middle divide of the solderless breadboard. A 10-kilohm resistor connects from the same row as pushbutton’s bottom left pin to the ground bus on the breadboard. There is a wire connecting to digital pin 2 from the same row that connects the resistor and the pushbutton. The top left pin of the pushbutton is connected to +3.3V.
Add four more pushbuttons
Repeat the last step, connecting four more pushbuttons to pins 3 through 6 as shown below in Figure 12. See Figure 11 for the schematic view. See Figure 13 for Arduino Nano.
Figure 13. Breadboard drawing of an Arduino Nano connected to five pushbuttons at digital pins 2-6 respectively.
Program the module to read the pushbutton
Follow the same steps as you did in the first Mouse Control lab to read when the pushbutton on pin 2 is pressed. Your code should only print out a message when the button changes state.
Similarly, set up a global variable to track whether or not you’re controlling the mouse, called mouseIsActive. Each time the pushbutton on pin 2 is pressed, change the state of this variable from false to true, just like you did in the first mouse control lab.
// 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);
pinMode(2, INPUT);
}
void loop() {
// read the first 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;
}
Activate the other four buttons
The other four pushbuttons are intended to control the mouse movement: the button on pin 3 should move the mouse right; the one on pin 4 should move it left; the one on pin 5 should move it down; and the one on pin 6 should move it up. If the user holds the button down, the mouse should continue to move in the corresponding direction.
To make this happen, you need to read the buttons’ states. You don’t need to tell when they change from off to on, in this case. You just need to know that they are on. But you shouldn’t do anything unless the mouseIsActive variable is true.
Add code to the setup to set pins 3 through 6 to input using the pinMode command. Then add code to the loop to read whether the mouseIsActive variable is true. If it is, read the four other pushbuttons. For each one, check if it’s high, and if so, print the letter of the direction you’re moving, U, R, L, or D.
if (mouseIsActive) {
// read the other buttons:
int button2State = digitalRead(3);
int button3State = digitalRead(4);
int button4State = digitalRead(5);
int button5State = digitalRead(6);
if (button2State == HIGH) {
Serial.println("R");
}
if (button3State == HIGH) {
Serial.println("L");
}
if (button4State == HIGH) {
Serial.println("D");
}
if (button5State == HIGH) {
Serial.println("U");
}
}
When you run this sketch, you should see the Mouse Control State message once every time you press the first pushbutton, and the letters R, L, U, or D continuously when you hold down any of the other four pushbuttons. When you’ve got that working, you’re ready to take control of the mouse.
Add commands to control the mouse
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.
Include the Mouse library at the top of your code. Then modify the setup by adding the command Mouse.begin(). Then, in the loop where you print L, R, U, or D add Mouse.move commands to move as needed.
#include "Mouse.h"
// 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 through 6 inputs:
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(6, INPUT);
}
void loop() {
// read the first 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;
// if the mouse is active, and any button is pressed,
// move the mouse in the corresponding direction:
if(mouseIsActive) {
// read the other buttons:
int button2State = digitalRead(3);
int button3State = digitalRead(4);
int button4State = digitalRead(5);
int button5State = digitalRead(6);
if (button2State == HIGH) {
Serial.println("R");
Mouse.move(2, 0, 0); // move right
}
if (button3State == HIGH) {
Serial.println("L");
Mouse.move(-2, 0, 0); // move left
}
if (button4State == HIGH) {
Serial.println("D");
Mouse.move(0, 2, 0); // move down
}
if (button5State == HIGH) {
Serial.println("U");
Mouse.move(0, -2, 0); // move up
}
}
}
That’s the whole sketch. When you run this, press the button to enable or disable mouse control, then press any of the other four buttons, and the mouse will move in the appropriate direction.
The full sketch for this can be found on the phys comp github repository, called MouseMoveSimpleButtons.
You can see from this sketch the difference between reading a digital input to look for the state change (the button on pin 2) and reading a digital input just to see the current state (buttons on pins 3 through 6). When you’re designing a response that can repeat infinitely, you can just read the current state. But when you’re designing a response that should happen just once, you need to determine when the button changes state.
Next, try the Mouse Control With Joystick lab.