Jeff Pearson
Member
I made a Joystick/Trackball game controller based on the old Madcatz PantherXL they stopped making around 1998. I used a little Joystick module as a scroll stick with slow and fast vertical scrolling and slow horizontal scrolling. I had a lot of fun this is my first time coding starting with just the Trackball about a year ago
Here is a link to a short video of
https://m.youtube.com/watch?v=eTF1gP4PQRQ&feature=share
And here is the code
// ADNS2051.h reads out ADNS-2051 the Optical Navigation Sensor
// It's used in many cheap optical mice
//
// Written by Martijn The -> post [at] martijnthe.nl
// Tutorial: http://www.martijnthe.nl/optimouse/
// Based on the sketches by Beno”t Rousseau
//
// Panther XL clone (old trackball joystick game controller)
// Added a switch and if statement to switch between Trackball Only and Panther PXLR
// Panther Purple XL Reborn
#include "ADNS2051.h"
#include <OptiMouse.h>
#include <Bounce.h> // Create Bounce objects for each button. The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.
#define SCLK 0 // Serial clock pin on the Arduino
#define SDIO 1 // Serial data (I/O) pin on the Arduino
ADNS2051 Optical1 = ADNS2051(SCLK, SDIO); // Create an instance of the ADNS2051 object
Bounce button2 = Bounce(2, 10); // if a button is too "sensitive" Mouse
Bounce button3 = Bounce(3, 10); // to rapid touch, you can Left, Right,
Bounce button4 = Bounce(4, 10); // increase this time. and Middle Click
Bounce button7 = Bounce(7, 10); // joy trigger
Bounce button8 = Bounce(8, 10); // joy trigger2
Bounce button9 = Bounce(9, 10); // joy thumb
Bounce button10 = Bounce(10, 10); // joy thumb2
Bounce button11 = Bounce(11, 10); // hat up
Bounce button12 = Bounce(12, 10); // hat right
Bounce button13 = Bounce(13, 10); // hat down
Bounce button14 = Bounce(14, 10); // hat left
Bounce button15 = Bounce(15, 10); // Top Right Base Button
Bounce button16 = Bounce(16, 10); // Middle Right Base Button
Bounce button17 = Bounce(17, 10); // Bottom Right Base Button
Bounce button20 = Bounce(20, 10); // Tracball Thumb Bottom
Bounce button21 = Bounce(21, 10); // Tracball Thumb Top
Bounce button22 = Bounce(22, 10); // Left Base Right
Bounce button23 = Bounce(23, 10); // Left Base Bottom
Bounce button24 = Bounce(24, 10); // Left Base Corner
int scrollDelay = 15; // had to slow down scrolling and set
// initial value to send data on first loop (first episode of The Count)
int sH = 512; // Horizontal Scroll Stick Value
int sV = 512; // Verticall Scroll Stick Value
// Set initial values at center stick
// added these variables so Scroll Stick
// values only need to be read from pin
// once per loop
void setup()
{
Serial.begin(38400);
Optical1.begin(); // Resync (not really necessary?)
Optical1.writeConfig(B000101); // added command. set 800 CPI and always awake
// (B000100), set 800 CPI and auto sleep
// Default is 400 CPI and auto sleep
pinMode(2, INPUT_PULLUP); // Left Mouse Click
pinMode(3, INPUT_PULLUP); // Middle Mouse Click
pinMode(4, INPUT_PULLUP); // Right Mouse Click
pinMode(7, INPUT_PULLUP); // Joy button 1
pinMode(8, INPUT_PULLUP); // Joy button 4
pinMode(9, INPUT_PULLUP); // Joy button 2
pinMode(10, INPUT_PULLUP); // Joy button 3
pinMode(11, INPUT_PULLUP); // Joy Hat
pinMode(12, INPUT_PULLUP);
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
pinMode(15, INPUT_PULLUP); // Base and Trackball Thumb Buttons
pinMode(16, INPUT_PULLUP);
pinMode(17, INPUT_PULLUP);
pinMode(20, INPUT_PULLUP);
pinMode(21, INPUT_PULLUP);
pinMode(22, INPUT_PULLUP);
pinMode(23, INPUT_PULLUP);
pinMode(24, INPUT_PULLUP);
pinMode(25, INPUT_PULLUP); // Joystick.send_now ON/OFF
Joystick.useManualSend(true);
}
void loop()
{
button2.update(); // Read all buttons except Tracball Only mode switch
button3.update();
button4.update();
button7.update();
button8.update();
button9.update();
button10.update();
button11.update();
button12.update();
button13.update();
button14.update();
button15.update();
button16.update();
button17.update();
button20.update();
button21.update();
button22.update();
button23.update();
button24.update();
// Trackball Loop
if (button2.fallingEdge()) {
Mouse.press(MOUSE_LEFT);
}
if (button2.risingEdge()) {
Mouse.release(MOUSE_LEFT);
}
if (button3.fallingEdge()) {
Mouse.press(MOUSE_MIDDLE);
}
if (button3.risingEdge()) {
Mouse.release(MOUSE_MIDDLE);
}
if (button4.fallingEdge()) {
Mouse.press(MOUSE_RIGHT);
}
if (button4.risingEdge()) {
Mouse.release(MOUSE_RIGHT);
}
// The status commands are available only for the PAN3101 and the ADNS2051:
Optical1.updateStatus(); // Get the latest motion status
// if (Optical1.motion()) // If the 'Motion' status bit is set,
// {
Mouse.move((-1.5)*Optical1.dx(), (-1.5)*Optical1.dy());
// }
// Added vertical and horizontal mouse scrolling
// with joystick module input
sH = (analogRead(4)); // Scroll Stick Horizontal reading
sV = (analogRead(5)); // Scroll Stick Vertical reading
int hScroll = 0; // Y axis remapped for mouse scroll commands
// Scrollstick Y axis is on pin A4 (Horizontal)
int vScroll = 0; // X axis remapped for mouse scroll commands
// Scrollstick X axis is on pin A5 (Vertical)
// initial value and reset to no scroll once per loop
// before checking Scroll Stick value arguements
scrollDelay ++; // adding a simple delay in the loop to slow
// down scrolling is not an option in the
// case of joystick or mouse operation so
// So I remeber Sesame Street from my childhood
// and run an episode of The Count hahahahahahaha
// add 1 to delay counter each loop
if (sH < 400) {
hScroll = 1;
}
if (sH > 700) {
hScroll = -1;
}
if (sV < 502) {
vScroll = 1;
}
if (sV > 522) {
vScroll = -1;
}
if (sV > 990) { // 2 speed scrolling on vertical
scrollDelay = (scrollDelay + 7); // adjust scroll fast here
}
if (sV < 22) {
scrollDelay = (scrollDelay + 7); // and here
}
if (scrollDelay > 15) { // adjust scroll speed here. larger number = slower
scrollDelay = 0;
Mouse.scroll(vScroll, hScroll);
}
// Joystick Loop
if (button7.fallingEdge()) {
Joystick.button(1, 1); // Trigger
}
if (button8.fallingEdge()) {
Joystick.button(4, 1); // TopBtn
}
if (button9.fallingEdge()) {
Joystick.button(2, 1); // ThumbBtn
}
if (button10.fallingEdge()) {
Joystick.button(3, 1); // ThumbBtn2
}
if (button15.fallingEdge()) {
Joystick.button(10, 1); // BaseBtn4
}
if (button16.fallingEdge()) {
Joystick.button(11, 1); // BaseBtn5
}
if (button17.fallingEdge()) {
Joystick.button(12, 1); // BaseBtn6
}
if (button20.fallingEdge()) {
Joystick.button(6, 1); // PinkieBtn (Trackball Thumb Bottom)
}
if (button21.fallingEdge()) {
Joystick.button(5, 1); // TopBtn2 (Trackball Thumb Top)
}
if (button22.fallingEdge()) {
Joystick.button(9, 1); // BaseBtn3
}
if (button23.fallingEdge()) {
Joystick.button(7, 1); // BaseBtn1
}
if (button24.fallingEdge()) {
Joystick.button(8, 1); // BaseBtn2
}
if (button7.risingEdge()) {
Joystick.button(1, 0);
}
if (button8.risingEdge()) {
Joystick.button(4, 0);
}
if (button9.risingEdge()) {
Joystick.button(2, 0);
}
if (button10.risingEdge()) {
Joystick.button(3, 0);
}
if (button15.risingEdge()) {
Joystick.button(10, 0);
}
if (button16.risingEdge()) {
Joystick.button(11, 0);
}
if (button17.risingEdge()) {
Joystick.button(12, 0);
}
if (button20.risingEdge()) {
Joystick.button(6, 0);
}
if (button21.risingEdge()) {
Joystick.button(5, 0);
}
if (button22.risingEdge()) {
Joystick.button(9, 0);
}
if (button23.risingEdge()) {
Joystick.button(7, 0);
}
if (button24.risingEdge()) {
Joystick.button(8, 0);
}
if (button11.fallingEdge()) {
Joystick.hat(0);
}
if (button11.risingEdge()) {
Joystick.hat(-1);
}
if (button12.fallingEdge()) {
Joystick.hat(90);
}
if (button12.risingEdge()) {
Joystick.hat(-1);
}
if (button13.fallingEdge()) {
Joystick.hat(180);
}
if (button13.risingEdge()) {
Joystick.hat(-1);
}
if (button14.fallingEdge()) {
Joystick.hat(270);
}
if (button14.risingEdge()) {
Joystick.hat(-1);
}
if ((digitalRead(25)) == LOW) { // Added Trackball only mode.
Joystick.X(analogRead(0)); // Joystick output having nowhere to go can cause interruptions
Joystick.Y(analogRead(1)); // in the loop/janky trackball operation
Joystick.Z(analogRead(2)); // pin 25 high is trackball only mode....if pin low
Joystick.sliderLeft(analogRead(3)); // read Joystick axis and send Joystick data
Joystick.send_now();
}
delay(5); // So this loop ONLY runs 200 times per second?
}
Here is a link to a short video of
https://m.youtube.com/watch?v=eTF1gP4PQRQ&feature=share
And here is the code
// ADNS2051.h reads out ADNS-2051 the Optical Navigation Sensor
// It's used in many cheap optical mice
//
// Written by Martijn The -> post [at] martijnthe.nl
// Tutorial: http://www.martijnthe.nl/optimouse/
// Based on the sketches by Beno”t Rousseau
//
// Panther XL clone (old trackball joystick game controller)
// Added a switch and if statement to switch between Trackball Only and Panther PXLR
// Panther Purple XL Reborn
#include "ADNS2051.h"
#include <OptiMouse.h>
#include <Bounce.h> // Create Bounce objects for each button. The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.
#define SCLK 0 // Serial clock pin on the Arduino
#define SDIO 1 // Serial data (I/O) pin on the Arduino
ADNS2051 Optical1 = ADNS2051(SCLK, SDIO); // Create an instance of the ADNS2051 object
Bounce button2 = Bounce(2, 10); // if a button is too "sensitive" Mouse
Bounce button3 = Bounce(3, 10); // to rapid touch, you can Left, Right,
Bounce button4 = Bounce(4, 10); // increase this time. and Middle Click
Bounce button7 = Bounce(7, 10); // joy trigger
Bounce button8 = Bounce(8, 10); // joy trigger2
Bounce button9 = Bounce(9, 10); // joy thumb
Bounce button10 = Bounce(10, 10); // joy thumb2
Bounce button11 = Bounce(11, 10); // hat up
Bounce button12 = Bounce(12, 10); // hat right
Bounce button13 = Bounce(13, 10); // hat down
Bounce button14 = Bounce(14, 10); // hat left
Bounce button15 = Bounce(15, 10); // Top Right Base Button
Bounce button16 = Bounce(16, 10); // Middle Right Base Button
Bounce button17 = Bounce(17, 10); // Bottom Right Base Button
Bounce button20 = Bounce(20, 10); // Tracball Thumb Bottom
Bounce button21 = Bounce(21, 10); // Tracball Thumb Top
Bounce button22 = Bounce(22, 10); // Left Base Right
Bounce button23 = Bounce(23, 10); // Left Base Bottom
Bounce button24 = Bounce(24, 10); // Left Base Corner
int scrollDelay = 15; // had to slow down scrolling and set
// initial value to send data on first loop (first episode of The Count)
int sH = 512; // Horizontal Scroll Stick Value
int sV = 512; // Verticall Scroll Stick Value
// Set initial values at center stick
// added these variables so Scroll Stick
// values only need to be read from pin
// once per loop
void setup()
{
Serial.begin(38400);
Optical1.begin(); // Resync (not really necessary?)
Optical1.writeConfig(B000101); // added command. set 800 CPI and always awake
// (B000100), set 800 CPI and auto sleep
// Default is 400 CPI and auto sleep
pinMode(2, INPUT_PULLUP); // Left Mouse Click
pinMode(3, INPUT_PULLUP); // Middle Mouse Click
pinMode(4, INPUT_PULLUP); // Right Mouse Click
pinMode(7, INPUT_PULLUP); // Joy button 1
pinMode(8, INPUT_PULLUP); // Joy button 4
pinMode(9, INPUT_PULLUP); // Joy button 2
pinMode(10, INPUT_PULLUP); // Joy button 3
pinMode(11, INPUT_PULLUP); // Joy Hat
pinMode(12, INPUT_PULLUP);
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
pinMode(15, INPUT_PULLUP); // Base and Trackball Thumb Buttons
pinMode(16, INPUT_PULLUP);
pinMode(17, INPUT_PULLUP);
pinMode(20, INPUT_PULLUP);
pinMode(21, INPUT_PULLUP);
pinMode(22, INPUT_PULLUP);
pinMode(23, INPUT_PULLUP);
pinMode(24, INPUT_PULLUP);
pinMode(25, INPUT_PULLUP); // Joystick.send_now ON/OFF
Joystick.useManualSend(true);
}
void loop()
{
button2.update(); // Read all buttons except Tracball Only mode switch
button3.update();
button4.update();
button7.update();
button8.update();
button9.update();
button10.update();
button11.update();
button12.update();
button13.update();
button14.update();
button15.update();
button16.update();
button17.update();
button20.update();
button21.update();
button22.update();
button23.update();
button24.update();
// Trackball Loop
if (button2.fallingEdge()) {
Mouse.press(MOUSE_LEFT);
}
if (button2.risingEdge()) {
Mouse.release(MOUSE_LEFT);
}
if (button3.fallingEdge()) {
Mouse.press(MOUSE_MIDDLE);
}
if (button3.risingEdge()) {
Mouse.release(MOUSE_MIDDLE);
}
if (button4.fallingEdge()) {
Mouse.press(MOUSE_RIGHT);
}
if (button4.risingEdge()) {
Mouse.release(MOUSE_RIGHT);
}
// The status commands are available only for the PAN3101 and the ADNS2051:
Optical1.updateStatus(); // Get the latest motion status
// if (Optical1.motion()) // If the 'Motion' status bit is set,
// {
Mouse.move((-1.5)*Optical1.dx(), (-1.5)*Optical1.dy());
// }
// Added vertical and horizontal mouse scrolling
// with joystick module input
sH = (analogRead(4)); // Scroll Stick Horizontal reading
sV = (analogRead(5)); // Scroll Stick Vertical reading
int hScroll = 0; // Y axis remapped for mouse scroll commands
// Scrollstick Y axis is on pin A4 (Horizontal)
int vScroll = 0; // X axis remapped for mouse scroll commands
// Scrollstick X axis is on pin A5 (Vertical)
// initial value and reset to no scroll once per loop
// before checking Scroll Stick value arguements
scrollDelay ++; // adding a simple delay in the loop to slow
// down scrolling is not an option in the
// case of joystick or mouse operation so
// So I remeber Sesame Street from my childhood
// and run an episode of The Count hahahahahahaha
// add 1 to delay counter each loop
if (sH < 400) {
hScroll = 1;
}
if (sH > 700) {
hScroll = -1;
}
if (sV < 502) {
vScroll = 1;
}
if (sV > 522) {
vScroll = -1;
}
if (sV > 990) { // 2 speed scrolling on vertical
scrollDelay = (scrollDelay + 7); // adjust scroll fast here
}
if (sV < 22) {
scrollDelay = (scrollDelay + 7); // and here
}
if (scrollDelay > 15) { // adjust scroll speed here. larger number = slower
scrollDelay = 0;
Mouse.scroll(vScroll, hScroll);
}
// Joystick Loop
if (button7.fallingEdge()) {
Joystick.button(1, 1); // Trigger
}
if (button8.fallingEdge()) {
Joystick.button(4, 1); // TopBtn
}
if (button9.fallingEdge()) {
Joystick.button(2, 1); // ThumbBtn
}
if (button10.fallingEdge()) {
Joystick.button(3, 1); // ThumbBtn2
}
if (button15.fallingEdge()) {
Joystick.button(10, 1); // BaseBtn4
}
if (button16.fallingEdge()) {
Joystick.button(11, 1); // BaseBtn5
}
if (button17.fallingEdge()) {
Joystick.button(12, 1); // BaseBtn6
}
if (button20.fallingEdge()) {
Joystick.button(6, 1); // PinkieBtn (Trackball Thumb Bottom)
}
if (button21.fallingEdge()) {
Joystick.button(5, 1); // TopBtn2 (Trackball Thumb Top)
}
if (button22.fallingEdge()) {
Joystick.button(9, 1); // BaseBtn3
}
if (button23.fallingEdge()) {
Joystick.button(7, 1); // BaseBtn1
}
if (button24.fallingEdge()) {
Joystick.button(8, 1); // BaseBtn2
}
if (button7.risingEdge()) {
Joystick.button(1, 0);
}
if (button8.risingEdge()) {
Joystick.button(4, 0);
}
if (button9.risingEdge()) {
Joystick.button(2, 0);
}
if (button10.risingEdge()) {
Joystick.button(3, 0);
}
if (button15.risingEdge()) {
Joystick.button(10, 0);
}
if (button16.risingEdge()) {
Joystick.button(11, 0);
}
if (button17.risingEdge()) {
Joystick.button(12, 0);
}
if (button20.risingEdge()) {
Joystick.button(6, 0);
}
if (button21.risingEdge()) {
Joystick.button(5, 0);
}
if (button22.risingEdge()) {
Joystick.button(9, 0);
}
if (button23.risingEdge()) {
Joystick.button(7, 0);
}
if (button24.risingEdge()) {
Joystick.button(8, 0);
}
if (button11.fallingEdge()) {
Joystick.hat(0);
}
if (button11.risingEdge()) {
Joystick.hat(-1);
}
if (button12.fallingEdge()) {
Joystick.hat(90);
}
if (button12.risingEdge()) {
Joystick.hat(-1);
}
if (button13.fallingEdge()) {
Joystick.hat(180);
}
if (button13.risingEdge()) {
Joystick.hat(-1);
}
if (button14.fallingEdge()) {
Joystick.hat(270);
}
if (button14.risingEdge()) {
Joystick.hat(-1);
}
if ((digitalRead(25)) == LOW) { // Added Trackball only mode.
Joystick.X(analogRead(0)); // Joystick output having nowhere to go can cause interruptions
Joystick.Y(analogRead(1)); // in the loop/janky trackball operation
Joystick.Z(analogRead(2)); // pin 25 high is trackball only mode....if pin low
Joystick.sliderLeft(analogRead(3)); // read Joystick axis and send Joystick data
Joystick.send_now();
}
delay(5); // So this loop ONLY runs 200 times per second?
}