(PARKINSONS DISEASE) mouse project!!

Hi
I'm trying to help my bother out who has Parkinson's disease which makes his had shake from side to side quite rapidly, he uses a computer for his job and is finding it increasingly difficult to hold the mouse still on an icon to click! He has software on his computer at home and work to help with this but he goes out to clients a lot and wanted something USB to take with him and just plug in .
I'm using a Leandro to emulate the mouse and a USB shield to plug a USB mouse into.
I can make the mouse work using USB_Host_Shield_2.0-master - HID - USBHIDBootMouse.
But I am not a great coder and can't get my head around how to filter out the constant left and right shake values to leave me with a steady value representing the middle, without introducing massive lag!
I can't find anything online the even starts me off to hack into something usable.
Any ideas would be much appreciated

I guess you can do that with a running average of the values. But I don't know if the Leonardo has enough memory to do that without introducing a large delay. What are your expectations?

Post the code you're currently working with!

Speed is not a problem.

To perform a running average, you have a cyclic array of values. Once set up you have the accumulated sum; at each step you have the index to the array; using that you subtract the oldest value and add the new value that you then replace in the array. Only one subtraction and one addition needed. Using a binary power number of elements, the necessary division is a simple bit shift, for 256 elements it is a byte shift.

Subject corrected - please correct the subject in your first post!

My grandfather uses a touchpad instead. He moves the cursor to the location he wants, removes the finger and clicks the button. No risk of slipping as mouse often do. You may consider something like that.

I really like the OP’s idea.
With the right backing, this could productised to a significant market opportunity. Include mouse-click jitters as well.

Good luck with your development.
If you have the opportunity, go file a cheap ‘provisionsl’ innovation patent application. Be fast.

(please don’t start yapping on about patents!)

Since the Parkinson's hand-shaking I've observed seems to have a particular frequency, I wonder if a notch filter or low-pass filter may help. A quick google indicates that a moving average has a notch at the sampling rate. Maybe there's a better way to implement a notch, but it's beyond my pay grade. Would need to be tuned to each user's own frequency in a calibration process, and maybe updated on the fly if there is variation over time?

Thats an interesting project. I did a quick feasibility with a Teensy 3.6 which has a USB host built in.

Despite the rather simple approach it works nicely!
Please note: this is just a quick feasibility code for testing and giving you a head start. It definitely needs to be finished to get something working reliable.

Here the code Shouldn't be too difficult to port it to your board (or better: get a T3.6 :slight_smile: )

#include "USBHost_t36/USBHost_t36.h"

//setting for the averager adjust to your needs -------------
constexpr int averagingTime = 200;                  // ms  larger values generate more "damping"
constexpr int deltaT = 5;                           // ms
constexpr int arraySize = averagingTime / deltaT;

//quick and dirty class to manage the moving average. (Can and should be
//optimized.) Also a better filter than a simple moving average might be worth
//trying

class Averager
{
public:
  Averager(int32_t xStart, int32_t yStart) // constructor takes the start position of the connectedMouse
  {
    xAverage = xStart;
    yAverage = yStart;
    for (int i = 0; i < arraySize; i++)
    {
      xArr[i] = xAverage;
      yArr[i] = yAverage;
    }
  }  
  int32_t xAverage, yAverage = 0;

  // add a new point and update the average. 
  // this is just a proof of principle and can be improved a lot
  void addPoint(int32_t newX, int32_t newY)
  {
    xAverage = newX;
    yAverage = newY;

    for (size_t i = 0; i < (arraySize - 1); i++)
    {
      xArr[i] = xArr[i + 1];
      yArr[i] = yArr[i + 1];

      xAverage += xArr[i];
      yAverage += yArr[i];
    }
    xArr[arraySize - 1] = newX;
    yArr[arraySize - 1] = newY;

    xAverage = xAverage / arraySize;
    yAverage = yAverage / arraySize;
  }

private:
  int32_t xArr[arraySize];
  int32_t yArr[arraySize];
};

//-----------------------------------------------------------------------

// setup the USB host to accept a mouse
USBHost myusb;

USBHIDParser hid(myusb);
MouseController connectedMouse(myusb);

// we use an averager with a start position of 500, 500
Averager averager(500, 500);

int32_t absoluteX, absoluteY;        // hold the current mouse position in absolute coordinates
int32_t oldAverageX, oldAverageY;    // to check if something changed

void setup()
{
  myusb.begin();                      // start the USB Host

  Mouse.screenSize(1000,1000);        // configure screen size for the PC mouse

  absoluteX = 500;                    // start values
  absoluteY = 500;
  oldAverageX =  500;
  oldAverageY =  500;
}


elapsedMillis stopWatch = 0;          

void loop()
{
  myusb.Task();                    // call this as often as possible

  if (connectedMouse.available())  // we got new data from the connected mouse -> update the current absolute position
  {
    absoluteX += connectedMouse.getMouseX();  // tbd: limiting to screensize 
    absoluteY += connectedMouse.getMouseY();
    connectedMouse.mouseDataClear();

    // handle mouse buttons tbd...
  }

  if (stopWatch >= deltaT)   // update averager at fixed interval
  {
    stopWatch = 0;
    averager.addPoint(absoluteX, absoluteY);   // add current mouse position

    int x = averager.xAverage;                 // get current averaged values
    int y = averager.yAverage;

    if(x != oldAverageX || y != oldAverageY)  // only move PC mouse if something changed
    {
      oldAverageX = x;
      oldAverageY = y; 
      Mouse.moveTo(x,y);  // move PC mouse to new position
    }    
  }
}

Quick and dirty it is!

Obviously if it works, it works if the hardware is fast enough to do unnecessary processing, but I did explain in #2 how to properly - efficiently - implement a moving average. :astonished: