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]
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.
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.
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]