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-28
- Revision:
- 2:3d012df30652
- Parent:
- 1:03d0f8a4a2d7
File content as of revision 2:3d012df30652:
// Name: T.H. Lu // Date: 03/27/2014 // Description: // This program is not for practical use. It is my first mbed C code, // and is released only because my instructor mandates it. // The Accelestick uses the accelerometer and joystick on the mbed to emulate // a mouse. It is somewhat awkward to use, which is why it is only intended // for educational purpose. // The accelerometer provides the mouse movement, and the joystick acts as // the mouse buttons. A real-time debug mode prints messages onto the USB // serial interface. // Some features are: // 1. left joystick emulates left mouse button (with double click support) // right joystick emulates right mouse button, center position emulates // middle mouse button // 2. up and down joystick positions emulate mouse scroll // 3. push and hold left joystick for 1 second to initiate left mouse button // press and hold feature. LED1 will lit up. Push left joystick again to // release // 4. press joystick center position down twice to initiate accelestick // calibration. This will reduce cursor movement at rest position. LED3 // will flash rapidly and then remain lit when done // 5. push right joystick twice to enable debug mode printing. Debug messages // are written to USB serial (COM port). LED2 will lit when debug is on // LED4 will blink every second if accelestick is initialized properly // #include "accelestick.h" //--- Global Variables declaration ---// C12832_LCD lcd; // for debugging MMA7660 mma(p28, p27); // I2C Accelerometer USBMouseKeyboard mouse; // USB mouse DigitalOut leds[] = {LED1, LED2, LED3, LED4}; DigitalIn jd = p12; // joystick down DigitalIn jl = p13; // joystick left DigitalIn jc = p14; // joystick center DigitalIn ju = p15; // joystick up DigitalIn jr = p16; // joystick right BusIn joyb(p12, p13, p14, p15, p16); // joystick in vector form Max_min_t peaks; Mouse_state_t mouse_info; G_int_t offset; // calibration offset value int mouse_scroll = 0; // mouse scroll value bool calib_on = 0; // calibrate mouse bool debug_on = 0; // print debug message // two joystick active timers: one for long hold detection, one for double click detection uint8_t joystick_timers_active[2] = {JS_NONE,JS_NONE}; uint8_t 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 Timer tm_joystick_lock; // Timer for locking mouse button position RawSerial debug_rs(USBTX, USBRX); // Raw serial is non-blocking, and is used for debugging // End of Global Variable Declaration inline void reset_tm_joystick_dc() { joystick_timers_active[0] = JS_NONE; tm_joystick_dc.stop(); tm_joystick_dc.reset(); } inline void reset_tm_joystick_lock() { joystick_timers_active[1] = JS_NONE; tm_joystick_lock.stop(); tm_joystick_lock.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 and press and hold detections are done using a timer void get_joystick_input() { static uint8_t prev_js = 0; static bool debug_sel_en = 0; static uint8_t mouse_lock = JS_NONE; // clear mouse buttons by default 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; leds[LED_DEBUG] = 1; } 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) && (joystick_timers_active[0] == JS_LEFT)) { // double click left to lock mouse_info.dc = 1; reset_tm_joystick_dc(); } else if (!joystick_timers_active[0]) { joystick_timers_active[0] = JS_LEFT; tm_joystick_dc.start(); } mouse_info.button = MOUSE_LEFT; break; case JS_CENTER: // center if ((prev_js != JS_CENTER) && (joystick_timers_active[0] == JS_CENTER)) { // double click center to calibrate calib_on = 1; reset_tm_joystick_dc(); } else if (!joystick_timers_active[0]) { joystick_timers_active[0] = JS_CENTER; tm_joystick_dc.start(); } mouse_info.button = MOUSE_MIDDLE; break; case JS_RIGHT: if ((prev_js != JS_RIGHT) && (joystick_timers_active[0] == JS_RIGHT)) { // double click left to turn debug on/off 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 { leds[LED_DEBUG] = 0; } } else if (!joystick_timers_active[0]) { joystick_timers_active[0] = JS_RIGHT; tm_joystick_dc.start(); } mouse_info.button = MOUSE_RIGHT; break; case JS_DOWN: // down reset_tm_joystick_dc(); break; case JS_UP: // up reset_tm_joystick_dc(); break; } // Update mouse scroll value switch (joyb) { case JS_DOWN: // down mouse_scroll++; break; case JS_UP: // up mouse_scroll--; break; default: mouse_scroll = 0; } mouse_info.scroll = mouse_scroll; // timeouit for double-click detectino if (tm_joystick_dc.read_ms() > 1000) { reset_tm_joystick_dc(); } // only support mouse lock feature for left button if ((prev_js != joyb) && (joyb == JS_LEFT)) { // click JS_LEFT to unlock if (mouse_lock == JS_LEFT) { mouse_lock = JS_NONE; } else if (joystick_timers_active[1] == JS_NONE) { joystick_timers_active[1] = JS_LEFT; tm_joystick_lock.start(); } } if (joyb != JS_LEFT) { reset_tm_joystick_lock(); // hold JS_LEFT for 1.2 sec to lock } else if ((joystick_timers_active[1] == JS_LEFT) && tm_joystick_lock.read_ms() > 1000) { mouse_lock = JS_LEFT; } prev_js = joyb; if (mouse_lock == JS_LEFT) { mouse_info.button = MOUSE_LEFT; } leds[LED_LOCK] = (mouse_lock == JS_LEFT); } 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 = filter_noise(current.x - offset.x); mouse_info.y = filter_noise(current.y - offset.y); mouse_info.z = filter_noise(current.z - offset.z); } void update_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_rs.printf("Mx = %1.2f, mx = %1.2f ", peaks.max_x, peaks.min_x); debug_rs.printf("My = %1.2f, my = %1.2f ", peaks.max_y, peaks.min_y); debug_rs.printf("Mz = %1.2f, mz = %1.2f\r\n", peaks.max_z, peaks.min_z); break; case JS_CENTER: debug_rs.printf("x=%1.2f y=%1.2f z=%1.2f\r\n", mma_g.x, mma_g.y, mma_g.z); debug_rs.printf(" x=%0d, y=%0d, z=%0d, button=%0d, scroll=%0d, dc=%0d\r\n", mouse_info.x, mouse_info.y, mouse_info.z, mouse_info.button, mouse_info.scroll, mouse_info.dc); break; case JS_RIGHT: debug_rs.printf("offset.x=%0d, y=%0d, z=%0d\r\n", offset.x, offset.y, offset.z); break; case JS_UP: debug_rs.printf("scroll = %0d\r\n", mouse_scroll); break; } } } // Calibrate the accelerometer to work on non-level surface. This function is blocking. void calib_mma(const G_int_t current) { static uint8_t ctr = 0; static G_int_t prev[CALIB_SMPLS]; // average array Ticker tk_led_calib; int temp; tk_led_calib.attach(&flash_led2, 0.05); if (debug_on) { 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_rs.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 if (debug_on) { lcd.cls(); lcd.locate(0,0); lcd.printf("Calibration Completed!\n"); wait(1); lcd.cls(); } tk_led_calib.detach(); leds[LED_CALIB] = 1; } } // int16_t filter_noise (int16_t const num) { if (abs(num) < NOISE_FLOOR) { return 0; } else if (num < 0) { return num + NOISE_FLOOR; } else { return num - NOISE_FLOOR; } } // 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; } // Ticker cannot attach to functions with parameters void flash_led2() { leds[LED_CALIB] = !leds[LED_CALIB]; } void flash_led3() { leds[LED_ALIVE] = !leds[LED_ALIVE]; } int8_t init_accelestick() { lcd.cls(); reset_tm_joystick_dc(); reset_tm_joystick_lock(); 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_led3, 1.0); tk_print_debug.attach(&print_debug_msg, 1.0); return 0; } void run_accelestick() { get_joystick_input(); sample_mma(); update_mouse(); }