Joystick to Analogue Mouse: HELP needed please

I'm trying to convert a standard ALPS thumb-stick (X,Y,5V,GND and pin 7 for the click) - into an analogue mouse. When I say analogue, I mean for the thumb-stick to adjust the speed the mouse-pointer moves (e.g. on a Windows PC), so the further you push the stick from dead-centre, the faster the pointer goes in whatever direction you push in.

I've tried the likes of this: https://create.arduino.cc/editor/vxviper6/ca8f3852-55ae-4dc8-af5c-c9434ea8d148/preview - but the mouse pointer movement is very basic. It's either moving at a fixed speed or not. No good really for general use.

Can anyone help, please? I'm trying to make this work for a disabled user.

Best wishes,

Barrie

You didn’t supply a link to this “standard“ switch but I suspect it is a digital switch.
Therefore there is no analogue information for you to convert. It can only ever supply a digital signal and so move at a fixed rate or not move at all.

const int sensitivity = 200; // Higher sensitivity value = slower mouse, should be <= about 500

Did you try setting the 'sensitivity' to a lower value?

Thanks for replying. It's the thumb-stick click on a standard ALPS thumb-stick (e.g. Xbox One / PS4 type)... a simple digital momentary push-button switch... but that's not really the issue. It's the actual analogue joystick not behaving in an analogue fashion as regards moving the mouse-pointer.

All I get is a fixed speed in 8-directions - no matter how gently or strongly I push the stick. I've tried tweaking the code a little with this:

#include <Mouse.h>

/* HID Joystick Mouse Example
by: Jim Lindblom
date: 1/12/2012
license: MIT License - Feel free to use this code for any purpose.
No restrictions. Just keep this license if you go on to use this
code in your future endeavors! Reuse and share.

This is very simplistic code that allows you to turn the
SparkFun Thumb Joystick (http://www.sparkfun.com/products/9032)
into an HID Mouse. The select button on the joystick is set up
as the mouse left click.
*/
#include <Mouse.h>
int horzPin = A0; // Analog output of horizontal joystick pin
int vertPin = A1; // Analog output of vertical joystick pin
int selPin = 7; // select button pin of joystick

int vertZero, horzZero; // Stores the initial value of each axis, usually around 512
int vertValue, horzValue; // Stores current analog output of each axis
const int sensitivity = 200; // Higher sensitivity value = slower mouse, should be <= about 500
int mouseClickFlag = 0;

//int invertMouse = 1; //Invert joystick based on orientation
int invertMouse = -1; //Noninverted joystick based on orientation

void setup()
{
pinMode(horzPin, INPUT); // Set both analog pins as inputs
pinMode(vertPin, INPUT);
pinMode(selPin, INPUT); // set button select pin as input
digitalWrite(selPin, HIGH); // Pull button select pin high
delay(1000); // short delay to let outputs settle
vertZero = analogRead(vertPin); // get the initial values
horzZero = analogRead(horzPin); // Joystick should be in neutral position when reading these

Mouse.begin(); //Init mouse emulation
}

void loop()
{
vertValue = analogRead(vertPin) - vertZero; // read vertical offset
horzValue = analogRead(horzPin) - horzZero; // read horizontal offset

if (vertValue != 0)
Mouse.move(0, (invertMouse * (vertValue / sensitivity)), 0); // move mouse on y axis
if (horzValue != 0)
Mouse.move((invertMouse * (horzValue / sensitivity)*-1), 0, 0); // move mouse on x axis

if ((digitalRead(selPin) == 0) && (!mouseClickFlag)) // if the joystick button is pressed
{
mouseClickFlag = 1;
Mouse.press(MOUSE_LEFT); // click the left button down
}
else if ((digitalRead(selPin)) && (mouseClickFlag)) // if the joystick button is not pressed
{
mouseClickFlag = 0;
Mouse.release(MOUSE_LEFT); // release the left button
}
}

But no luck.

Yes. If I try figures outside of 200 it tends to jam mouse-pointer movement hard left. As if it's exceeding a deadzone of the thumb-stick at rest.

Do you think that code should behave in an analogue fashion, looking at it? It feels like some sort of Atari VCS joystick simulating mouse movement presently for me.

Yes.

Strange that it should work so badly. Especially so for such a simple off-the-shelf extremely common analogue stick.

A0 = x-axis
A1 = y-axis
VCC = 5v
GND = GND

pin-7 + GND for tact-push-button

I have code that converts this physical stick to a USB HID joystick perfectly. Here's part of it....

#define X_AXIS_IN A0 // X-AXIS
#define Y_AXIS_IN A1 // Y-AXIS
#define X_DRIFT_FIX -0
#define Y_DRIFT_FIX -0
#define SENSITIVITY 220

{
xaxis = map(analogRead(X_AXIS_IN), 0, 1023, lowrange, highrange);
yaxis = map(analogRead(Y_AXIS_IN), 0, 1023, highrange, lowrange);

xaxis = ((xaxis) + X_DRIFT_FIX); // STICK DRIFT AND SENSITIVITY FIX
yaxis = ((yaxis) + Y_DRIFT_FIX);

xaxis = (((int)xaxis)*(SENSITIVITY))/100 ;
yaxis = ((yaxis)*SENSITIVITY)/100 ;
Joystick.setXAxis(xaxis);
Joystick.setYAxis(yaxis);
mplex++;
break;
}

Not sure if that helps at all, anyone to help me.

Not really we need to see all the code and for it to be posted correctly.
Use the “ copy for forum “ in the IDE and then paste it into your reply.

The problem you have is that there is a limited range of the pot travel in these joysticks, so only a limited number range.

You have the choice of placing the mouse at a position defined by the number returned which might not cover much of a distance. Or to use the displacement of the mouse to control the velocity of the movement.

Your code seems to do nether.

Run the sketch below and observe the output on Serial Monitor. What values do you get for:

  1. Joystick centered
  2. Joystick full left.
  3. Joystick full right.
  4. Joystick full top.
  5. Joystick full bottom.
  6. Joystick full top left.
  7. Joystick full top right.
  8. Joystick full bottom left.
  9. Joystick full bottom right.
void setup()
{
  Serial.begin(115200);
  delay(200);
}

void loop()
{
  int x = analogRead(A0);
  int y = analogRead(A1);

  Serial.print(x);
  Serial.print(", ");
  Serial.println(y);

  delay(1000);
}
1 Like
  1. Joystick centered - 537, 472
  2. Joystick full left - 241, 446
  3. Joystick full right - 813, 453
  4. Joystick full top - 515, 759
  5. Joystick full bottom - 553, 184
  6. Joystick full top left - 334, 711
  7. Joystick full top right - 744, 674
  8. Joystick full bottom left - 372, 208
  9. Joystick full bottom right - 730, 248

So it looks like quite a bit of the analog range is not used. Instead of +/- 512 you are getting -296 to 276 on X and -288 to 287 on Y.

Since your full range is roughly +/-287 then a scale factor of 200 is quite high. I would suggest you try a scale factor closer to 50. That will give you speeds in X and Y closer to 0 through 5 rather than 0 through 1.

Since, even with a top speed of 1 pixel, your cursor was moving too fast, you may want to limit the frequency of moves by scheduling updates at regular intervals rather than as fast as possible.

1 Like

Thanks so much. I was thrown by the behaviour being so digital. 50 does the trick (issue below).

An improvement to this code I'd love to add are the following:

  1. Deadzone adjustment.
  2. Drift correction (these ALPS sticks vary at rest from stick to stick).
  3. A wider range of sensitivity. I'd like to move the stick very slowly, but at the extremes, very fast.
  4. A button pin dedicated to a latching button option for left-mouse-click (for drag).
  5. A button pin dedicated to right-click.
  6. A button pin dedicated to double-click.
  7. Ideally a button pin that cycles through three speeds (slow, medium, fast).

I'll try to implement some of this but my coding skills aren't quite there yet for Arduino.

#include <Mouse.h>

/* HID Joystick Mouse Example
by: Jim Lindblom
date: 1/12/2012
license: MIT License - Feel free to use this code for any purpose.
No restrictions. Just keep this license if you go on to use this
code in your future endeavors! Reuse and share.

This is very simplistic code that allows you to turn the
SparkFun Thumb Joystick (http://www.sparkfun.com/products/9032)
into an HID Mouse. The select button on the joystick is set up
as the mouse left click.
*/
#include <Mouse.h>
int horzPin = A0; // Analog output of horizontal joystick pin
int vertPin = A1; // Analog output of vertical joystick pin
int selPin = 7; // select button pin of joystick

int vertZero, horzZero; // Stores the initial value of each axis, usually around 512
int vertValue, horzValue; // Stores current analog output of each axis
const int sensitivity = 50; // Higher sensitivity value = slower mouse, should be <= about 500
int mouseClickFlag = 0;

//int invertMouse = 1; //Invert joystick based on orientation
int invertMouse = -1; //Noninverted joystick based on orientation

void setup()
{
pinMode(horzPin, INPUT); // Set both analog pins as inputs
pinMode(vertPin, INPUT);
pinMode(selPin, INPUT); // set button select pin as input
digitalWrite(selPin, HIGH); // Pull button select pin high
delay(1000); // short delay to let outputs settle
vertZero = analogRead(vertPin); // get the initial values
horzZero = analogRead(horzPin); // Joystick should be in neutral position when reading these

Mouse.begin(); //Init mouse emulation
}

void loop()
{
vertValue = analogRead(vertPin) - vertZero; // read vertical offset
horzValue = analogRead(horzPin) - horzZero; // read horizontal offset

if (vertValue != 0)
Mouse.move(0, (invertMouse * (vertValue / sensitivity)), 0); // move mouse on y axis
if (horzValue != 0)
Mouse.move((invertMouse * (horzValue / sensitivity)*-1), 0, 0); // move mouse on x axis

if ((digitalRead(selPin) == 0) && (!mouseClickFlag)) // if the joystick button is pressed
{
mouseClickFlag = 1;
Mouse.press(MOUSE_LEFT); // click the left button down
}
else if ((digitalRead(selPin)) && (mouseClickFlag)) // if the joystick button is not pressed
{
mouseClickFlag = 0;
Mouse.release(MOUSE_LEFT); // release the left button
}
}

Thanks for that tip on posting correctly. Problem/hope for development has moved on below. Could be a very handy bit of code accessibility wise, with drag, double-click help, speed control, and hopefully some improvement for sensitivity (currently it's not easy to get very precise movement when moving gently, and also, fast movement across the screen if moving the stick at full throw).

I do some work with disabled people with an organisation called Drake Music. We make musical instruments and control electronics that is accessible to them.

What I have found is that you need to do something that requires less precise movement. So for a mouse pointing device a large track ball is a much better than a mouse, but better than that is a one dimensional rolling device you could call it a track cylinder. One for each axis.

We have made them using optical shaft encoder or a click-less switch one attached to a roller mounted so that it only sticks out half way through a top panel. The user is free to roll it with their hand rather like the gesture you use to scroll along a touch screen, using the momentum in the cylinder and when the mouse reaches approximately the required position they put there hand on it to stop it moving, and then smaller movement of the hand on the roller to get it into position. This proved easer to use than a track ball and a lot easer to program.

I did make a mouse pointer using a 2 dimensional touch sensor called a Trill touch sensor by Bella instruments. But due to covid I have not had a chance to test it with any disabled user.
This is a video of it meting used for several applications
trill Part 2
This is in python for the raspberry Pi but you can use these sensors on an Arduino.
See the full article in MagPi #103 March 2021 free pdf download available.

Drake do brilliant work. Really enjoyed your video. Very interesting work. Subscribed. Did you see this recent Magic Flute + SWAM + Mini Midi joystick video? https://youtu.be/FacUYxdIyUQ

On the controller front, I've found like you that one-size rarely fits all. For some, a simple joystick mounted well will do the trick. For others all manner of alternative techniques can work well... if you find the right one for the right person.

Regarding getting a nice script working here, I think mimicking some of the functionality of the old Penny and Giles type joysticks would be great. E.g. a pin to cycle through three speed modes... precise... normal use.... fast use. Currently the script doesn't serve slow precision and speed from the same stick very well. It feels like it's one or the other.

Thanks for the link, I had a look at it.

In fact one of our members at Drake Music Labs North was loaned one a bit ago, and here is her video blog describing how she got on with it.
Hanna's Video

I did make a version of this using an air pressure sensor but what was a problem was the condensation onto the sensor which stopped it working after a time. Plus the fact that there was an error in the Adafruit MIDI library for the Seeeduino XIAO processor I used that stopped it sending control codes. Seeeduino XIAO

In fact Hanna made it into a film report on the BBC - I am not sure if these can be viewed outside the UK but here is the link-
Hanna & Chris

There is a video of the rotary encoders we used. I didn't do that bit of the project, I did the motorised sliders but here is Lewis explaining the whole thing. The encoder wheels are towards the end of the video.
Nashesizer
Cheers
Mike

Nice stuff. Hanna seemed to get on well with it there. I am impressed with the Virtual Synthesis stuff people can get more control over nowadays. I remember the Skoog team pushing hard to implement virtual models of real world instruments with their spongy cube device. There's a long scattered interesting history of accessible musical instruments. Someone should write a book (Drake?). I remember the Midi Creator when I used to work in a day-centre, which was an ultra-sonic device (much like the Sound Beam) that was great. Loved the small range ultra-sonic device they used to support.

You might find this interesting Mike over at the OneSwitch Accessible Gaming Museum: https://www.oneswitch.org.uk/art.php?id=318 - It's a bit on MAVIS which was (I believe) the first multi-purpose computer aimed at disabled people. They had a musical sequencer that could be played via sip-puff working in 1977!

By the way, I managed to get alternative code working to do almost everything I wanted, thanks to Graham Law at Celtic Magic. Thanks all for your help here though. Hopefully it will help some others in the future who might scratch their heads like I did.

Thanks, very interesting. I was around computers in those days but had no idea of that.

I built my first micro computer back in 1976 from just the data sheet of the processor. I also wrote the "Body Build" articles in the Micro User Magazine.

Indeed, my book on Arduino Audio doesn't cover specifically interfaces for the disabled.

At Drake we are in the process of setting up a accessible instrument library where people can borrow instruments for a month or so to try. Covid stopped progress, as do the bureaucratic regulations which stop people who contribute to making an accessible instrument being able to be paid for it. This because of the certification required for any electronic device if it is sold, even if it is only a one off.

Maybe you could supply a link to that solution for future reference.