Gleich vorweg: Es gibt reichlich wenig Verwendung für eine Maus im Retropie Umfeld (zumindest auf einem ZeroBoy)…aber es gibt eben auch Day oft the Tentacle.
Nach diversen Tests mit den Sensoren einer optischen Maus bzw. einer alten analogen USB Maus bin ich am Ende über den Blackberry Trackball gestolpert. Diese Anleitung beschreibt wie das ganze Realisiert wurde.
http://goo.gl/sjuYAh (AliExpress Link)
http://www.sparkfun.com/products/13169
Als Board kommt ein TeensyLC zum Einsatz. Die bereits verwendeten 11 Pins für die Tastensteuerung sowie die benötigten 9 Pins für den Trackball plus einen Pin für die Möglichkeit einen „Rechtsklick“ zu realisieren, haben noch 4 Pins übriggelassen, die ich zur dynamischen Beleuchtung der ABXY Tasten verwendet habe. Den teensy sollte man nicht hart verlöten, sondern per Stecker anschließen, damit man einfach Änderungen am Quellcode machen kann.
Hier sieht man das 5mm Loch für den Trackball und die Verdünnung des Cases um gut 1 mm, damit der Trackball etwas heraussteht.
Man kann aber auch einfach ein 7mm großes Loch verwenden.
Der fertig bestückte TeensyLC mit dem Trackball. Am Trackball muss man eine Ecke wegdremeln, damit er sauber in die untere linke Ecke passt.
Die finale Verkabelung ist in diesem Beispiel etwas chaotisch ausgefallen. USB Hub, USB Sound, TeensyLC, Trackball, Verstärker, zwei Platinen für die Akkuanzeige (Das Steuerkreuz dient als Akkuanzeige) erzeugen leider etwas Kabelsalat.
Die ABXY Knöpfe werden beim Drücken für ca. 0.5 Sekunden beleuchtet.
Pin | Funktion |
0 | Links |
1 | Hoch |
2 | Runter |
3 | Rechts |
4 | Escape à SELECT |
5 | Enter à START |
6 | X |
7 | Y |
8 | “F5” (Menü in Day of the Tentacle) à B |
9 | A |
10 | L |
11 | R |
12 | LED A Knopf |
13 | Blackberry Click |
14 | Rechte Maustaste |
15 | Blackberry UP |
16 | Blackberry DOWN |
17 | Blackberry VCC |
18 | Blackberry RIGHT |
19 | Blackberry UP |
20 | Weiße Blackberry LED |
21 | Grüne Blackberry LED |
22 | Blaue Blackberry LED |
23 | Rote Blackberry LED |
24 | LED für B Knopf |
25 | LED für X Knopf |
26 | LED für Y Knopf |
Und hier der Code für den Teensy/ATMega32U4 im “Tastatur, Maus, Joystick” Modus
#include <Bounce.h>
#define NUM_KEYS 12
const int moveDistance = 15; // how much should the mouse move on each button press
const int redPin = 23; // pin for red Blackberry LED
const int bluePin = 22; // pin for blue Blackberry LED
const int greenPin = 21; // pin for blue Blackberry LED
const int whitePin = 20; // pin for white Blackberry LED
const int ledApin = 12; // pin for A Button LED
const int ledBpin = 24; // pin for B Button LED
const int ledXpin = 25; // pin for X Button LED
const int ledYpin = 26; // pin for Y Button LED
const int mouse_screen_X = 320; // Screen resolution horizontal
const int mouse_screen_Y = 240; // Screen resolution vertical
const int blackberry_light_duration = 25000; // how long should the Blackberry Ball glow?
const int blackberry_click_max_time = 60000; // how long do you have time to input the clicks for lights ON/OFF
const int led_button_light_duration = 5000; // how long should the ABXY Button LEDs glow?
int blackberry_light_mode = 1; // lights ON or OFF ?
int blackberry_click_count = 0; // counter for clicks within blackberry_click_max_time
int blackberry_click_time = 0; // "time" counter for x clicks.
int blackberry_clicks_needed = 5; // 5 clicks for turning lights ON/OFF
int ired = 1; // time counter for RED BALL LED
int iblue = 1; // time counter for BLUE BALL LED
int igreen = 1; // time counter for GREEN BALL LED
int iwhite = 1; // time counter for WHITE BALL LED
int ledAcounter = 0;
int ledBcounter = 0;
int ledXcounter = 0;
int ledYcounter = 0;
struct Key {
int keycode;
Bounce* bounce;
};
// 2 mousebuttons
Bounce mbuttonl = Bounce(13, 50); // pin 13 left click
Bounce mbuttonr = Bounce(14, 50); // pin 14 right click
// 4 directions for mouse
Bounce mup = Bounce(19, 20); // UP
Bounce mdown = Bounce(16, 20); // DOWN
Bounce mleft = Bounce(15, 20); // LEFT
Bounce mright = Bounce(18, 20); // RIGHT
Key keys[NUM_KEYS];
Key key(int keycode, int pin) {
Key *ret = new Key;
ret->keycode = keycode;
ret->bounce = new Bounce(pin, 50);
pinMode(pin, INPUT_PULLUP);
return *ret;
}
void setupKeys() {
keys[0] = key(KEY_LEFT_ARROW, 0); // left
keys[1] = key(KEY_UP_ARROW, 1); // up
keys[2] = key(KEY_DOWN_ARROW, 2); // down
keys[3] = key(KEY_RIGHT_ARROW, 3); // right
keys[4] = key(KEY_ESC, 4); // select
keys[5] = key(KEY_RETURN, 5); //start
keys[6] = key('x', 6); // using ESC for retropie menu
keys[7] = key('y', 7); // using for retropie menu
keys[8] = key(KEY_F5, 8); // B - workaround for Day of Tentacle F5 for Menu
keys[9] = key('a', 9); // A
keys[10] = key('l',10); // left button
keys[11] = key('r',11); // right button
}
void setup() {
setupKeys();
// mouse setup
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
pinMode(15, INPUT_PULLUP);
pinMode(16, INPUT_PULLUP);
pinMode(18, INPUT_PULLUP);
pinMode(19, INPUT_PULLUP);
pinMode(redPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(whitePin, OUTPUT);
pinMode(ledApin, OUTPUT);
pinMode(ledBpin, OUTPUT);
pinMode(ledXpin, OUTPUT);
pinMode(ledYpin, OUTPUT);
//Mouse.screenSize(1920, 1080);
Mouse.screenSize(mouse_screen_X, mouse_screen_Y);
Keyboard.begin();
}
void loop() {
for (int i = 0; i < NUM_KEYS; i++) {
keys[i].bounce->update();
if (keys[i].bounce->fallingEdge()) {
Keyboard.press(keys[i].keycode);
// hard-coded ABXY Buttons for LEDs
if ( i == 6 && blackberry_light_mode > 0 ) { digitalWrite(ledXpin, HIGH); } // X button pressed --> turn on button LED
if ( i == 7 && blackberry_light_mode > 0 ) { digitalWrite(ledYpin, HIGH); } // Y button pressed --> turn on button LED
if ( i == 8 && blackberry_light_mode > 0 ) { digitalWrite(ledBpin, HIGH); } // B button pressed --> turn on button LED
if ( i == 9 && blackberry_light_mode > 0 ) { digitalWrite(ledApin, HIGH); } // A button pressed --> turn on button LED
} else if (keys[i].bounce->risingEdge()) {
Keyboard.release(keys[i].keycode);
}
}
// ++++++++++++++mouse++++++++++++++
// #################################
// # turn off red Blackberry LEDs #
// #################################
if (ired >=2 ){ired = ired + 1;} // if led is on "count time"
if (ired >= blackberry_light_duration){ // if max_time is reached -> counter reset, turn LED OFF
ired = 1; // reset counter
digitalWrite(redPin, LOW); // switch off
}
// turn off blue Blackberry LED
if (iblue >=2 ){iblue = iblue + 1;}
if (iblue >= blackberry_light_duration){
iblue = 1; // switch off
digitalWrite(bluePin, LOW);
}
if (igreen >=2 ){igreen = igreen + 1;} // if led is on "count time"
if (igreen >= blackberry_light_duration){
igreen = 1; // switch off
digitalWrite(greenPin, LOW);
}
if (iwhite >=2 ){iwhite = iwhite + 1;} // if led is on "count time"
if (iwhite >= blackberry_light_duration){
iwhite = 1; // switch off
digitalWrite(whitePin, LOW);
}
// #################################
// # turn off ABXY Button LEDs #
// #################################
if ( ledAcounter > led_button_light_duration || blackberry_light_mode < 0 ){
digitalWrite(ledApin, LOW); // turn off A pin
}
if ( ledBcounter > led_button_light_duration || blackberry_light_mode < 0 ){
digitalWrite(ledBpin, LOW); // turn off B pin
}
if ( ledXcounter > led_button_light_duration || blackberry_light_mode < 0 ){
digitalWrite(ledXpin, LOW); // turn off X pin
}
if ( ledYcounter > led_button_light_duration || blackberry_light_mode < 0 ){
digitalWrite(ledYpin, LOW); // turn off Y pin
}
// ####### READ MOUSE BUTTONS ###########
mup.update();
mdown.update();
mleft.update();
mright.update();
mbuttonl.update();
mbuttonr.update();
if (mleft.fallingEdge()) {
Mouse.move(-moveDistance, 0); // move Left
if (blackberry_light_mode > 0) { digitalWrite(redPin, HIGH); }
ired = 2;
}
if (mright.fallingEdge()) {
Mouse.move(moveDistance, 0); // move Right
if (blackberry_light_mode > 0) { digitalWrite(greenPin, HIGH); }
igreen = 2;
}
if (mup.fallingEdge()) {
Mouse.move(0, -moveDistance); // move Up
if (blackberry_light_mode > 0) { digitalWrite(bluePin, HIGH); }
iblue = 2;
}
if (mdown.fallingEdge()) {
Mouse.move(0, moveDistance); // move Down
if (blackberry_light_mode > 0) { digitalWrite(whitePin, HIGH); }
iwhite = 2;
}
// For the mouse buttons, we must detect both the falling and rising edges,
// to press the mouse button when the button on our digital pin is pressed,
// and to later release it when the physical button releases (the pin rises
// from low back to high, thanks to INPUT_PULLUP).
if (mbuttonl.fallingEdge()) {
Mouse.press(MOUSE_LEFT);
delay(100); // reduce bouncing as primitive solution.
blackberry_click_count = blackberry_click_count + 1;
if (blackberry_click_count == 1) {
blackberry_click_time = 1; // start timer on first click.
}
if (blackberry_click_count >= 5) { // now turn off / on the lights
blackberry_light_mode = blackberry_light_mode * -1;
}
}
if (mbuttonl.risingEdge()) {
Mouse.release(MOUSE_LEFT);
}
if (mbuttonr.fallingEdge()) {
Mouse.press(MOUSE_RIGHT);
}
if (mbuttonr.risingEdge()) {
Mouse.release(MOUSE_RIGHT);
}
// check the click count time for x clicks to activate LIGHT
if ( blackberry_click_time >= 1){ // timer is started on first click
blackberry_click_time = blackberry_click_time + 1; // add 1 cycle
if (blackberry_click_time > blackberry_click_max_time) { // if max duration is reached --> reset timer and clicks
blackberry_click_time = 0; // reset timer for x clicks.
blackberry_click_count = 0; // reset click counter
}
}
// LED fuer Buttons schalten
if (blackberry_light_mode > 0) {
digitalWrite(ledApin, HIGH); // hier A
digitalWrite(ledBpin, HIGH); // hier B
// hier X
digitalWrite(ledYpin, HIGH); // hier Y
}
}
Zur Zeit ist der Teensy mit “f5” noch speziell für Day of Tentacle eingerichtet, hier muss ich noch schauen, wie man in SCRUMMVM eine halbwegs einheitliche Steuerung hinterlegen kann. Damit man den teensy jederzeit umprogrammieren kann, sollte man USB nicht fest verlöten, sondern mit einem Stecker versehen!
Die Maus wird sehr gut erkannt und funktioniert auch unter PIXEL. Auf einem 640er bzw HDMI screen kann man sogar bequem surfen und die Desktop Oberfläche nutzen!
Vielen Dank an Achim für die tolle Anleitung!