1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

[Completed] CH Products Trackball Pro - BUSMOUSE to USB

Discussion in 'Project Logs' started by GuilleAcoustic, 17 Sep 2014.

  1. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    72
    Last update:

    I finally took the time to solder the Arduino PRO Micro to the new cable harness.

    [​IMG]

    [​IMG]

    I made a temporary cable from a micro USB cable. I just got the casing off of the micro USB connector.

    [​IMG]

    I'll do some proper sleeving during the weekend and a strain releaser from epoxy in other to have a clean finish. At least, no more code nor soldering and the controller is now inside the case.

    Thanks a lot for everyone who followed this and for the support I got from all of you.

    The code is much more compact and optimzed:

    Code:
    /* =================================================================================
       Author  : GuilleAcoustic
       Date    : 2015-05-16
       Revision: V1.0
       Purpose : Opto-mechanical trackball firmware
       ---------------------------------------------------------------------------------
       Wiring informations: Sparkfun Pro micro (Atmega32u4)
       ---------------------------------------------------------------------------------
         - Red             : Gnd                         |  Pin: Gnd
         - Orange          : Vcc (+5V)                   |  Pin: Vcc
         - Yellow          : X axis encoder / channel A  |  Pin: INT0 - SCL
         - Green           : X axis encoder / channel B  |  Pin: INT1 - SDA
         - Blue            : Y axis encoder / channel A  |  Pin: INT2 - Rx
         - Violet          : Y axis encoder / channel B  |  Pin: INT3 - Tx
         - Grey            : Switch 1                    |  Pin: PB3  - MISO
         - White           : Switch 2                    |  Pin: PB2  - MOSI
         - Black           : Switch 3                    |  Pin: PB1  - SCK
       ================================================================================= */
    
    // =================================================================================
    // Type definition
    // =================================================================================
    typedef struct
    {
      int8_t  coordinate = 0;
      uint8_t index      = 0;
    } ENCODER_;
    
    // =================================================================================
    // Constant for binary mask
    // =================================================================================
    #define  _SWITCH_1    B1000
    #define  _SWITCH_2    B0100
    #define  _SWITCH_3    B0010
    
    // =================================================================================
    // Constants
    // =================================================================================
    const int8_t lookupTable[] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0};
    
    // =================================================================================
    // Volatile variables
    // =================================================================================
    volatile ENCODER_ xAxis;
    volatile ENCODER_ yAxis;
    
    // =================================================================================
    // the setup function runs once when you press reset or power the board
    // =================================================================================
    void setup()
    {
      // Attach interruption to encoders channels
      attachInterrupt(0, ISR_HANDLER_X, CHANGE);
      attachInterrupt(1, ISR_HANDLER_X, CHANGE);
      attachInterrupt(2, ISR_HANDLER_Y, CHANGE);
      attachInterrupt(3, ISR_HANDLER_Y, CHANGE);
      
      // Start the mouse function
      Mouse.begin();
    }
    
    // =================================================================================
    // The loop function runs over and over again forever
    // =================================================================================
    void loop()
    {
      // Update mouse coordinates
      if (xAxis.coordinate != 0 || yAxis.coordinate != 0)
      {
        Mouse.move(xAxis.coordinate, yAxis.coordinate);
        xAxis.coordinate = 0;
        yAxis.coordinate = 0;
      }
    
      // Update buttons state
      !(PINB & _SWITCH_1) ? Mouse.press(MOUSE_LEFT)   : Mouse.release(MOUSE_LEFT);
      !(PINB & _SWITCH_2) ? Mouse.press(MOUSE_RIGHT)  : Mouse.release(MOUSE_RIGHT);
      !(PINB & _SWITCH_3) ? Mouse.press(MOUSE_MIDDLE) : Mouse.release(MOUSE_MIDDLE);
    
      // Wait a little before next update
      delay(10);
    }
    
    // =================================================================================
    // Interrupt handlers
    // =================================================================================
    void ISR_HANDLER_X()
    {
      // Build the LUT index from previous and new data
      xAxis.index = ((xAxis.index << 2) | ((PIND & 0b0011) >> 0)) & 0b1111;
    
      // Compute the new coordinates  
      xAxis.coordinate += lookupTable[xAxis.index];
    }
    
    void ISR_HANDLER_Y()
    {
      // Build the LUT index from previous and new data
      yAxis.index = ((yAxis.index << 2) | ((PIND & 0b1100) >> 2)) & 0b1111;
    
      // Compute the new coordinates  
      yAxis.coordinate += lookupTable[yAxis.index];
    }
     
  2. r4tch3t

    r4tch3t hmmmm....

    Joined:
    17 Aug 2005
    Posts:
    3,166
    Likes Received:
    48
    Awesome work man. I love to see things done with micros.
     
  3. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    72
    Thanks for you interest in my humble project.

    I'll start a new thread pretty soon, as I'm currently working on a scratch built laser trackball. I recently bought this sensor from Tindie : https://www.tindie.com/products/jkicklighter/adns-9800-optical-laser-sensor/

    [​IMG]

    This is a self calibrating 8200 CPI laser sensor. I'll use it to build a trackball from scratch, with the following features :

    - Wireless (bluetooth / zigbee)
    - Cue ball sized trackball
    - Omron switches
    - Cherry MX fonction buttons (5 or 6 me think)
    - LCD and EEPROM to store / load user profiles

    I planned a driverless device, fully HID compliant, that can be programmed from the device itself (through the LCD and buttons). Programmation includes shortcuts (Ctrl+C, Ctrl+V) or macros.

    I'll create a thread once the firsts 3D designs are done.
     
  4. r4tch3t

    r4tch3t hmmmm....

    Joined:
    17 Aug 2005
    Posts:
    3,166
    Likes Received:
    48
    I saw that in the latest purchases thread, I was wondering if you were going to do a scratch build or mod it into this one.
    Have you checked out electrodragon?
    They have some interesting things.
     
  5. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    72
    That will be a scratch build. Way more fun and a good way to release some stress from work.

    Didn't know about them, but now I have it bookmarked. Thanks a lot for the link. I'm not decided on the hardware side, except for the sensor and I'll probably build several prototypes.
     
  6. jinq-sea

    jinq-sea 'write that down in your copy book' Super Moderator

    Joined:
    15 Oct 2012
    Posts:
    8,823
    Likes Received:
    721
    I'm well impressed! :D
     
  7. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    72
    Thanks a lot. I use the trachball as my daily driver now and it's a real pleasure. Less stressing than the mouse.
     
  8. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    72
    Last modification:

    • Added software deboucing to all switches
    • Code cleanup
    Code:
    #include <Mouse.h>
    
    /* ================================================================================
       Author  : GuilleAcoustic
       Date    : 2015-05-22
       Revision: V1.1
       Purpose : Opto-mechanical trackball firmware
       --------------------------------------------------------------------------------
       Wiring informations: Sparkfun Pro micro (Atmega32u4)
       --------------------------------------------------------------------------------
         - Red    : Gnd                          |   Pin: Gnd
         - Orange : Vcc (+5V)                    |   Pin: Vcc
         - Yellow : X axis encoder / channel A   |   Pin: PD3 - (INT0)
         - Green  : X axis encoder / channel B   |   Pin: PD2 - (INT1)
         - Blue   : Y axis encoder / channel A   |   Pin: PD0 - (INT2)
         - Violet : Y axis encoder / channel B   |   Pin: PD1 - (INT3)
         - Grey   : Switch 1                     |   Pin: PB3
         - White  : Switch 2                     |   Pin: PB2
         - Black  : Switch 3                     |   Pin: PB1
       --------------------------------------------------------------------------------
       Latest additions:
         - 2016-01-28: Software switch debouncing
       ================================================================================ */
    
    // =================================================================================
    // Type definition
    // =================================================================================
    
    #ifndef DEBOUNCE_THREASHOLD
    #define DEBOUNCE_THREASHOLD 50
    #endif
    
    // =================================================================================
    // Type definition
    // =================================================================================
    typedef struct ENCODER_
    {
      int8_t  coordinate;
      uint8_t index;
    } ENCODER_;
    
    typedef struct BUTTON_
    {
      boolean state;
      boolean needUpdate;
      char    button;
      byte    bitmask;
      long    lastDebounceTime;
    } BUTTON_;
    
    // =================================================================================
    // Constant definition
    // =================================================================================
    const int8_t lookupTable[] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1,  1,  0};
    
    // =================================================================================
    // Volatile variables
    // =================================================================================
    volatile ENCODER_ xAxis = {0, 0};
    volatile ENCODER_ yAxis = {0, 0};
    
    // =================================================================================
    // Global variables
    // =================================================================================
    BUTTON_ leftButton   = {false, false, MOUSE_LEFT,   0b1000, 0};
    BUTTON_ middleButton = {false, false, MOUSE_MIDDLE, 0b0010, 0};
    BUTTON_ rightButton  = {false, false, MOUSE_RIGHT,  0b0100, 0};
    
    // =================================================================================
    // Setup function
    // =================================================================================
    void setup()
    {
      // Attach interruption to encoders channels
      attachInterrupt(0, ISR_HANDLER_X, CHANGE);
      attachInterrupt(1, ISR_HANDLER_X, CHANGE);
      attachInterrupt(2, ISR_HANDLER_Y, CHANGE);
      attachInterrupt(3, ISR_HANDLER_Y, CHANGE);
      
      // Start the mouse function
      Mouse.begin();
    }
    
    // =================================================================================
    // Main program loop
    // =================================================================================
    void loop()
    {
      // Update mouse coordinates
      if (xAxis.coordinate != 0 || yAxis.coordinate != 0)
      {
        Mouse.move(xAxis.coordinate, yAxis.coordinate);
        xAxis.coordinate = 0;
        yAxis.coordinate = 0;
      }
    
      // ---------------------------------
      // Left mouse button state update
      // ---------------------------------
      ReadButton(leftButton);
      UpdateButton(leftButton);
    
      // ---------------------------------
      // Right mouse button state update
      // ---------------------------------  
      ReadButton(rightButton);
      UpdateButton(rightButton);
      
      // ---------------------------------
      // Middle mouse button state update
      // ---------------------------------
      ReadButton(middleButton);
      UpdateButton(middleButton);
    
      // Wait a little before next update
      delay(10);
    }
    
    // =================================================================================
    // Interrupt handlers
    // =================================================================================
    void ISR_HANDLER_X()
    {
      // Build the LUT index from previous and new data
      xAxis.index       = (xAxis.index << 2) | ((PIND & 0b00000011) >> 0);
      xAxis.coordinate += lookupTable[xAxis.index & 0b00001111];
    }
    
    void ISR_HANDLER_Y()
    {
      // Build the LUT index from previous and new data
      yAxis.index       = (yAxis.index << 2) | ((PIND & 0b00001100) >> 2);
      yAxis.coordinate += lookupTable[yAxis.index & 0b00001111];
    }
    
    // =================================================================================
    // Functions
    // =================================================================================
    void ReadButton(BUTTON_& button)
    {
      // Variables
      long    currentime;
      boolean switchState;
      boolean debounced;
      
      // Get current time
      currentime = millis();
      debounced  = (currentime - button.lastDebounceTime > DEBOUNCE_THREASHOLD);
    
      // Get current switch state
      switchState = !(PINB & button.bitmask);
    
      // Button state acquisition
      if ((switchState != button.state) && debounced)
      {
        button.lastDebounceTime = currentime;
        button.state            = switchState;
        button.needUpdate       = true;
      }
    }
    
    void UpdateButton(BUTTON_& button)
    {
      if (button.needUpdate)
      {
        (button.state) ? Mouse.press(button.button) : Mouse.release(button.button);
        button.needUpdate = false;
      }
    }
    
     

Share This Page