Use Accelerometer and Joystick to mimic a mouse. This is for a class project. It is done for educational purpose. It is not practical to real world use.

Dependencies:   C12832_lcd MMA7660 USBDevice mbed

Files at this revision

API Documentation at this revision

Comitter:
thlu
Date:
Sat Mar 15 09:32:43 2014 +0000
Child:
1:03d0f8a4a2d7
Commit message:
Got calibration working using x,y, and z-axes

Changed in this revision

C12832_lcd.lib Show annotated file Show diff for this revision Revisions of this file
MMA7660.lib Show annotated file Show diff for this revision Revisions of this file
USBDevice.lib Show annotated file Show diff for this revision Revisions of this file
accelestick.cpp Show annotated file Show diff for this revision Revisions of this file
accelestick.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C12832_lcd.lib	Sat Mar 15 09:32:43 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/dreschpe/code/C12832_lcd/#8f86576007d6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MMA7660.lib	Sat Mar 15 09:32:43 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/Sissors/code/MMA7660/#a8e20db7901e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBDevice.lib	Sat Mar 15 09:32:43 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/USBDevice/#335f2506f422
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/accelestick.cpp	Sat Mar 15 09:32:43 2014 +0000
@@ -0,0 +1,291 @@
+#include "accelestick.h"
+
+inline void reset_tm_joystick_dc()
+{
+    tm_joystick_running = 0;
+    tm_joystick_dc.stop();
+    tm_joystick_dc.reset();
+}
+
+// This function checks for joystick button presses which emulate a mouse's
+//   left button, right button, middle button, double click, and scroll
+//   features. Double click requires a timer
+void get_joystick_input()
+{
+    static uint8_t prev_js = 0;
+    static bool debug_sel_en = 0;
+    static int mouse_scroll = 0;
+
+    mouse_info.button = 0;
+    mouse_info.scroll = 0;
+    mouse_info.dc = 0;
+
+    if (debug_on && debug_sel_en && (joyb != JS_NONE)) {
+        debug_sel_en = 0;
+        debug_sel = joyb;
+    }
+
+    if (debug_on && (debug_sel == JS_NONE) && (joyb == JS_NONE)) {
+        debug_sel_en = 1;
+    }
+
+    switch(joyb) { // 1 = down, 2 = left, 4 = center, 8 = up, 16 = right
+        case  JS_LEFT:
+            // 1 click = left button, 2 clicks = double click
+            if ((prev_js != JS_LEFT) && (tm_joystick_running == JS_LEFT)) { // double click
+                mouse_info.dc = 1;
+                reset_tm_joystick_dc();
+            } else if (!tm_joystick_running) {
+                tm_joystick_running = JS_LEFT;
+                tm_joystick_dc.start();
+            }
+            mouse_info.button = MOUSE_LEFT;
+            break;
+        case  JS_CENTER: // center
+            if ((prev_js != JS_CENTER) && (tm_joystick_running == JS_CENTER)) { // double click
+                calib_on = 1;
+                reset_tm_joystick_dc();
+            } else if (!tm_joystick_running) {
+                tm_joystick_running = JS_CENTER;
+                tm_joystick_dc.start();
+
+            }
+            mouse_info.button = MOUSE_MIDDLE;
+            break;
+        case JS_RIGHT:
+            if ((prev_js != JS_RIGHT) && (tm_joystick_running == JS_RIGHT)) {
+                debug_on = !debug_on;
+                reset_tm_joystick_dc();
+                lcd.cls();
+                lcd.locate(0,0);
+                if (debug_on) {
+                    lcd.printf("Debug print is ON\n");
+                    debug_sel = JS_NONE;
+                    debug_sel_en = 0;
+                }
+            } else if (!tm_joystick_running) {
+                tm_joystick_running = JS_RIGHT;
+                tm_joystick_dc.start();
+            }
+            mouse_info.button = MOUSE_RIGHT;
+            break;
+        case JS_DOWN:  // down
+            if (prev_js == JS_DOWN) {
+                mouse_scroll++;
+            } else {
+                mouse_scroll = 1; // >0 means down
+            }
+            debug.printf("down scroll = %0d\r\n", mouse_info.scroll);
+            reset_tm_joystick_dc();
+            mouse_info.scroll = mouse_scroll;
+
+            break;
+        case JS_UP:  // up
+            if (prev_js == JS_UP) {
+                mouse_scroll--;
+            } else {
+                mouse_scroll = -1;
+            }
+            debug.printf("up scroll = %0d\r\n", mouse_info.scroll);
+            reset_tm_joystick_dc();
+            mouse_info.scroll = mouse_scroll;
+            break;
+    }
+
+
+    if (tm_joystick_dc.read_ms() > 1000) { // timeout for double-click detection
+        reset_tm_joystick_dc();
+    }
+
+    prev_js = joyb;
+}
+
+
+void sample_mma()   // Accelerometer value ranges from -1.5 to 1.5
+{
+    mma_g.x = mma.x();
+    mma_g.y = mma.y();
+    mma_g.z = mma.z();
+    G_int_t current;
+
+    if (mma_g.x > peaks.max_x) {
+        peaks.max_x = mma_g.x;
+    } else if (mma_g.x < peaks.min_x) {
+        peaks.min_x = mma_g.x;
+    }
+    if (mma_g.y > peaks.max_y) {
+        peaks.max_y = mma_g.y;
+    } else if (mma_g.y < peaks.min_y) {
+        peaks.min_y = mma_g.y;
+    }
+    if (mma_g.z > peaks.max_z) {
+        peaks.max_z = mma_g.z;
+    } else if (mma_g.z < peaks.min_z) {
+        peaks.min_z = mma_g.z;
+    }
+
+    current = conv_g2int(mma_g);
+    if (calib_on) {
+        calib_mma(current);
+    }
+    mouse_info.x = current.x - offset.x;
+    mouse_info.y = current.y - offset.y;
+    mouse_info.z = current.z - offset.z;
+    detect_mma_rest();
+}
+
+void drive_mouse()
+{
+    // x-direction is reversed on PC screen
+    mouse.move(-mouse_info.x, mouse_info.y);
+    mouse.press(mouse_info.button);
+    mouse.scroll(mouse_info.scroll);
+    if (mouse_info.dc) {
+        mouse.doubleClick();
+    }
+}
+
+// Used for debugging
+void print_debug_msg()
+{
+    if (debug_on) {
+        switch (debug_sel) {
+            case JS_LEFT:
+                debug.printf("Mx = %1.2f, mx = %1.2f ", peaks.max_x, peaks.min_x);
+                debug.printf("My = %1.2f, my = %1.2f ", peaks.max_y, peaks.min_y);
+                debug.printf("Mz = %1.2f, mz = %1.2f\r\n", peaks.max_z, peaks.min_z);
+                break;
+            case JS_CENTER:
+                debug.printf("x=%1.2f y=%1.2f z=%1.2f\r\n", mma_g.x, mma_g.y, mma_g.z);
+                debug.printf("x=%0d, y=%0d, z=%0d, button=%0d, scroll=%0d, dc=%0d\r\n\n",
+                             mouse_info.x, mouse_info.y, mouse_info.z, mouse_info.button,
+                             mouse_info.scroll, mouse_info.dc);
+                break;
+            case JS_RIGHT:
+                debug.printf("offset.x=%0d, y=%0d, z=%0d\r\n", offset.x, offset.y, offset.z);
+                break;
+        }
+    }
+}
+
+// calibrate the accelerometer for leveling
+void calib_mma(const G_int_t current)
+{
+    static uint8_t ctr = 0;
+    static G_int_t prev[CALIB_SMPLS]; // average array
+
+    int temp;
+
+    if (ctr == 0) {
+        lcd.cls();
+        lcd.locate(0,0);
+        lcd.printf("Calibrating mma");
+        wait(3);
+    } else {
+        lcd.printf(".");
+    }
+    prev[ctr++] = current;
+
+    if (ctr == CALIB_SMPLS) {
+        temp = 0;
+        for (uint8_t i=0; i<CALIB_SMPLS; i++) {
+            debug.printf("CALIB[%0d]: x=%0d, y=%0d, z=%0d\r\n", i, prev[i].x, prev[i].y, prev[i].z);
+            temp += prev[i].x;
+        }
+        offset.x = temp/CALIB_SMPLS; // average
+
+        temp = 0;
+        for (uint8_t i=0; i<CALIB_SMPLS; i++) {
+            temp += prev[i].y;
+        }
+        offset.y = temp/CALIB_SMPLS;
+
+        temp = 0;
+        for (uint8_t i=0; i<CALIB_SMPLS; i++) {
+            temp += prev[i].z;
+        }
+        offset.z = temp/CALIB_SMPLS;
+
+        ctr = 0;
+        calib_on = 0;    // calibration done
+        lcd.cls();
+        lcd.locate(0,0);
+        lcd.printf("Calibration Completed!\n");
+        wait(1);
+    }
+}
+
+void detect_mma_rest ()
+{
+    if ((abs(mouse_info.z) <= 0.06*SCALE_Z) &&
+        (abs(mouse_info.x) <= 0.10*SCALE_X) &&
+        (abs(mouse_info.y) <= 0.10*SCALE_Y)) {
+        mouse_info.x = 0;
+        mouse_info.y = 0;
+    }
+}
+
+// convert mma signed g float value into signed int value
+G_int_t conv_g2int (G_float_t mma_g)
+{
+    G_int_t temp;
+
+    temp.x = (mma_g.x * SCALE_X);
+    temp.y = (mma_g.y * SCALE_Y);
+    temp.z = (mma_g.z * SCALE_Z);
+    return temp;
+}
+
+//
+void flash_led_alive()
+{
+    static bool led_state = 0;
+    led_alive = led_state;
+    led_state = !led_state;
+
+}
+
+int8_t init_accelestick()
+{
+    lcd.cls();
+    reset_tm_joystick_dc();
+
+    offset.x = 0;
+    offset.y = 0;
+    offset.z = 1.0*SCALE_Z;
+
+    peaks.max_x=ACCEL_MIN, peaks.max_y=ACCEL_MIN, peaks.max_z=ACCEL_MIN;
+    peaks.min_x=ACCEL_MAX, peaks.min_y=ACCEL_MAX, peaks.min_z=ACCEL_MAX;
+
+    if (!mma.testConnection()) {
+        lcd.printf("Error in MMA init\n");
+        return -1;
+    }
+    tk_led_alive.attach(&flash_led_alive, 1.0);
+    tk_print_debug.attach(&print_debug_msg, 1.0);
+    return 0;
+
+}
+
+int8_t run_accelestick()
+{
+    int8_t status;
+
+    status = init_accelestick();
+
+    if (status != 0) {
+        return -1;
+    }
+
+    while(1) {
+        get_joystick_input();
+        sample_mma();
+        drive_mouse();
+    }
+
+}
+
+int main()
+{
+    run_accelestick();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/accelestick.h	Sat Mar 15 09:32:43 2014 +0000
@@ -0,0 +1,100 @@
+#include <stdlib.h>
+#include "mbed.h"
+
+#ifndef ACCELESTICK_H
+#define ACCELESTICK_H
+
+#include "MMA7660.h"     // Accelerometer API
+#include "C12832_lcd.h"  // LCD API
+#include "USBMouseKeyboard.h"
+ 
+#define SCALE_X 50        // g value has noise about 0.05
+#define SCALE_Y SCALE_X 
+#define SCALE_Z SCALE_X
+#define ACCEL_MIN -1.5
+#define ACCEL_MAX 1.5
+
+#define CALIB_SMPLS 16   // number of samples used for calibrating accelerometer
+
+// Joystick button values based on joyb declaration
+#define JS_NONE 0
+#define JS_DOWN 1
+#define JS_LEFT 2
+#define JS_CENTER 4
+#define JS_UP 8
+#define JS_RIGHT 16
+
+//--- Global Variables declaration ---//
+
+C12832_LCD lcd;         // for debugging
+MMA7660 mma(p28, p27);  // I2C Accelerometer
+USBMouseKeyboard mouse;
+DigitalOut led_alive(LED4);
+DigitalOut leds[] = {LED1, LED2, LED3};
+
+DigitalIn jd = p12;
+DigitalIn jl = p13;
+DigitalIn jc = p14;
+DigitalIn ju = p15;
+DigitalIn jr = p16;
+BusIn joyb(p12, p13, p14, p15, p16);
+
+typedef struct s_max_min {
+    float max_x, max_y, max_z;
+    float min_x, min_y, min_z;
+} Max_min_t;
+
+typedef struct s_int_g {
+    int16_t x, y, z;
+} G_int_t;
+
+typedef struct s_float_pos {
+    float x, y, z;
+} G_float_t;
+
+typedef struct 
+{
+    int16_t x, y, z;  // x, y, z position
+    uint8_t button;   // left button, 1 = left, 2 = center, 4 = middle
+    bool dc;          // double click
+    int8_t scroll;    // <0 to go up, >0 to go down
+} Mouse_state_t;
+
+Max_min_t peaks;
+Mouse_state_t mouse_info;
+G_int_t offset;       // calibration offset value
+
+bool calib_on = 0;
+bool debug_on = 0;
+uint8_t tm_joystick_running = 0, debug_sel;
+
+G_float_t mma_g;      // g values from accelerometer
+
+Ticker tk_led_alive;  // Ticker for flashing LED as program alive beacon
+Ticker tk_print_debug;// Ticker for printing debug information
+Timer tm_joystick_dc; // Timer for joystick double-click detection
+RawSerial debug(USBTX, USBRX);
+
+// End of Global Variable Declaration
+
+
+//--- Functions Declaration ---//
+
+  // calibrate accelerometer
+  void calib_mma(G_int_t current);
+  
+  // convert floating MMA g value into integer
+  int16_t scale_g(int8_t g);
+  G_int_t conv_g2int(G_float_t mma_g);
+  
+  // detect MMA at rest
+  void detect_mma_rest();
+
+  // Sample joystick for button press
+  void get_joystick_input();
+  
+  inline void reset_tm_joystick_dc();
+  int8_t init_accelestick();
+  int8_t run_accelestick();
+  
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Mar 15 09:32:43 2014 +0000
@@ -0,0 +1,9 @@
+/*
+#include "accelestick.h"
+
+int main()
+{
+    run_accelestick();
+
+}
+*/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Mar 15 09:32:43 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/8e73be2a2ac1
\ No newline at end of file