Projects

Thumb Control

Build a Joystick Mouse Emulator

Johann Wyss

Issue 37, August 2020

This article includes additional downloadable resources.
Please log in to access.

Log in

Replace your mouse with this easy-to-build joystick-controlled mouse emulator.

BUILD TIME: 2 HOURS
SKILL LEVEL: INTERMEDIATE

This project came about when we had no room to use a mouse to control the computers in our server room.

The Broad overview

The premise for the project is actually very simple. We need an analog joystick that can convert physical movement into an electronic signal. This signal can then be read on a microcontroller and translated into positional data which can be sent as a USB device to a computer or other mouse compatible device.

This is made very simple thanks to the Arduino environment due to the great choice of libraries available that can do the “heavy lifting” for us.

How it works

Analog Joystick

Analog joysticks are incredibly simple yet elegant in their design and construction. They are little more than two 10KΩ potentiometers which are positioned together via a universal joint. Moving the joystick in either direction causes the wiper on the potentiometers to change position, either increasing or decreasing the ratio of resistance seen either side of the wiper relative to the axis. Thus, if you used a multimeter to measure between the wiper VRY for example and GND, or VRY and VCC (out of circuit), you would see that there is a different resistance value on both when the thumbstick is moved away from the centre of the y-axis potentiometer.

The joystick module is a simple 5-pin device consisting of two 10K potentiometers and a tactile switch. We can read the voltage at VRX and VRY and approximate where the joystick is positioned.

This ratio is just a simple voltage divider which we covered in issue 11 circuit analysis. By changing the position of the thumbstick in the X and Y direction you directly change the ratio of resistance either side of the potentiometer wiper VRY and VRX. Naturally, this creates a voltage divider.

We can calculate the voltage at Vout (VRX or VRY) using the formula:

Vout = Vin x (R2 / (R1 + R2))

We know the resistor is 10KΩ and that in the resting state the thumbstick is centred so that the resistance value either side of the wiper will be roughly identical and therefore half of the total resistance i.e. 5KΩ. If Vin is 5V, we can calculate the voltage on Vout using:

Vout = 5V x (5000 / (5000 + 5000)) = 2.5V

Thus, when the potentiometer is centred, both VRY and VRX will have about 2.5V potential with respect to ground. If we were to move the thumbstick all the way to the top end of its movement, the output of VRX stays centred but the ratio on VRY changes, thus VRX will be 2.5V and VRY will be changed due to the now different ratio. The resistance at the top of VRY (R1) will be near zero and the resistance below VRY (R2) will be nearly 10K:

Vout = 5V x (10000 / (0 + 10000)) == 5V

Thus, the voltage at VRY(Vout) will be 5V. This is easily shown using the chart shown here.

Choosing a microcontroller

To convert this simple joystick into a mouse we need a microcontroller that has two available analog pins and that has USB functionality.

The first hurdle we found in creating such a project using the Arduino environment is, of course, getting USB connectivity. The Arduino Uno that we all know and love has an ATmega328P microcontroller at its heart. This microcontroller is not directly capable of sending and receiving Universal Serial Bus (USB) signals. This functionality comes from a second microcontroller (called a co-processor) hidden away on the Arduino Uno.

This ATmega16U2 acts as a bridge between the computer’s USB port and the ATmega328P’s serial port. This allows us to program the Arduino Uno’s ATmega328P microcontroller and send serial data via USB. Whilst it is possible to reprogram the ATmega16U2 on the Arduino Uno to allow the Uno to work directly as a mouse, in this project, you lose the ability to program the microcontroller via USB. Simply put, that tends to counter the benefit of the Arduino platform which is successful due to the ease of use and ability to easily program.

This made us think of the Digispark from Digistump, which we used back in Issue 18 to make a USB rubber ducky where we emulate a keyboard to make some interesting pranks. The Digispark uses an ATtiny85 microcontroller which has built-in USB capabilities, allowing it to act as a human interface device (HID).

Thus, it seems fair to start there with this project.

To identify if this microcontroller development board is suitable for our project, we needed to take a close look at the device pinout.

The ATtiny85 microcontroller used in this development board has four analog input pins.

ADC0 is used for the RESET pin.

ADC1 is used for SCLK and SCL and thus can easily be used in the project.

ADC2 is used for USB - and can’t be used to measure the joystick potentiometer voltage.

ADC3 is used for USB + and can’t be used to measure the joystick potentiometer voltage.

Since we need two analog pins to read the VRY and VRX pins from the joystick, the Digispark became less ideal for the project. With this configuration, the microcontroller would reset anytime the voltage from the potentiometer would drop below 2.5V.

We tried to reprogram the fuses on the ATtiny85 to reclaim the reset pin, however, this interfered with the Digispark bootloader and prevented the device from being used as a human interface device. It was time to look for a different microcontroller / development board to better suit our needs.

The next controller in our lineup was the Arduino Leonardo, a development board with the exact same footprint as the Arduino Uno with one significant difference. Rather than the ATmega328P microcontroller, the Leonardo uses the ATmega32U which natively supports USB and, more excitingly, has a library specifically for mouse emulation.

* 7V on DC jack with blank sketch

Not being familiar with the Leonardo, we looked into the differences between it and the Arduino UNO. The good news is, whilst it is a little different, most of the functionality is identical between the two development boards. It seems as if the vast majority of libraries written for the Arduino Uno will work on the Leonardo, whereas libraries written for the Leonardo may not be compatible with the Arduino Uno. The same can't be said for Arduino shields.

Especially shields that use SPI or I2C as the pins are not identical so caution must be used when selecting the Arduino Leonardo, especially if you intend on using a shield.

From this table, you will notice that the Arduino Leonardo has more analog pins whilst having the same footprint. Like the Arduino Uno, the Leonardo has pins A0 to A5 as dedicated analog pins, whereas, digital pins 4, 6, 8, 9, 10, and 12 on the Leonardo can also be used as analog inputs.

With our choice of the Leonardo microcontroller decided, it was time to get started making the circuit.

The Prototype:

Electronics

Parts Required:JaycarAltronicsCore Electronics
1 x Arduino LeonardoXC4430-A000057
1 x Joystick ModuleXC4422Z6363TOY0017 ^

Parts Required:

A breadboard and prototyping hardware is also required.

^ This component is not identical to the component used and may require a modification the enclosure and or wiring to make it compatible with this project.

As you would no doubt expect, the construction of this prototype is very simple. It’s just a matter of connecting the joystick module to the Arduino Leonardo.

To do this, you need to connect the two components in the following way:

The code

The code for this project uses the mouse.h library which comes pre-installed in recent versions of the Arduino integrated development environment (IDE). If you’re having any issues with the supplied code not compiling due to a line of code, upgrading your IDE to the newer versions should be your first troubleshooting step.

This mouse.h library contains the example “joystickMouseControl” which we used as the starting point for this project. We condensed the code down to just the sections we needed.

Setup

#include "Mouse.h"
const int yAxis = A0;
const int xAxis = A1;
const int button = 2;
int range = 12;
int responseDelay = 10;
int threshold = 2;
int center = range / 2 ;
void setup() {
  pinMode(button, INPUT_PULLUP);
  Mouse.begin();
}
In the code, we follow the usual process of defining the libraries used. Whilst the library is built-in, we still need to add the #include “Mouse.h” line here for the sketch to compile.

We then create a const for each of the IO pins used. Using constants makes it easier to remember what you did when you read the code in another month or so time. Using constants also makes it easier if you should change the hardware, as you have only the constants to change.

We then create several variables. These variables are used in the program to convert the analog readings, which will be a value between 0 and 1024, down to a value between 0 and 12. We also set a dead point for the joystick so that the joystick must move the amount specified in the threshold variable before the mouse will move. This is required to prevent the mouse from moving when the joystick is in the central position.

To slow down the movement, we set a value for responseDelay, measured in milliseconds. If you’re using a high-resolution display like we are, you may find increasing this value improves the usability of the project, as it can be a little difficult to click certain buttons like the minimise and maximise buttons in Windows 10.

Loop

void loop() {
  int xValue = readValue(xAxis);
  int yValue = readValue(yAxis);
  Mouse.move(xValue, yValue, 0);
  delay(responseDelay);
  if (digitalRead(button) == LOW) {
    Mouse.click();
    delay(10);
  }
}
int readValue(int axis) {
  int reading = analogRead(axis);
  reading = map(reading, 0, 1023, 0, range);
  int distance = reading - center;
  if (abs(distance) < threshold) {
    distance = 0;
  }
  return distance;
}

In the loop, we populate the x and y-axis values by calling the function readValue();. This reads the value on the respective analog pin and then maps it to a value between 0 and the range variable, in this case 12. The distance the mouse needs to move is calculated by taking the read value and subtracting from it an amount equal to half of the range variable.

Thus, if reading is equal to 6, the distance will be equal to zero.

distance = (reading - (range/2)) = 6 - (12 / 2) = 0

If this result is less than our threshold variable, in our case 2, the distance variable will remain as zero and the mouse won’t move.

Programming the Leonardo

The Arduino Leonardo is very similar to the Arduino Uno that you have likely used to, and thus, programming is very much the same. If you have never used the board before, you first connect it to your PC and ensure that the drivers for it are downloaded.

With the Leonardo connected, go to the Device Manager.

Under ports, you should see Leonardo (COMxx), where xx is the port number your Arduino Leonardo is attached to.

Inside the Arduino IDE, go to Tools > Board: and select the Arduino Leonardo from the list. Ensure that the com port selected matches the com port you just found in the Device Manager.

With that done, you should be able to upload the code as usual.

Testing & troubleshooting

With the code uploaded successfully, you should be able to control your computer’s mouse pointer with the joystick.

If you lose control of the mouse pointer, you may have poor connections for the A0 and A1 pins, an incorrectly set variable, or a faulty joystick.

If the mouse pointer moves by itself or keeps trying to return to a previous position after moving, it can be difficult to click the upload button to rectify the fault. In this case, you can use the keyboard shortcut CTRL + U (Command + U on a Mac) to upload the sketch to the Arduino Leonardo to regain use.

The Main Build:

Wiring the circuit

Parts Required:JaycarAltronicsCore Electronics
1 x Arduino LeonardoXC4430-A000057
1 x Joystick ModuleXC4422Z6363TOY0017 ^
4 x #4 6mm Screws*HP0550--
4 x #4 9mm Screws*HP0565H1139-
1 x Male Pin Header StripHM3211P5430POLOLU-965
1 x Female Pin Header StripHM3230P5392PRT-00115
1m x Ribbon Cable*WM4504--

Parts Required:

* Quantity shown, may be sold in packs.

^This component is not identical to the component used and may require a modification the enclosure and or wiring to make it compatible with this project.

After proving the hardware and software were functioning as expected with our prototype, it was time to create a build for a permanent application. For this, we need to make a very basic wiring harness using pin headers. This way, we don’t need to make any permanent modifications to the Arduino Leonardo, and it can be repurposed for other projects later, if needed.

The first step is to 3D print the enclosure and attach the modules to it. The enclosure top holds the joystick module which is secured using 4 x #4 9mm screws. Make sure that the pin header of this module faces toward the hole in the enclosure, which is where the USB plug enters for the Arduino Leonardo.

Similarly, the Arduino Leonardo needs to be secured to the base of the enclosure using 4 x #4 6mm screws.

Note: If you intend to mount the enclosure to a surface, you will need to secure the bottom section to the surface first as installing the Arduino Leonardo will cover the mounting holes.

With both modules created, start building the wiring harness. We used a 15cm length of 26-way ribbon cable which we cut down to 5 strands. We then stripped about 5mm of insulation from both ends.

We then cut down the female pin header strip to match the 5-pin male pin header on the joystick module. and soldered the 5-pin female pin header to the ribbon cable.

To protect against short circuits, apply liquid electrical tape to insulate the solder joints. If you don’t have such a product you can also use standard electrical tape or heatshrink, but it is important to insulate these connections. A hot glue gun is also a good alternative.

Cut a 5-pin male pin header down to a string of 5 pins. These pins will be inserted into the Arduino Leonardo as per the wiring diagram. We made an effort to keep the pins together where possible, and thus, the header is cut into two 2-pin headers and a single pin.

5V and GND are on one header, VRX and VRY are on one 2-pin header and the switch is on a single pin. We simply soldered the wires to these headers and attached them directly to the Arduino Leonardo.

You can now close the enclosure by pressing the two sides together ensuring that the USB port matches with the hole made in the enclosure lid. The parts should be a friction fit and depending on the tolerances of your printer may require a small amount of hot glue to secure it. When done, your project should look like this.

3D printed Enclosure

The enclosure is a friction fit two-piece design where the joystick module is secured to the top and the Arduino Leonardo to the bottom. Both pieces were printed on our Flashforge Creator Pro using 3D fillies branded white PLA+ filament. Both sides were printed face down on the build surface and were printed without using any support material.

We printed both parts at 200-microns. In this orientation and layer height, the top took about 3.5 hours to print while the bottom took about 1.5 hours.

Testing

Testing the build is fairly straightforward. Simply program the Arduino Leonardo, after which it will begin working as a human interface device.

If you find that your controls were inverted, you can easily rectify this in software by changing the lines:

const int xAxis = A0;
const int yAxis = A1;

to:

const int xAxis = A1;
const int yAxis = A0;

Alternatively, you can rectify it in hardware by swapping the pins connected to the Arduino Leonardo. Either would solve the problem.

Something we encountered during testing was when we had the range value or threshold value too low and the mouse would constantly move in one direction when the joystick was centred. This made it difficult to control the cursor using our regular mouse. We found disconnecting the erroneous joystick mouse and using the regular mouse to correct the software error was the best solution. Once the software error was rectified, we could then reconnect the mouse and use keyboard shortcut Ctrl + U to upload the sketch.

WHERE TO FROM HERE?

Whilst it’s pretty obvious, using a joystick to control a mouse cursor takes a lot of dexterity compared to using a mouse that we’re all used to. However, we were pretty amazed at just how useful it was in situations with larger icons. For things like smart TVs or media players etc. this little device is ideal and much more practical than using a remote control. Of course, that quickly falls in a heap when you’re dealing with the tiny sized icons on your PC, but with patience and in situations with limited space, this device does indeed make a usable substitute for a traditional mouse.

Some interesting ways one could improve on the project would be to perhaps use a gesture sensor to convert other hand movements into cursor movement. This could be ideal for users with a disability that makes the fine muscle control needed for a traditional mouse impractical.

Another potential avenue one could take this project is to make a prank device. Imagine playing a video game and randomly your mouse moves just as you’re trying to make a shot or other such important action causing you to lose a round… such a frustrating little device would be incredibly simple to implement and devilishly frustrating to be on the receiving end of.

Johann Wyss

Johann Wyss

DIYODE Staff Technical Writer.