Arduino Leonardo mouse emulator using a potentiometer

Dear folks,

I have a problem in my project. This project builds a simple uniaxial mouse emulator by using Arduino Leonardo board and a potentiometer (pot) and a switch. My desire is to achieve X axis mouse control by using a pot. The switch here is to turn on and off the Leonardo mouse control.

My code is working OK but I have one big challenage. The mouse movement controlled by the pot can not cover the whole X range of computer screen, which, in other words, goes from the left end of screen to the right end of screen. I try to use the map syntax and find several codes that use map syntax in joystick mouse control. I post my code here and let me know where I should improve my code. Thanks a lot.

const int switchPin = 2; // switch to turn on and off mouse control
const int xAxis = A0; // joystick X axis
const int ledPin = 13; // Mouse control LED

// parameters for reading the joystick:
int range = 12; // output range of X or Y movement
int responseDelay = 5; // response delay of the mouse, in ms
int threshold = range/4; // resting threshold
int center = range/2; // resting position value

int prevXreading = 0;
int prevYreading = 0;

boolean mouseIsActive = false; // whether or not to control the mouse
int prevSwitchState = LOW; // previous switch state

void setup() {
pinMode(switchPin, INPUT); // the switch pin
pinMode(ledPin, OUTPUT); // the LED pin
// take control of the mouse:
Mouse.begin();
}

void loop() {
// read the switch:
int switchState = digitalRead(switchPin);
// if it's changed and it's high, toggle the mouse state:
if (switchState != prevSwitchState) {
if (switchState == HIGH) {
mouseIsActive = !mouseIsActive;
// turn on LED to indicate mouse state:
digitalWrite(ledPin, mouseIsActive);
}
}

// save switch state for next comparison:
prevSwitchState = switchState;

int currXreading = analogRead(xAxis);

int xReading = prevXreading - currXreading;

prevXreading = currXreading;

// if the mouse control state is active, move the mouse:
if (mouseIsActive) {
Mouse.move(xReading, 0, 0);
}

}

The mouse movement controlled by the pot can not cover the whole X range of computer screen, which, in other words, goes from the left end of screen to the right end of screen.

Why not? What happens?

xReading will be small values, until the total of the xReading values that has been sent is 1023. If your screen is larger than that, you won't be able to move to the extreme right edge without scaling xReading.

hi Paul,

Thank you for reply. What happens is that the coursor moves only in the certain range, whose boundary is much smalled than the boundaries of the screen. Yes, I realize that I need to use map syntax to upscale my analogread to the range of whole range, I just dont know how it should work. I looked some examples for joystick mouse control that uses map(analogread(),0,1023,0,range). But the way that a joystick works is different from potentiometer.

I can attach an example here. Could u give me some suggestion how I should modify it to let it work for a pot.

BTW, how I check if the screen is 1023? Does it mean the resolution of screen?

// set pin numbers for switch, joystick axes, and LED:
const int switchPin = 2; // switch to turn on and off mouse control
const int xAxis = A0; // joystick X axis
const int ledPin = 13; // Mouse control LED

// parameters for reading the joystick:
int range = 12; // output range of X or Y movement
int responseDelay = 5; // response delay of the mouse, in ms
int threshold = range/4; // resting threshold
int center = range/2; // resting position value

boolean mouseIsActive = false; // whether or not to control the mouse
int lastSwitchState = LOW; // previous switch state

void setup() {
pinMode(switchPin, INPUT); // the switch pin
pinMode(ledPin, OUTPUT); // the LED pin
// take control of the mouse:
Mouse.begin();
}

void loop() {
// read the switch:
int switchState = digitalRead(switchPin);
// if it’s changed and it’s high, toggle the mouse state:
if (switchState != lastSwitchState) {
if (switchState == HIGH) {
mouseIsActive = !mouseIsActive;
// turn on LED to indicate mouse state:
digitalWrite(ledPin, mouseIsActive);
}
}
// save switch state for next comparison:
lastSwitchState = switchState;

// read and scale the two axes:
int xReading = readAxis(A0);

// if the mouse control state is active, move the mouse:
if (mouseIsActive) {
Mouse.move(xReading, 0, 0);
}

}

delay(responseDelay);
}

/*
reads an axis (0 for x) and scales the
analog input range to a range from 0 to
*/

int readAxis(int thisAxis) {
// read the analog input:
int reading = analogRead(thisAxis);

// map the reading from the analog input range to the output range:
reading = map(reading, 0, 1023, 0, range);

// if the output reading is outside from the
// rest position threshold, use it:
int distance = reading - center;

if (abs(distance) < threshold) {
distance = 0;
}

// return the distance for this axis:
return distance;
}

But the way that a joystick works is different from potentiometer.

How is it different (aside from the fact that a joystick has two potentiometers?

That mapping is exactly what you need to do.

Thanks Paul. I will try it as soon as I figure out the current newbie question.

My newbie question is that I try to connect my leonardo board to my IBM laptop so I can try my mouse control code at home. In order for me to try IDE coding at my laptop, as you know, I need to install the driver for micro usb at laptop. The problem I run into is that once I connect my leonardo board to laptop through usb, my mouse control seems having taken control an automatically moves to the left end of laptop screen. I then try to press the reset button on leonardo to remove any preconditioned program on the leonardo. This helped for a few seconds that I can control my mouse cursor and then after than the cursor again moves to the left end of my laptop screen. I dont know what the problem is. Could you let me know what happens ? Thanks a lot.

Paul,

I think I have figured out the drifting problem. It seems that the reset button on leonardo board works differently from what I thought. After I read the reset function of ARduino leonardo board on the website, it seems to me that as soon as the board connects laptop or pc, whatever the last code has been written to the board will automatically run on the laptop. My resolution is that I wrote another code such as LED blink to the board, then I take back the control of my mouse.

Regarding to the first question of mouse control, I copy and modify the other code. This code doesn’t work as the way I want. As soon as I load the code and turn on the switch, the mouse starts drifting. Then I try to use the knob of my potentiometer to control the mouse movement. It seems to me that mouse movement is very sensitive to rotation of pot knob. As I rotate the knob slightly, the mouse moves from one end of screen to the other. Could you take the look at the code and let me know what is the problem. Thank you very much.

const int switchPin = 2; // switch to turn on and off mouse control
const int xAxis = A0; // joystick X axis
const int ledPin = 13; // Mouse control LED

int range = 12; // output range of X or Y movement
int responseDelay = 2; // response delay of the mouse, in ms
int threshold = range/16; // resting threshold
int center = range/2; // resting position value
int minima = {
1023}; // actual analogRead minima for {x, y}
int maxima = {
0}; // actual analogRead maxima for {x, y}
int axis = {
xAxis}; // pin numbers for {x, y}
int mouseReading[1]; // final mouse readings for {x, y}

boolean mouseIsActive = false; // whether or not to control the mouse
int lastSwitchState = LOW; // previous switch state

void setup() {
pinMode(switchPin, INPUT); // the switch pin
pinMode(ledPin, OUTPUT); // the LED pin
// take control of the mouse:
Mouse.begin();
}

void loop() {

// read the switch:
int switchState = digitalRead(switchPin);
// if it’s changed and it’s high, toggle the mouse state:
if (switchState != lastSwitchState) {
if (switchState == HIGH) {
mouseIsActive = !mouseIsActive;
// turn on LED to indicate mouse state:
digitalWrite(ledPin, mouseIsActive);
}
}
// save switch state for next comparison:
lastSwitchState = switchState;

// read and scale the two axes:
int xReading = readAxis(0);

// move the mouse:
if (mouseIsActive) {
Mouse.move(xReading, 0, 0);
delay(responseDelay);
}
}
/*
reads an axis (0 or 1 for x or y) and scales the
analog input range to a range from 0 to
*/

int readAxis(int axisNumber) {
int distance; // distance from center of the output range

// read the analog input:
int reading = analogRead(axis[axisNumber]);

// of the current reading exceeds the max or min for this axis,
// reset the max or min:
if (reading < minima[axisNumber]) {
minima[axisNumber] = reading;
}
if (reading > maxima[axisNumber]) {
maxima[axisNumber] = reading;
}

// map the reading from the analog input range to the output range:
reading = map(reading, minima[axisNumber], maxima[axisNumber], 0, range);

// if the output reading is outside from the
// rest position threshold, use it:
if (abs(reading - center) > threshold) {
distance = (reading - center);
}

// return the distance for this axis:
return distance;
}

I would also appreciate help from anybody. Thank you folks.

int minima[] = {
  1023};                // actual analogRead minima for {x, y}
 int maxima[] = {
   0};                       // actual analogRead maxima for {x, y}
 int axis[] = {
   xAxis};              // pin numbers for {x, y}
 int mouseReading[1];          // final mouse readings for {x, y}

How are these one element arrays more useful that non-array variables?

int range = 12;               // output range of X or Y movement

If a value of 1023 did not get the mouse all the way across the screen, how will a value of 12 ever get there?

 int threshold = range/16;      // resting threshold
 int center = range/2;         // resting position value

So, threshold is 12/16 or 0. Is that by design?

Hi Paul,

Thank you for kindly pointing out these flaws.

Code:

int range = 12; // output range of X or Y movement
(you say: If a value of 1023 did not get the mouse all the way across the screen, how will a value of 12 ever get there?)
About the number of 1023, does it have something to do with the resolution of screen? So how I should set up the mapping function?

Code:

int threshold = range/16; // resting threshold
int center = range/2; // resting position value
(You say: So, threshold is 12/16 or 0. Is that by design?)
I saw the examples use threshold = range/4. I have no idea why they design that way and I try to play around with it.

My another observation is that as soon as I load the code and turn on the switch, the mouse starts drifting. Then I try to use the knob of my potentiometer to control the mouse movement. It seems to me that mouse movement is very sensitive to rotation of pot knob. As I rotate the knob slightly, the mouse moves from one end of screen to the other. What do you think about that?

About the number of 1023, does it have something to do with the resolution of screen? So how I should set up the mapping function?

Yes, the number 1023 is the number of pixels that the mouse actually moves. If your screen is wider than that, the mouse won't make it all the way across the screen. If it is narrower than that, the mouse will run off the screen.

You need to know what your screen resolution, in pixels, is, and map the potentiometer (or joystick) output to that range.

As I rotate the knob slightly, the mouse moves from one end of screen to the other.

There are two ways to control the mouse - using relative movements and using absolute movements. The code you have is using relative movements (the difference between the current potentiometer output and the previous amount is used to move the mouse a little bit). Perhaps absolute movements are more appropriate.

Quote

As I rotate the knob slightly, the mouse moves from one end of screen to the other.
There are two ways to control the mouse - using relative movements and using absolute movements. The code you have is using relative movements (the difference between the current potentiometer output and the previous amount is used to move the mouse a little bit). Perhaps absolute movements are more appropriate.

so do you mean I dont need to calculate the distance. Instead, I just need to send out whatever from mapping. But I thought conceptually that the smooth control of mouse movement can only be achieved by relative movement.

Thanks, Paul.

Paul,

how to find the number of pixels that the mouse actually moves? It has something to do with the screen resolution. I am very serious with this project I have now. Do you mind if you keep track of my update. I really appreciate it, Paul.

Ji

You can't really do absolute movement (without cooperation of software on the PC) because you don't know the initial position of the mouse.

I thought your idea was that there was a button to enable and disable your virtual 'mouse' so that you could press the button, move the joystick (and have the mouse move a proportionate distance on the screen) and then release the button to 'drop' the mouse. Isn't that your scheme? If not, how's it going to work bearing in mind that the initial position of the mouse is unknown and the only method available to you to move the mouse is by applying a relative movement; you can't simply make the mouse position follow the joystick position.

Peter,

Yes, that was my scheme. I use a physical switch (which connects to pin 2) to turn on and off the mouse control from leonardo board. Yes, my scheme is using relative movement instead of absolute movement. If you review my previous messages on this topic, I have trouble scaling the movement to the whole screen of my computer. I am trying to use the function MAP() to scale the movement to the whole screen level. For example, map(reading, 0, 1023, 0, range). I was told that 1023 is not correct. how should I find the right number to put in? I was told that it has something to do with the screen resolution. (my screen resolution is 1366*768, i want to control horizontal axis movement, should I use 1366).

However, even when I use 1023 in my map function, one observation is that as soon as I load the code and turn on the switch, the mouse starts drifting. Then I try to use the knob of my potentiometer to control the mouse movement. It seems to me that mouse movement is very sensitive to rotation of pot knob. As I rotate the knob slightly, the mouse moves from one end of screen to the other. What do you think about that?

I was told that 1023 is not correct.

No. You were told that 12, the value in range, was incorrect. You (should) know the resolution of your screen. It is the width, in pixels, that should be the value in range.

(my screen resolution is 1366*768, i want to control horizontal axis movement, should I use 1366).

For the value in range, yes.

The drifting can be stopped by not sending values when the difference is less that some small value, like 2. Don't update the previous value, either.

As I rotate the knob slightly, the mouse moves from one end of screen to the other.

You have not told us anything about the configuration of the "mouse" on the PC. If the Leonardo is emulating a mouse, it is going to do it all the way, acceleration and all.

Hi Paul,

Thanks. I see what you mean by range. My current observation is that even when I turn the pot knob one direction, the cursor moves to the ends of screen back and forth. I have no clue what happens. You mention about the configuration of mouse. Should I check the properties of mouse on my laptop.Thanks

The following is the updated code.

// set pin numbers for switch, and LED:
const int switchPin = 2; // switch to turn on and off mouse control
const int xAxis = A0; // joystick X axis
const int ledPin = 13; // Mouse control LED

// parameters for reading the joystick:
int range = 1366; // output range of X or Y movement
int responseDelay = 5; // response delay of the mouse, in ms
int threshold = range/4; // resting threshold
int center = range/2; // resting position value

boolean mouseIsActive = false; // whether or not to control the mouse
int lastSwitchState = LOW; // previous switch state

void setup() {
pinMode(switchPin, INPUT); // the switch pin
pinMode(ledPin, OUTPUT); // the LED pin
// take control of the mouse:
Mouse.begin();
}

void loop() {
// read the switch:
int switchState = digitalRead(switchPin);
// if it’s changed and it’s high, toggle the mouse state:
if (switchState != lastSwitchState) {
if (switchState == HIGH) {
mouseIsActive = !mouseIsActive;
// turn on LED to indicate mouse state:
digitalWrite(ledPin, mouseIsActive);
}
}
// save switch state for next comparison:
lastSwitchState = switchState;

// read and scale the two axes:
int xReading = readAxis(A0);

// if the mouse control state is active, move the mouse:
if (mouseIsActive) {
Mouse.move(xReading, 0, 0);
}

delay(responseDelay);
}

/*
reads an axis (0 for x) and scales the
analog input range to a range from 0 to
*/

int readAxis(int thisAxis) {
//int distance = 0; //distance from center of the output range
// read the analog input:
int reading = analogRead(thisAxis);

// map the reading from the analog input range to the output range:
reading = map(reading, 0, 1023, 0, range);

// if the output reading is outside from the
// rest position threshold, use it:
int distance = reading - center;

if (abs(distance) < threshold) {
distance = 0;
}

// return the distance for this axis:
return distance;
return reading;
}

 return distance;
 return reading;

One return statement per function is the general recommendation.

Your threshold is 1/4 of the screen. Is that really a valid distance?

Perhaps what you need to do is quit trying to be a mouse, until you get the code debugged. Use Serial.begin() (in setup()) and Serial.print(ln)() in other functions, to see what is going on.

Sorry for the confusing. I removed the very last line of my code (return reading), the problem is still there. I feel a little lost. As you said, the joystick is essentially two pots. I saw other people use pretty much same code as I have used to make mouse control. Why can I use this code ( with removing whatever code lines for vertical axis) to just achieve the horizontal mouse control. In their code for mouse control, the range they use are all 12. For example, this is an example: http://idea-dev-storage.de/?p=211#more-211.

In their code for mouse control, the range they use are all 12.

They are splitting the potentiometer reading into 12 ranges.

What happens if you run that code, but change:

  int yReading = readAxis(A1);

to

  int yReading = 0;

Thanks, Paul. I tried it. The cursor still swings back and forth. But this following code seems working for my purpose. Again, I still use the relative movement input instead of absolute movement. Any suggestion for improvement or any comments on flaws. Here the range I put is 2000 (although x y coordinates of screen is 1366*766). I tried 1366 for the range,but the cursor can not movement from one end of screen to the other (from 0v to 5v). The range of 2000, however, works fine. Any thoughts on why it happens like this way. Thanks a lot, Paul. You are a great helper.

const int switchPin = 2; // switch to turn on and off mouse control
const int xAxis = A0; // joystick X axis
const int ledPin = 13; // Mouse control LED

// parameters for reading the joystick:
int range =2000; // output range of X or Y movement
int responseDelay = 5; // response delay of the mouse, in ms
// int threshold = range/4; // resting threshold
// int center = range/2; // resting position value

int prevXreading = 0;

boolean mouseIsActive = false; // whether or not to control the mouse
int prevSwitchState = LOW; // previous switch state

void setup() {
pinMode(switchPin, INPUT); // the switch pin
pinMode(ledPin, OUTPUT); // the LED pin
// take control of the mouse:
Mouse.begin();
}

void loop() {
// read the switch:
int switchState = digitalRead(switchPin);
// if it's changed and it's high, toggle the mouse state:
if (switchState != prevSwitchState) {
if (switchState == HIGH) {
mouseIsActive = !mouseIsActive;
// turn on LED to indicate mouse state:
digitalWrite(ledPin, mouseIsActive);
}
}

// save switch state for next comparison:
prevSwitchState = switchState;

int currXreading = analogRead(xAxis);

int currXreading1 = map (currXreading, 0, 1023, 0, range);

int xReading = prevXreading - currXreading1;

// xReading = map(xReading, 0, 1023, 0, 20);

prevXreading = currXreading1;

// if the mouse control state is active, move the mouse:
if (mouseIsActive) {
Mouse.move(xReading, 0, 0);
}

}