Project tutorial
Connect USB Joystick to Commodore C64

Connect USB Joystick to Commodore C64 © GPL3+

HW equipment for classic home computers such as C64 gets old. Joysticks wear out and are hardly repairable. Can new equipment be used?

  • 1,131 views
  • 0 comments
  • 4 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
USB Host Shield
Not the original Arduino USB Host Shield, but the one which is linked in Github at the uSB Host Shield library. Available in various online shops. Compare the pictures.
×1
20x0054 40
USB 2.0 Hub, 4 Port
USB Hub wIthout external power supply
×1
Fairchild semiconductor pn2222abu. image
General Purpose Transistor NPN
BC547
×12
Mfr 25frf52 47k5 sml
Resistor 47.5k ohm
×6
4415447
Through Hole Resistor, 33 kohm
×4
Mfr 25frf52 10k sml
Resistor 10k ohm
×2
4415447 jg5sklyzsj
Resistor 220 ohm
×2
09590 01
LED (generic)
×2
5157655
D Sub Connector, 9 Contacts
×2
29c8785 40
Pushbutton Switch, Momentary
×1

About this project

Introduction

I own a C64 with Quickshot II Turbo joysticks. Unfortunately the joysticks are worn out. On one, one spring of the micro switch is broken and the micro switches are discrete, which means there is a spring between two poles. Without the exactly matching spring it's not possible to repair. Also the rubber ring which shall bring the joystick back to middle position has dried out over the years. So, buy another pair of used joysticks which are decades old, or find another solution?

The idea: Use two Competition Pro joysticks which are available still today as vintage rebuild but with USB. Important to know: There are two variants available. One which as a slower USB polling rate and one with a high polling rate (125 Hz). In some forums you can read about complaints that the slow polling rate is too slow. So, make sure to get the one with the high polling rate.

Option 1: Connect the micro switches in the joystick and mount a C64 compatible connector. See http://www.stefan-uhlmann.de/cbm/CPUSB/CPUSB.html

Option 2: Convert the USB port into C64 control port digital signals by using an Arduino --> This is described here.

How does it work

The HW is an Arduino Uno and an USB Host Shield ( http://shop.tkjelectronics.dk/product_info.php?products_id=43 ). Furthermore, a breadboard PCB with the switching transistors and the connections of the C64 control port.

The SW uses the USB Host Shield library. In detail, the PS3USB implementation of the library. The Competition Pro is detected as a PS3 compatible device. However, the fire buttons are interpreted as cross, triangle, circle, square.

A USB hub is used to connect more than one joystick to the USB Host Shield. Depending on the order of connection the joysticks the first is detected as joystick1, the second as joystick2.

To swap the joysticks for the two C64 control ports an pushbutton is used. n LED signals the position of joystick1. As pushbutton input input the GPIO input of the USB Host Shield In0 is used. To control the LEDs the GPIO output of the USB Host Shield Out2, 3 is used.

The C64 control port expects a switch-to-ground for any activation. Details can be found here: https://en.wikipedia.org/wiki/Atari_joystick_port#Other_platforms. For switching the control port pins to ground simple transistor switches are used. To control the transistors the Arduino digital outputs D2-D7 are used and the GPIO outputs of the USB Host Shield Out0, 1, 4, 5 are used.

As power supply I'm using an external power supply (7, 5V) which is connected to Vin of the Arduino. I didn't want to use the 5V output of the C64 control port to not put any additional load to the C64 power supply.

Code

C64 USB joystickC/C++
Initializes the USB Host Shield library for PS3 USB joysticks. Reads in and transfers the USB signals from the up to two joysticks to the C64 control port signals.
/*
 Arduino sketch to connect 2 USB joysticks to Commodore C64
 */

/*
 Basis for this sketch: 
 Example sketch for the PS3 USB library - developed by Kristian Lauszus
 For more information visit my blog: http://blog.tkjelectronics.dk/ or
 send me an e-mail:  kristianl@tkjelectronics.com
 */

 /*
  Mapping Competition Pro to --> PS3:
  Front Left Button --> Triangle
  Front Right Button --> Circle
  Rear Left Button (triangle shaped) --> Cross
  Rear Right Button (triangle shaped) --> Square
  Forward --> LeftHatY = 0
  Backward --> LeftHatY = 255
  Left --> LeftHatX = 0
  Right --> LeftHatY = 255
  * 
  */

#include <PS3USB.h>
#include <usbhub.h>
#include <UHS2_gpio.h>

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>

#define PRINTREPORT

#define LEFT1   4 //via GPIO_Out4 on USB Host Shield
#define RIGHT1  5 //via GPIO_Out5 on USB Host Shield
#define FORWARD1     2
#define BACKWARD1   3
#define BUTTON1 0   //via GPIO_Out0 on USB Host Shield
#define LEFT2   4
#define RIGHT2  5
#define FORWARD2     6
#define BACKWARD2   7
#define BUTTON2 1   //via GPIO_Out1 on USB Host Shield
#define LED_PORT1   2
#define LED_PORT2   3
#define SWAPBUTTON  0 // via GPIO_In0 on USB Host Shield
#define STATE_BUTTON  0
#define STATE_LEFT  1
#define STATE_RIGHT 2
#define STATE_FORWARD 3
#define STATE_BACKWARD  4


#define JOYSTICK_COUNT  2
#define JOYSTICK_STATES 5

#define DEBOUNCETIME  50


USB Usb;
USBHub     Hub(&Usb);
PS3USB PS3(&Usb); // This will just create the instance
PS3USB PS3_1(&Usb); // This will just create the instance
UHS2_GPIO Gpio(&Usb); // Create a GPIO object

bool joystickState[JOYSTICK_COUNT][JOYSTICK_STATES];
bool joystickSwap = LOW;

void setup() {
  Serial.begin(115200);
  #if !defined(__MIPSEL__)
    while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
  #endif
  
  if (Usb.Init() == -1) {
    Serial.print(F("\r\nOSC did not start"));
    while (1); //halt
  }
  Serial.print(F("\r\nPS3 USB Library Started"));
  
  //initialize joystick states for all joysticks
  for (unsigned char i=0; i<JOYSTICK_COUNT; i++){
    for (unsigned char j=0; j<JOYSTICK_STATES; j++){
      joystickState[i][j] = 0;
    }
  }

  //initialize Output pins
  Gpio.digitalWrite(LEFT1, LOW);
  Gpio.digitalWrite(RIGHT1, LOW);
  digitalWrite(FORWARD1, LOW);
  digitalWrite(BACKWARD1, LOW);
  digitalWrite(LEFT2, LOW);
  digitalWrite(RIGHT2, LOW);
  digitalWrite(FORWARD2, LOW);
  digitalWrite(BACKWARD2, LOW);
  pinMode(LEFT1, OUTPUT);
  pinMode(RIGHT1, OUTPUT);
  pinMode(FORWARD1, OUTPUT);
  pinMode(BACKWARD1, OUTPUT);
  pinMode(LEFT2, OUTPUT);
  pinMode(RIGHT2, OUTPUT);
  pinMode(FORWARD2, OUTPUT);
  pinMode(BACKWARD2, OUTPUT);
  Gpio.digitalWrite(BUTTON1, LOW);
  Gpio.digitalWrite(BUTTON2, LOW);
}

void loop() {
  unsigned char joystickMapping[JOYSTICK_COUNT];
  static bool joystickButtonState[JOYSTICK_COUNT];
  Usb.Task();
  PS3.attachOnInit(USBonInit1);
  PS3_1.attachOnInit(USBonInit2);

 // Shall Joysticks be swapped?
 joystickSwap = ToggleButton(Gpio.digitalRead(SWAPBUTTON));
 
 if(joystickSwap){
  joystickMapping[0] = 1;
  joystickMapping[1] = 0;
  Gpio.digitalWrite(LED_PORT1, LOW);
  Gpio.digitalWrite(LED_PORT2, HIGH);
 }
 else{
  joystickMapping[0] = 0;
  joystickMapping[1] = 1;
  Gpio.digitalWrite(LED_PORT1, HIGH);
  Gpio.digitalWrite(LED_PORT2, LOW);
 }


 // Joystick 1
  if (PS3.PS3Connected || PS3.PS3NavigationConnected) {
    if (PS3.getAnalogHat(LeftHatX) == 0){
      joystickState[joystickMapping[0]][STATE_LEFT] = true;
      Serial.print(F("\r\nLeft1")); 
    }
    else{
      joystickState[joystickMapping[0]][STATE_LEFT] = false; 
    }

    if (PS3.getAnalogHat(LeftHatX) == 255){
      joystickState[joystickMapping[0]][STATE_RIGHT] = true;
      Serial.print(F("\r\nRight1")); 
    }
    else{
      joystickState[joystickMapping[0]][STATE_RIGHT] = false; 
    }

    if (PS3.getAnalogHat(LeftHatY) == 0){
      joystickState[joystickMapping[0]][STATE_FORWARD] = true;
      Serial.print(F("\r\nForward1")); 
    }
    else{
      joystickState[joystickMapping[0]][STATE_FORWARD] = false; 
    }   

    if (PS3.getAnalogHat(LeftHatY) == 255){
      joystickState[joystickMapping[0]][STATE_BACKWARD] = true;
      Serial.print(F("\r\nBackward1")); 
    }
    else{
      joystickState[joystickMapping[0]][STATE_BACKWARD] = false; 
    }   
  
   
    if (PS3.getButtonPress(TRIANGLE) || PS3.getButtonPress(CIRCLE) || PS3.getButtonPress(CROSS) || PS3.getButtonPress(SQUARE)){
      Serial.print(F("\r\nButton1_on"));   
      joystickState[joystickMapping[0]][STATE_BUTTON] = true;  
    }
    else{
      joystickState[joystickMapping[0]][STATE_BUTTON] = false;  
    }
  }

// Joystick 2
if (PS3_1.PS3Connected || PS3_1.PS3NavigationConnected) {
    if (PS3_1.getAnalogHat(LeftHatX) == 0){
      joystickState[joystickMapping[1]][STATE_LEFT] = true;
      Serial.print(F("\r\nLeft2")); 
    }
    else{
      joystickState[joystickMapping[1]][STATE_LEFT] = false; 
    }

    if (PS3_1.getAnalogHat(LeftHatX) == 255){
      joystickState[joystickMapping[1]][STATE_RIGHT] = true;
      Serial.print(F("\r\nRight2")); 
    }
    else{
      joystickState[joystickMapping[1]][STATE_RIGHT] = false; 
    }

    if (PS3_1.getAnalogHat(LeftHatY) == 0){
      joystickState[joystickMapping[1]][STATE_FORWARD] = true;
      Serial.print(F("\r\nForward2")); 
    }
    else{
      joystickState[joystickMapping[1]][STATE_FORWARD] = false; 
    }   

    if (PS3_1.getAnalogHat(LeftHatY) == 255){
      joystickState[joystickMapping[1]][STATE_BACKWARD] = true;
      Serial.print(F("\r\nBackward2")); 
    }
    else{
      joystickState[joystickMapping[1]][STATE_BACKWARD] = false; 
    }
    
  if (PS3_1.getButtonPress(TRIANGLE) || PS3_1.getButtonPress(CIRCLE) || PS3_1.getButtonPress(CROSS) || PS3_1.getButtonPress(SQUARE)){
      Serial.print(F("\r\nButton2_on"));   
      joystickState[joystickMapping[1]][STATE_BUTTON] = true;  
    }
    else{
      joystickState[joystickMapping[1]][STATE_BUTTON] = false;  
    }
  }
  Gpio.digitalWrite(LEFT1, joystickState[0][STATE_LEFT]);
  Gpio.digitalWrite(RIGHT1, joystickState[0][STATE_RIGHT]);
  digitalWrite(FORWARD1, joystickState[0][STATE_FORWARD]);
  digitalWrite(BACKWARD1, joystickState[0][STATE_BACKWARD]);
  digitalWrite(LEFT2, joystickState[1][STATE_LEFT]);
  digitalWrite(RIGHT2, joystickState[1][STATE_RIGHT]);
  digitalWrite(FORWARD2, joystickState[1][STATE_FORWARD]);
  digitalWrite(BACKWARD2, joystickState[1][STATE_BACKWARD]);
  Gpio.digitalWrite(BUTTON1, joystickState[0][STATE_BUTTON]);
  Gpio.digitalWrite(BUTTON2, joystickState[1][STATE_BUTTON]);
}

void USBonInit1(){ //runs once at moment of USB controller initialization
  Serial.println(" USB 1 Connected ");
}

void USBonInit2(){ //runs once at moment of USB controller initialization
  Serial.println(" USB 2 Connected ");
}


bool DebounceButton(bool reading){
  static unsigned int lastDebounceTime;
  static bool lastButtonState;
  static bool buttonState;
      
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }  
  
  if ((millis() - lastDebounceTime) > DEBOUNCETIME) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
      buttonState = reading;
   }
  
  lastButtonState = reading;
  return buttonState;
}


bool ToggleButton(bool reading){
    static bool toggleState = LOW;
    static bool lastButtonState = LOW;
    bool buttonState;

    buttonState = DebounceButton(reading);
    
    if((buttonState != lastButtonState) && (buttonState == HIGH)){
      if (toggleState == LOW){
        toggleState = HIGH;
        Serial.println(" Toggle High");
      }
      else{
        toggleState = LOW;
        Serial.println(" Toggle Low");
      }
    }
    lastButtonState = buttonState;
  return toggleState;
}

Schematics

C64 USB joystick interface schematic as Eagle .sch-file
Schematic of the circuit to connect the Arduino UNO and the USB Host Shield to the specific breadboard design.
c64_joystick_q6DD5r2i7t.sch
C64 USB joystick interface schematic as PDF
Schematic of the circuit to connect the Arduino UNO and the USB Host Shield to the specific breadboard design.

Comments

Similar projects you might like

Let's Build an SN76489 USB MIDI Synth With Arduino Nano

Project tutorial by tyrkelko

  • 936 views
  • 1 comment
  • 4 respects

Joystick Controller for MeArm Robot - Recording Coordinates

Project tutorial by utilstudio

  • 11,282 views
  • 6 comments
  • 34 respects

Joystick Game

Project tutorial by xXarduino_11Xx

  • 4,897 views
  • 5 comments
  • 14 respects

Arduino Altair 8800 Simulator

Project tutorial by David Hansel

  • 77,553 views
  • 109 comments
  • 165 respects

Control Your Light Switch with Your Smartphone

Project tutorial by Alasdair Allan

  • 18,602 views
  • 1 comment
  • 34 respects
Add projectSign up / Login