Hand Free Mouse

I'd like to build a hands free mouse. I'm thinking a 9dof attached to a hat brim and possibly bluetooth connected to a promicro. Any ideas, keep in mind I'm a noob

Yes a micro will do it.

If the micro is not on the hat and you are using an I2C sensor (most of them) then long wires will perform poorly for I2C. I have had trouble with wires as short as 20cm but you can usually expect 100cm to work.

Sounds cool.
You ask for ideas.

  1. Google. See what other people have done that is similar.
  2. Work in small steps. Get a 9dof sensor and play with it. Connect it to your arduino. Write a simple sketch that reads the values and prints them to the serial monitor. If you are a complete noob (not being insulting, just trying to help) start out with Blink examples to get familiar.
  3. Come here often with any questions that you have. Be aware that the best way to ask for help is to present you code (use code tags), state what you expected it to do, state what actually happened and state how those two differ.

Put a 3D compass on top of the head, over the neck turn axis. They're good to a degree or two in 3 dimensions and not need to calculate from forces measured. Incorporate headphones and boom mike and think about touch buttons on the headband sides and brim, there's at least 2 ways to DIY the deal.
With an accelerometer you can get gestures easier.
I would use a Micro or Teensy mounted on the side opposite the boom mic and run USB as HID to the PC.

Touch button can be made from a piezo disk (I bought bags of 10 cheap one year.), a diode, a BJT, wire and heatshrink tube.
The piezo disk is like a capacitor that charges when it's squeezed. If there's a path from one side to the next, charges move. The BJT lets the piezo surge open the gate even to a light touch, a resistor can tune that. The diode is for when the disk is released and the charges need to flow back.

Diodes and things made of diodes can take a lot from a piezo disk and just sort of flatten it a bit. The BJT won't deliver the Arduino pin more than the collector is given and with a high value pulldown you might read the pin a few times for a tap and once for just a touch.

Thanks for the feedback!

This is to replace TrackIR?

GoForSmoke:
This is to replace TrackIR?

No, at the moment I'm using a gyro wired to a mouse. The company I got it from is long out of business. I'm not a fan of the TrackIR types being I'm paralyzed and can only move my head.

My plan is to attach a promicro to a dof9 stick, if it works well enough I'll try to bluetooth it, but for now I just want to see if it will work for me.

Does anyone know if I need more than the promicro and dof9?

You said you wanted Bluetooth?

But you could get started with the Pro Micro and a USB cable. The 9DOF sensor can be just glued onto the top of the micro and run short wires where they need to go.

I've used the Sparkfun LSM9DS1 and MahoneyAHRS.h library on a Micro and it came together very quickly. I wasn't using USB however. I don't know if it will fit in the Micro's limited memory. I had to trim a few of the "clever" ideas I had to make it fit in the available memory.

The Micro does USB in hardware, it's pre-fit.

later .... oh, wait. You didn't have room for HID commands?

The Micro came from the PJRC Teensy 2.0. They make a Teensy++ 2.0 (got one) with 8K RAM and more flash and pins that should get you the USB too.

They also make the $12 Teensy LC with an ARM-M0 and lots of features. It is 3.3V with lower pin max mA than most AVR Arduinos but hey they run 6x as fast and 4x as wide.

Ok here's what I have, I bought a pro micro attached to a MPU925X+280 9DOF/10DOF. I am ignorant on coding but here goes nothing

Here is the code I found for IMU mouse in air. I don't need the left/right mouse so can I just delete them? And I'm getting an error: 'Quaternion does not name a type' No idea what that means.

(code tags added by moderator who was tired of scrolling thru all that)

#include <Mouse.h>
#include "Wire.h"
#include "I2Cdev.h"
#include <MPU9250.h>
#include <quaternionFilters.h>

#define OUTPUT_READABLE_YAWPITCHROLL

#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
bool blinkState = false;

bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

Quaternion q;           // [w, x, y, z]         quaternion container
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
float yaw, pitch, roll;

int left_button_pin = 9; // Left button
int right_button_pin = 10; // right button
int leftClickFlag = 0;
const int sensitivity = 30;
float vertZero, horzZero;
float vertValue, horzValue;  // Stores current analog output of each axis

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
   mpuInterrupt = true;
}

// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================

void setup() 
{
 Wire.begin();                                // join I2C bus (I2Cdev library doesn't do this automatically)
 Serial.begin(115200);                       // initialize serial communication
 while (!Serial);                            // wait for Leonardo enumeration, others continue immediately
 mpu.initialize();
 devStatus = mpu.dmpInitialize();
  if (devStatus == 0) 
  {
     mpu.setDMPEnabled(true);                // turn on the DMP, now that it's ready
     attachInterrupt(0, dmpDataReady, RISING);     // enable Arduino interrupt detection
     mpuIntStatus = mpu.getIntStatus();
     dmpReady = true;                        // set our DMP Ready flag so the main loop() function knows it's okay to use it
     packetSize = mpu.dmpGetFIFOPacketSize();      // get expected DMP packet size for later comparison
 } 
 else 
 {                                          // ERROR!        1 = initial memory load failed         2 = DMP configuration updates failed        (if it's going to break, usually the code will be 1)
     Serial.print(F("DMP Initialization failed (code "));
     Serial.print(devStatus);
     Serial.println(F(")"));
 }
 pinMode(LED_PIN, OUTPUT);                 // configure LED for output
 pinMode(left_button_pin, INPUT);
 pinMode(right_button_pin, INPUT);
 yaw = 0.0;
 pitch = 0.0;
 roll = 0.0;
 vertZero = 0;
 horzZero = 0;
}

// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================

void loop() {

   if (!dmpReady) return;                                                    // if programming failed, don't try to do anything
   mpuInterrupt = true;
   fifoCount = mpu.getFIFOCount();                                           // get current FIFO count
   if ((mpuIntStatus & 0x10) || fifoCount == 1024)                           // check for overflow (this should never happen unless our code is too inefficient)
   {
       mpu.resetFIFO();                                                      // reset so we can continue cleanly
       Serial.println(F("FIFO overflow!"));
   } 
   else if (mpuIntStatus & 0x01)                                             // otherwise, check for DMP data ready interrupt (this should happen frequently)
   {    
       while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();        // wait for correct available data length, should be a VERY short wait
       mpu.getFIFOBytes(fifoBuffer, packetSize);                             // read a packet from FIFO
       fifoCount -= packetSize;                                              // track FIFO count here in case there is > 1 packet available
       #ifdef OUTPUT_READABLE_YAWPITCHROLL                                               // display Euler angles in degrees
           mpu.dmpGetQuaternion(&q, fifoBuffer);
           mpu.dmpGetGravity(&gravity, &q);
           mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
           yaw = ypr[1] /PI * 180;
           pitch = ypr[2] /PI * 180;
           roll = ypr[0] /PI * 180;
           Serial.print("ypr\t");
           Serial.print(yaw);
           Serial.print("\t");
           Serial.print(pitch);
           Serial.print("\t");
           Serial.println(roll);
       #endif
       blinkState = !blinkState;                                             // blink LED to indicate activity
       vertValue = yaw - vertZero;
       horzValue = roll - horzZero;
       vertZero = yaw;
       horzZero = roll;   
       if (vertValue != 0)
         Mouse.move(0, vertValue * sensitivity, 0);                                      // move mouse on y axis
       if (horzValue != 0)
         Mouse.move(horzValue * sensitivity, 0, 0);                                      // move mouse on x axis

       if ((digitalRead(left_button_pin))&&(!leftClickFlag))
       {
         leftClickFlag = 1;
         Mouse.press(MOUSE_LEFT);
       }
       else if ((digitalRead(left_button_pin))&&(leftClickFlag))
       {
         leftClickFlag = 0;
         Mouse.release(MOUSE_LEFT);
       }
       if (digitalRead(right_button_pin))
       {
         Mouse.click(MOUSE_RIGHT);
       }
   }
}

'Quaternion does not name a type'

Typically means you are missing a library, or it is installed in the wrong place.

Feargaill:
error: 'Quaternion does not name a type' No idea what that means.

Type is data type as in int, byte, long, char and any types that the programmer defines, not usually a beginner thing.

Feargaill:
No, at the moment I'm using a gyro wired to a mouse. The company I got it from is long out of business. I'm not a fan of the TrackIR types being I'm paralyzed and can only move my head.

My plan is to attach a promicro to a dof9 stick, if it works well enough I'll try to bluetooth it, but for now I just want to see if it will work for me.

Does anyone know if I need more than the promicro and dof9?

Wonderful idea, but I can take my hand off the mouse and it won't do anything until I put my hand back on it and move it. You need a method to tell your mouse to stop moving until you tell it to move once more.

Paul

This code checks out once I added the Quaternion library (thanks CrossR!) My new problem is uploading, it just keeps running, it reads 'busy':

Using Port : COM3

Using Programmer : arduino

Overriding Baud Rate : 19200

Push reset twice and then upload. The timing might be important - press reset just before the compilation finishes and starts the upload. From memory, you get about 8 seconds.

I don't see a reset button

Something else using com port 3.
Maybe another port to be used
No usb hubs in between?
Also did you try rebooting your pc?
I have had the bussy error before have never figured what the problem was usualy rebooting worked for me...

Thank you Bringamosa rebooting helped

Now I'm trying to use this sketch:

#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050_9Axis_MotionApps41.h"
MPU6050 mpu;

#define OUTPUT_READABLE_YAWPITCHROLL

#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
bool blinkState = false;

bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

Quaternion q;           // [w, x, y, z]         quaternion container
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
float yaw, pitch, roll;

int left_button_pin = 9; // Left button
int right_button_pin = 10; // right button
int leftClickFlag = 0;
const int sensitivity = 30;
float vertZero, horzZero;
float vertValue, horzValue;  // Stores current analog output of each axis

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}

// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================

void setup() 
{
  Wire.begin();                                // join I2C bus (I2Cdev library doesn't do this automatically)
  Serial.begin(115200);                       // initialize serial communication
  while (!Serial);                            // wait for Leonardo enumeration, others continue immediately
  mpu.initialize();
  devStatus = mpu.dmpInitialize();
   if (devStatus == 0) 
   {
      mpu.setDMPEnabled(true);                // turn on the DMP, now that it's ready
      attachInterrupt(0, dmpDataReady, RISING);     // enable Arduino interrupt detection
      mpuIntStatus = mpu.getIntStatus();
      dmpReady = true;                        // set our DMP Ready flag so the main loop() function knows it's okay to use it
      packetSize = mpu.dmpGetFIFOPacketSize();      // get expected DMP packet size for later comparison
  } 
  else 
  {                                          // ERROR!        1 = initial memory load failed         2 = DMP configuration updates failed        (if it's going to break, usually the code will be 1)
      Serial.print(F("DMP Initialization failed (code "));
      Serial.print(devStatus);
      Serial.println(F(")"));
  }
  pinMode(LED_PIN, OUTPUT);                 // configure LED for output
  pinMode(left_button_pin, INPUT);
  pinMode(right_button_pin, INPUT);
  yaw = 0.0;
  pitch = 0.0;
  roll = 0.0;
  vertZero = 0;
  horzZero = 0;
}

// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================

void loop() {

    if (!dmpReady) return;                                                    // if programming failed, don't try to do anything
    mpuInterrupt = true;
    fifoCount = mpu.getFIFOCount();                                           // get current FIFO count
    if ((mpuIntStatus & 0x10) || fifoCount == 1024)                           // check for overflow (this should never happen unless our code is too inefficient)
    {
        mpu.resetFIFO();                                                      // reset so we can continue cleanly
        Serial.println(F("FIFO overflow!"));
    } 
    else if (mpuIntStatus & 0x01)                                             // otherwise, check for DMP data ready interrupt (this should happen frequently)
    {    
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();        // wait for correct available data length, should be a VERY short wait
        mpu.getFIFOBytes(fifoBuffer, packetSize);                             // read a packet from FIFO
        fifoCount -= packetSize;                                              // track FIFO count here in case there is > 1 packet available
        #ifdef OUTPUT_READABLE_YAWPITCHROLL                                               // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            yaw = ypr[1] /PI * 180;
            pitch = ypr[2] /PI * 180;
            roll = ypr[0] /PI * 180;
            Serial.print("ypr\t");
            Serial.print(yaw);
            Serial.print("\t");
            Serial.print(pitch);
            Serial.print("\t");
            Serial.println(roll);
        #endif
        blinkState = !blinkState;                                             // blink LED to indicate activity
        vertValue = yaw - vertZero;
        horzValue = roll - horzZero;
        vertZero = yaw;
        horzZero = roll;   
        if (vertValue != 0)
          Mouse.move(0, vertValue * sensitivity, 0);                                      // move mouse on y axis
        if (horzValue != 0)
          Mouse.move(horzValue * sensitivity, 0, 0);                                      // move mouse on x axis

        if ((digitalRead(left_button_pin))&&(!leftClickFlag))
        {
          leftClickFlag = 1;
          Mouse.press(MOUSE_LEFT);
        }
        else if ((digitalRead(left_button_pin))&&(leftClickFlag))
        {
          leftClickFlag = 0;
          Mouse.release(MOUSE_LEFT);
        }
        if (digitalRead(right_button_pin))
        {
          Mouse.click(MOUSE_RIGHT);
        }
    }
}

However, when I add the library for MPU9250 I get "fatal error: arduino.h: No such file or directory"
I've tried other sketches like https://www.himix.lt/arduino/arduino-pro-micro-and-mpu-9250/ and get the same error

Also I don't have buttons to turn it on/off does anyone think using the zaxis might work?