Skip to content

Arduino - Mouse Button Control

Certain Arduino boards, like the Leonardo, Micro, or Due, have native USB capabilities, allowing them to act as a Human Interface Device (HID), such as a keyboard or a mouse. This tutorial shows how to build a simple mouse controller using pushbuttons.

This updated example uses five pushbuttons: four for directional movement (up, down, left, right) and one for a left-click. We will use modern coding practices, including internal pull-up resistors to simplify wiring and a non-blocking delay structure for more responsive control.

Important Warning: When you run this sketch, the Arduino takes control of your computer’s mouse. To regain control, you may need to unplug the Arduino. Always have a plan to stop the sketch before uploading it.

  • 1 × Arduino Leonardo, Micro, or Due board (boards with ATmega32U4 or SAM3X8E chips)
  • 1 × Breadboard
  • 5 × Momentary Pushbuttons
  • Jumper wires

We will use the Arduino’s internal pull-up resistors, which simplifies the circuit by eliminating the need for external resistors. Connect one leg of each pushbutton to a digital pin and the other leg to GND.

Here is the connection layout:

Button Arduino Pin
-----------------------------
Up Button --> Pin 2
Down Button --> Pin 3
Left Button --> Pin 4
Right Button--> Pin 5
Click Button--> Pin 6
(The other leg of ALL buttons connects to Arduino GND)

When a button is pressed, it will connect the digital pin to GND, which the Arduino will read as LOW.

This sketch is designed for readability and scalability. It uses arrays for pin definitions and a non-blocking delay to keep the code responsive.

/*
* Modern Button Mouse Control
*
* Controls the mouse from five pushbuttons on an Arduino Leonardo, Micro, or Due.
* This version uses internal pull-up resistors and a non-blocking delay.
*
* WARNING: This sketch takes over your mouse! Unplug the Arduino to regain control.
*/
#include <Mouse.h>
// --- Pin Definitions ---
// Using an array makes it easy to add more buttons later.
const int buttonPins[] = {2, 3, 4, 5, 6}; // {UP, DOWN, LEFT, RIGHT, CLICK}
const int numButtons = sizeof(buttonPins) / sizeof(buttonPins[0]);
// --- Control Parameters ---
const int movementSpeed = 5; // How many pixels to move per step
const long responseDelay = 10; // Delay in ms between updates
// --- State Variables ---
unsigned long lastUpdateTime = 0; // For non-blocking delay
void setup() {
// Initialize all button pins as inputs with internal pull-ups
for (int i = 0; i < numButtons; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
// Initialize mouse control. This starts the HID communication.
Mouse.begin();
}
void loop() {
// Use a non-blocking delay to ensure the loop is responsive
if (millis() - lastUpdateTime > responseDelay) {
lastUpdateTime = millis();
// Read the state of all buttons (LOW means pressed)
bool upState = (digitalRead(buttonPins[0]) == LOW);
bool downState = (digitalRead(buttonPins[1]) == LOW);
bool leftState = (digitalRead(buttonPins[2]) == LOW);
bool rightState = (digitalRead(buttonPins[3]) == LOW);
bool clickState = (digitalRead(buttonPins[4]) == LOW);
// Calculate movement
int xDistance = 0;
if (leftState) xDistance = -movementSpeed;
if (rightState) xDistance = movementSpeed;
int yDistance = 0;
if (upState) yDistance = -movementSpeed;
if (downState) yDistance = movementSpeed;
// If there is movement, send the command
if (xDistance != 0 || yDistance != 0) {
Mouse.move(xDistance, yDistance, 0); // (x, y, scroll)
}
// Handle mouse click
// This logic presses the button when held and releases it when let go.
if (clickState) {
if (!Mouse.isPressed(MOUSE_LEFT)) {
Mouse.press(MOUSE_LEFT);
}
} else {
if (Mouse.isPressed(MOUSE_LEFT)) {
Mouse.release(MOUSE_LEFT);
}
}
}
}

#include <Mouse.h>: This line includes the necessary library for HID mouse emulation. It only works on specific boards.

pinMode(pin, INPUT_PULLUP): This is a key improvement. It configures the pin as an input and enables an internal resistor connected to VCC. This means you don’t need external resistors, simplifying your circuit. The pin will read HIGH when the button is not pressed and LOW when it is pressed (as it’s connected to GND).

Non-Blocking Delay: The if (millis() - lastUpdateTime > responseDelay) structure avoids using delay(). This is crucial for responsive applications. The loop() runs continuously, and we only update the mouse position at fixed intervals, preventing jerky movement while keeping the program ready for other tasks.

  • Code won’t compile? Make sure you have selected the correct board (Leonardo, Micro, etc.) in the Arduino IDE under Tools > Board. The Mouse library is not available for boards like the Uno or Mega.
  • Mouse moves erratically or not at all? Double-check your wiring. Ensure all buttons have one leg connected to the correct pin and the other to GND. A faulty connection can cause a pin to ‘float’, leading to random inputs.

This basic controller can be expanded into many useful projects: a custom gaming keypad, a presentation clicker, a media control panel (using Keyboard.press() for play/pause/volume keys), or an assistive device for users with limited mobility.