Using mouse library on micro

I'm trying to help a disabled lady who is finding it hard to use her mouse, she cant hold it still and click.
The idea is to provide a simple box with buttons to provide the clicks.

For now I have a micro with two buttons on pins 3 & 4; and leds to show their state.
The button signals are debouced in software, presently with a 20msec. wait.

My problems:
1: the button isnt proviidng a click until its been pressed twice; then it isnt releasing.
2: the response seems VERY slow since I turn on and off the mouse with begin & end. But I cant leave it on else it takes over.

[code]
/*
   Example program mouse1 for Arduino Micro Pro
   to test digital i/o switches & leds
*/

#include "Mouse.h"

//pin assignments
const byte L_Mouse = 3; //buttons on pins  3 & 4
const byte R_Mouse = 4;
const byte Blu = 9; //leds on 8 & 9
const byte Yel = 8;

//from mouse.h
//#define MOUSE_LEFT 1
//#define MOUSE_RIGHT 2
//#define MOUSE_MIDDLE 4

unsigned long tDebounce = 20; //time switch state must be continuous to count as stable
unsigned long tNow;

struct condition { //declares a type named "condition"
  int switchPin; // pin to read for this switch
  int lastState; //condition when last read
  int stableVal; // last stable value
  unsigned long changeTime; //time of last change
};

condition sw[2] = {  //array of two variables of type "condition"
  {L_Mouse, 1, 1, 0}, //switch on pin 3
  {R_Mouse, 1, 1, 0}  //switch on pin 4
};

int debounce(int i) {
  int s = digitalRead(sw[i].switchPin);
  if (s != sw[i].lastState) {  //its changed
    /*
    Serial.print("Switch S");
    Serial.print(i);
    Serial.print(" changed to ");
    Serial.println(s);
    */
    sw[i].lastState = s; //update lastState
    sw[i].changeTime = millis(); //update time of last change
  }
  else {
    tNow = millis();
    if ( (tNow  - sw[i].changeTime) >= tDebounce) { //its stable
      sw[i].stableVal = s;
    }
  }
  return (sw[i].stableVal);
}

void setup() {
  Serial.begin(57600);
  while (!Serial) {
  // wait for serial port to connect. Needed for native USB
  }
  pinMode(L_Mouse, INPUT_PULLUP);
  pinMode(R_Mouse, INPUT_PULLUP);
  pinMode(Yel, OUTPUT);
  pinMode(Blu, OUTPUT);
}

void loop() {
  int L_State = debounce(0);   // read the input pin
  digitalWrite(Yel, L_State);  // sets the LED to the button's value
  int R_State = debounce(1);  // read the input pin
  digitalWrite(Blu, R_State);  // sets the LED to the button's value


  // if the L mouse button is pressed:
  if (L_State == HIGH) {
    Mouse.begin();
    // if the mouse is not pressed, press it:
    if (!Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.press(MOUSE_LEFT);
      Mouse.end();
    }
  }
  else {
    // if the mouse is pressed, release it:
    Mouse.begin();
    if (Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.release(MOUSE_LEFT);
      Mouse.end();
    }
  }

  // if the R mouse button is pressed:
  if (R_State == HIGH) {
    // if the mouse is not pressed, press it:
    Mouse.begin();
    if (!Mouse.isPressed(MOUSE_RIGHT)) {
      Mouse.press(MOUSE_RIGHT);
      Mouse.end();
    }
  } 
  else {
    Mouse.begin();
    if (Mouse.isPressed(MOUSE_RIGHT)) {
      Mouse.release(MOUSE_RIGHT);
      Mouse.end();
    }
  }
}

[/code]

The Mouse.begin() and Mouse.end() methods do exactly nothing. What does that mean "it takes over"?

If the end() method would do anything it must go out of the if bock.

What does "VERY slow" actually mean? Do you notice a delay in the response? Is that delay constant or does it change from event to event?

Thanks @pylon

Yes I see that. Seems strange.

Testing is tricky because the buttons dont seem to be doing the same thing the mouse buttons do; so eg the release doesnt seem to work.

This page Mouse.click() - Arduino Reference
has this warning:

Notes and Warnings

When you use the Mouse.click() command, the Arduino takes over your mouse! Make sure you have control before you use the command. A pushbutton to toggle the mouse control state is effective.

Not helpful.

I've changed the button inputs to D6 & D7 which seems to make a difference, so perhaps d2 d3 are used for something else in the library function.
The problem seems to be that what takes a single mouse click takes two button clicks.
That is leaving the system in a "button pressed" condition which means when I move the mouse it does a drag.

You used D3 and D4 before, not D2 and D3. All of them aren't used in the library. D2 and D3 are I2C pins but the Micro doesn't have pull-ups on-board, so they aren't treated specially.

That warning is for beginners. The warning is on all pages that references a change in the mouse state. If you upload a sketch to the Arduino that sends mouse clicks every 100ms the inexperienced user will loose control over his machine and will probably not be able to load another sketch onto it's Micro. But it doesn't affect the function of your standard mouse, it will work as before.

No, it takes one button click but two mouse events, press and release.

I'm convinced that this is because of your buggy debouncing. You must ensure that release events don't get prevented just because the press event occurred less than 20ms ago.

Alternatively, could she use a regulars mouse with a separate button for "clicking"? Would be a simple wiring job.

Yes. Purchase cheap mouse. (Less than $5)

Unscrew.

Connect buttons to microswitches on mouse.

Plug into second USB port.

What is it your code does that is special?

Thanks for your replies; its the simplest things .. and shows the importance of a schematic even for a simple circuit like this. Sorry - I should know better!

FYI the buttons are active LOW and the code I unashamedly cribbed from the mouse example has active high.
Fixed that and the code works; left click, click and drag and right click all OK and it no longer interferes with operation by the original mouse.

One problem resolved and a few ahead.
@JohnRob @Paul_B yes I know - but with a Micro I can adapt the program and button response to best suit her condition, such as change the debounce time to cope with tremors.

The next stage (OK its "simple") is to add a button to provide a double left click - which she cannot do unaided.

and @pylon - I'm not taking offence at your secription of my debounce as "buggy", I'd like to know what makes you think that. It DOES work.

I never want to offend someone. As a non-native speaker I might not use the sound terms, my apologizes if I took the wrong ones.

I try to explain what I meant: Let's assume you have a non-bouncing hardware and your actions are quite fast. You want to click just once. You push the button and release it immediately (within 10ms). I would expect your code to loose the release event.

Thanks @pylon , the point of the very slow "debounce" is to prevent the lady's tremors (shakes) from generating unwanted clicks.

The serial i/o on the Micro is causing too many problems, eg not restarting properly from a reset.

I considered using an 8266 NodeMCU (as I have lots) but the mouse library does not seem to be compatible, and apparently the NodeMCU hardware doesnt support USB functions, just serial I/O.

Why is this turning out to be so hard???

I'm wondering now if the Serial I/O is interfering so next job is to try without it. More to follow.

You didn't understand my explanation. It's not the slow debounce, it's the loose of a state change. In my opinion a correct debounce always allows a release after press event, it just avoids the next press event if it came too early. A debounce that allows an event order of:

  • press
  • press
  • press
  • release
  • release
  • press
  • press
  • press

is buggy in my opinion.

Can you explain that in more detail? I never experienced problems in that area.

An ESP8266 doesn't have any USB hardware, most boards use a CP210x USB2UART converter chip to allow the programming over USB.

Because you don't start with simple task. I would have tried the custom hardware with just a serial link to the PC to ensure that this part is working as I expect it. Once that works I would try to add HID functionality.

I don't think so. If there is a problem most probably that is on the PC side.

MicroMouseButtons
Now working.

I've changed the debounce to look for a change AFTER a stable period; and taken this line out.
I'm not happy with the code as its a bit repetitive, but for a one-off I'll leave it as its all working.

[code]

/*
   Example program mousebuttons for Arduino Micro Pro  23 Jan 2022
   to provide mouse clicks for disabled lady
   produces time-controlled clicks as follows:
   left button (L_Mouse) single left click (& hold - ie not released until button released)
   middle button (L2_Mouse) double left click & hold
   right button - R_Mouse single RIght click & hold
   debounce time and dleay between button clicks adjustable.
   When testing can uncomment serial prints and comment out calls to click routines.
*/

#include "Mouse.h"

//pin assignments
const byte L_Mouse = 7; //buttons to GROUND & pulled HIGH so active low
const byte L2_Mouse = 6; //double left click
const byte R_Mouse = 5;
const byte Yel = 8; //LED_L   leds on 8 & 9 via resistors to GROUND so active HIGH
const byte Red = 9; //LED_R

bool L_State, L2_State, R_State;
int tRepeat;  //diagnostic; used in debugging tDelay

unsigned long tDebounce = 200; //time switch state must be continuous to count as stable
unsigned long tDelay = 1000; //delay before a new stable switch condition can cause a click
unsigned long tNow;
unsigned long tLastChange;  //time of the most recent change on ANY button

struct condition { //declares a type named "condition"
  int switchPin; // pin to read for this switch
  bool lastState; //condition when last read
  bool stableVal; // last stable value
  unsigned long changeTime; //time of last change
};

condition sw[3] = {  //array of three variables of type "condition"
  {L_Mouse, 1, 1, 0}, //switch on pin 5
  {L2_Mouse, 1, 1, 0}, //switch on pin 6
  {R_Mouse, 1, 1, 0}  //switch on pin 7
};

void setup() {
  Serial.begin(57600);
  delay(500);
  pinMode(L_Mouse, INPUT_PULLUP);
  pinMode(L2_Mouse, INPUT_PULLUP);
  pinMode(R_Mouse, INPUT_PULLUP);
  pinMode(Yel, OUTPUT);
  pinMode(Red, OUTPUT);
  digitalWrite(Red, LOW);
  digitalWrite(Yel, LOW);
  tLastChange = millis();
  //Serial.println("Setup complete");
}

void loop() {
  //has s1 changed?
  L_State = digitalRead(sw[0].switchPin);
  if (L_State != sw[0].lastState) { //s1 has changed
    sw[0].lastState = L_State; //record current state to look for new changes
    //flashLeft(1); //diagnostic
    //now - has it been stable for > tDebounce?
    tNow = millis();
    if ((tNow - sw[0].changeTime) > tDebounce) {
      //its a valid change
      sw[0].stableVal = L_State;
      //here stableVal represents the true condition of the S1 button
      digitalWrite(Yel, !sw[0].stableVal);
      LClick(sw[0].stableVal);  //only executes a press or release if not already set

      tLastChange = tNow;
      //Serial.print(" Lbutton tLastChange is: ");
      //Serial.println(tLastChange);
    }
    else {
      //not a valid change so not stable; update time of last change
      sw[0].changeTime = tNow;
    }
  }

  //has s2 changed?
  L2_State = digitalRead(sw[1].switchPin);
  if (L2_State != sw[1].lastState) { //s1 has changed
    sw[1].lastState = L2_State; //record current state to look for new changes
    flashLeft(1);

    //now - has it been stable for > tDebounce?
    tNow = millis();
    if ((tNow - sw[1].changeTime) > tDebounce) {
      //its a valid change
      sw[1].stableVal = L2_State;
      //here stableVal represents the true condition of the S1 button
      digitalWrite(Yel, !sw[1].stableVal);
      L2Click(sw[1].stableVal);  //only executes a press or release if not already set
      tLastChange = tNow;
      //Serial.print(" L2button tLastChange is: ");
      //Serial.println(tLastChange);
    }
    else {
      //not a valid change so not stable; update time of last change
      sw[1].changeTime = tNow;
    }
  }

  R_State = digitalRead(sw[2].switchPin);
  if (R_State != sw[2].lastState) { //s3 has changed
    sw[2].lastState = R_State; //record current state to look for new changes
    //now - has it been stable for > tDebounce?
    tNow = millis();
    if ((tNow - sw[2].changeTime) > tDebounce) {
      //its a valid change
      sw[2].stableVal = R_State;
      //here stableVal represents the true condition of the S3 button
      digitalWrite(Red, !sw[2].stableVal);
      RClick(sw[2].stableVal);  //only executes a press or release if not already set

      tLastChange = tNow;
      //Serial.print(" Rbutton tLastChange is: ");
      //Serial.println(tLastChange);
    }
    else {
      //not a valid change so not stable; update time of last change
      sw[2].changeTime = tNow;
    }
  }

  //if any state has changed, AND all buttons released, delay before next is allowed
  if (sw[0].stableVal && sw[1].stableVal && sw[2].stableVal) {
    while ((millis() - tLastChange) <= tDelay) {
      //tRepeat = millis() - tLastChange;
      //Serial.print(" tRepeat is: ");
      //Serial.println(tRepeat);
      delay(100);
    }
  }
} // loop
[/code]
[code]
/*
  struct condition { //declares a type named "condition"
  int switchPin; // pin to read for this switch
  int lastState; //condition when last read
  int stableVal; // last stable value
  unsigned long changeTime; //time of last change
  };
*/

void flashLeft(int count){
  for(int i = 0; i<count; i++){
   //flash LEFT LED
    digitalWrite(Yel, HIGH);
    delay(50);
    digitalWrite(Yel, LOW);
    delay(100);
  }
}

void LClick(bool valueL) {
  if (valueL == 0) { //button is depressed
    if (!Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.press(MOUSE_LEFT);
    }
  }
  else {  //button released
    if (Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.release(MOUSE_LEFT);
    }
  }
}

void L2Click(int valueL) {
  if (valueL == 0) { //button is depressed
    if (!Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.press(MOUSE_LEFT);
      delay(50);
      Mouse.release(MOUSE_LEFT);
      delay(50);
      Mouse.press(MOUSE_LEFT);
      delay(50);
    }
  }
  else {  //button released
    if (Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.release(MOUSE_LEFT);
    }
  }
}

void RClick(int valueR) {
  if (valueR == LOW) {
    if (!Mouse.isPressed(MOUSE_RIGHT)) {
      Mouse.press(MOUSE_RIGHT);
    }
  }
  else {  //button released
    if (Mouse.isPressed(MOUSE_RIGHT)) {
      Mouse.release(MOUSE_RIGHT);
    }
  }
}
[/code]

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.