CH Products Trackball PRO - BUSMOUSE to USB

iFreilicht

FlexATX Authority
Feb 28, 2015
3,243
2,361
freilite.com
Loving it! Great job on the arduino, and while I personally dislike soldering wire leads to a PCB directly, I can definitely understand why you've done it like that.
A scratch-built trackball would be very interesting.
 

GuilleAcoustic

Chief Procrastination Officer
Original poster
SFFn Staff
LOSIAS
Jun 29, 2015
2,967
4,390
guilleacoustic.wordpress.com
Loving it! Great job on the arduino, and while I personally dislike soldering wire leads to a PCB directly, I can definitely understand why you've done it like that.
Thanks a lot. I do not like soldering wires directly to the PCB either. I did the first firmware using a Teensy++ 2.0, but it didn't fit the case because of the header I soldered onto it.



The button assembly is really sturdy and CH Products used very thick ABS. Their's not much space left unused and I had to rely on the smallest arduino I found and solde the wire to the PCB. This way the device is also kept unharmed, which was a priority for me.



A scratch-built trackball would be very interesting.

The idea grew on me and I really catched the bug. Building / tweaking devices is really fun and rewarding.
 
  • Like
Reactions: esplin2966

dkwiad

What's an ITX?
Jul 15, 2015
1
0
Hello! I´m kind of new here. I´m making a project that consists of a trackball controlling a toy car, without wires. Do you know if I can use bluetooth for that?
Thank you for the post!
 

GuilleAcoustic

Chief Procrastination Officer
Original poster
SFFn Staff
LOSIAS
Jun 29, 2015
2,967
4,390
guilleacoustic.wordpress.com
Hello! I´m kind of new here. I´m making a project that consists of a trackball controlling a toy car, without wires. Do you know if I can use bluetooth for that?
Thank you for the post!

Sorry for the late reply. Bluetooth is possible, I saw several trackballs converted to bluetooth.

Now the real question is: What kind of receiver does your RC car have ?

Most RC vehicles use radio frequency controllers. Bluetooth is radio frequency based, of course, but it uses a formated protocol. I see two possibilities for you project :

  1. (medium to hard) : reverse engineer the signal sent by the car's remote controller for each possible action and build your own transmitter, sending the right signals / data.
  2. (easy to medium) : wire the trackball to an arduino and wire the arduino to the existing car's remote controller. You can then use the arduino's outputs to simulate the physical actuators.

There's a nice tutorial on the excellent slagcoin website (DIY arcade machine) : http://www.slagcoin.com/joystick/pcb_wiring.html
 

GuilleAcoustic

Chief Procrastination Officer
Original poster
SFFn Staff
LOSIAS
Jun 29, 2015
2,967
4,390
guilleacoustic.wordpress.com
Little update:

I bought a new monitor yesterday and jumped from a 1280x1024 resolution to a 2560x1440 one. I was a little afraid that my trackball would be a PITA to use due to its prehistoric sensors, but it is just perfect. I still do not have to perform a full ball revolution to travel the whole screen.
 

GuilleAcoustic

Chief Procrastination Officer
Original poster
SFFn Staff
LOSIAS
Jun 29, 2015
2,967
4,390
guilleacoustic.wordpress.com
Last modification:
  • Added software switch debouncing
  • 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;
  }
}
 

GuilleAcoustic

Chief Procrastination Officer
Original poster
SFFn Staff
LOSIAS
Jun 29, 2015
2,967
4,390
guilleacoustic.wordpress.com
This trackball is ultra rare in its BUS MOUSE variant. But you are right ! I created an humble wordpress called "Not In Stock".

https://guilleacoustic.wordpress.com/

The idea is to log all my retro-hacking and maybe sell them one day. No real stock as it'll depends on what I find and most of them will unique limited edition XD.
 

GuilleAcoustic

Chief Procrastination Officer
Original poster
SFFn Staff
LOSIAS
Jun 29, 2015
2,967
4,390
guilleacoustic.wordpress.com
Very good write-up, thanks for explaining some of the more technical stuff as well, that was really interesting.

Thanks a lot, that is very much appreciated. Feel free to correct my mistakes as it is my only way to improve my english :D.

I'm redesigning the blog to include more cathegories and give it a more professional look. I have not much visitors and no comment yet, but I hope it'll become more popular XD. It needs more contents though.