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

accelestick.cpp

Committer:
thlu
Date:
2014-03-15
Revision:
0:22bdcdc386df
Child:
1:03d0f8a4a2d7

File content as of revision 0:22bdcdc386df:

#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();
}