Hackster is hosting Hackster Holidays, Finale: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Tuesday!Stream Hackster Holidays, Finale on Tuesday!
giobbino
Published © GPL3+

Fancy Joystick for Atari 2600, Commodore 64, VIC-20, 128...

Using an analog joystick and Arduino Nano to create a fancy joystick for Atari 2600, Commodore 64, VIC-20, 128...

BeginnerFull instructions provided796
Fancy Joystick for Atari 2600, Commodore 64, VIC-20, 128...

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
General Purpose Transistor NPN
General Purpose Transistor NPN
×5
Modulo Joystick
Modulo Joystick
×1
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×1
D Sub Connector, DB9
D Sub Connector, DB9
**FEMALE** - joystick connector
×1
Slide Switch
Slide Switch
×1

Story

Read more

Schematics

schematics

Code

Arduino code

Arduino
I tried to make the code as clear as possible. I also opted for not optimizing the code, leaving it easier to read and understand.
/**********************************************************************************************************
 * 
 * ARDUINO BASED, ATARI 2600 COMPATIBLE JOYSTICK - SUITABLE FOR COMMODORE 64 & VIC-20 (TESTED ON C=64)
 * 
 * Schematics:
 * 
 *-------------------------------------------|
 * Joystick port [1] | Arduino Nano/Uno/etc. |
 * ----------------- | --------------------- |
 *    1 up         [NPN]   D2                | [NPN] means one NPN transistor like BC547 (see below)
 *    2 down       [NPN]   D4                |
 *    3 left       [NPN]   D6                |
 *    4 right      [NPN]   D8                |
 *    5 paddle Y     |    (Not Connected)    |
 *    6 fire       [NPN]   D10               |
 *    7 +5v 100mA    |     +5V               |
 *    8 GND          |     GND               |
 *    9 paddle X     |    (Not Connected)    |
 *                   |                       |
 * Analog Joystick   |                       |
 *   (movement)      |                       |
 * ----------------- |                       |
 *     Y axis        |     A0                |
 *     X axis        |     A1                |
 *     Switch        |    (Not Connected [2])|
 *     +5V           |     +5V               |
 *     GND           |     GND               |
 *                   |                       |
 * Momentary switch  |                       |
 *  (fire button)    |                       |
 * ----------------- |                       |
 *     1             |     A2                |
 *     2             |     GND               |
 *                   |                       |
 * switch 2 way [3]  |                       |
 * (autofire on/off) |                       |
 * ----------------- |                       |
 *     1             |     D12               |
 *    COM            |     GND               |
 *     2             |    (Not Connected)    |
 *-------------------------------------------|
 *
 * Notes
 * [1]: DON'T CONNECT THE JOYSTICK PORT DIRECTLY TO THE ARDUINO PINS (EXCEPT FOR +5V AND GND)!!!! 
 *      Every connection (up, down, left, right, fire) is driven by an NPN transistor (see below)  
 * 
 * [2]: at first I connected the analog joystick switch to the other switch, parallel, So there were 
 *      two switches doing the same thing. Ufortunately it didn't work fine for me: it often had 
 *       strange behaviors and returned wrong values to Arduino, so I decided to leave it unconnected.
 *       
 * [3]: if you don't need an autofire switch, simply doesn't connect any switch and the autofire 
 *      will be disable by default 
 *      
 * --------------------------------------------------------------------------------------------------     
 * 
 * You can't connect the joystick port directly to Arduino, it doesn't work. You need to drive it 
 * using an NPN transistor (I used five BC547) connecting it as shown here. 
 * Note: you must to replicate it for every connection between the joystick port and Arduino;
 *       it means you will need 5 BC547 transistors (2N3904 should be ok too, but I didn't test them)
 * 
 *                                         NPN transistor
 *                                               
 * Commodore 64 Joystick pin 1 (UP) ---- ----C-.   .-E-->  -------- GND
 *                                              \ /
 *                                             --B--
 *                                               |
 *  Arduino pin D2 ------------------------------+
 *       
 *      
 *********************************************************************************************************/

//defining where any input and output is connected on the Arduino pins

//analog ports for the analog joystick and the fire button
#define JOY_Y  A1  
#define JOY_X  A0
#define JOY_B  A3

//autofire switch (optional)
#define JOY_AUTOFIRE D12

//digital output, to the C=64 joystick port (through transistor)
#define JOY_UP     D2
#define JOY_DOWN   D4
#define JOY_LEFT   D6
#define JOY_RIGHT  D8
#define JOY_FIRE   D10

int joy_x_value = 0;
int joy_y_value = 0;
int joy_b_value = 0;

int internalledstatus = 0;  //this is just for the Arduino onboard LED (that will blink at every movement of fire)

//declaring the threshold as int value (instead of a #define constant) so we can add a calibration function later
//these values are the thresholds for the X and Y values and the button. You can change them in case your
//joystick doesn't work in some position or the axis or the button activate too soon.
//in the setup() and loop() function there are some code lines you can activate to read these values
//using the serial monitor. Search for "CALIBRATE" inside this code
int joy_x_l_ths = 600;  //low  X threshold
int joy_x_h_ths = 4000; //high X threshold
int joy_y_l_ths = 600;  //low  Y threshold
int joy_y_h_ths = 4000; //high Y threshold
int joy_b_l_ths = 10;  //analog button threshold

//autofire variables
float autofiredelay = millis();
bool autofiretoggle = false;
int  autofirespeed  = 50; //millisec


//===================================================================================================================================================

void setup() { 

  pinMode(LED_BUILTIN, OUTPUT);      //setting the Arduino LED as an output
  
  digitalWrite(LED_BUILTIN, HIGH);   //switching the Arduino LED on 
  delay(4000); //before to do anything, just wait some time for the Commodore 64 / VIC-20 boot sequence (without it, the C=64 doesn't boot if the autofire is on... strange...)  
  digitalWrite(LED_BUILTIN, LOW);  //switching the Arduino LED off so we know when the joystick starts to work
    
  pinMode(JOY_AUTOFIRE, INPUT_PULLUP);  //the autofire switch 

  //declaring the output pins
  pinMode(JOY_UP     ,OUTPUT);  
  pinMode(JOY_DOWN   ,OUTPUT);
  pinMode(JOY_LEFT   ,OUTPUT);
  pinMode(JOY_RIGHT  ,OUTPUT);
  pinMode(JOY_FIRE   ,OUTPUT);


  //------------------------------------------------------------------------------------------------------------------------------------------------------\-
  //CALIBRATE - activate the following lines, compile and run the Serial Monitor from inside the Arduino IDE (menu Tools -> Serial Monitor or Ctrl+Shift+M)
  //Note: if the Serial Monitor doesn't work (shows garbage, etc.) check the serial speed in the Serial Monitor is set to 115200 
  /*
  Serial.begin(115200);   
  Serial.println("Start --- ");
  */
  //------------------------------------------------------------------------------------------------------------------------------------------------------/- 
}

//===================================================================================================================================================

void loop() {

  //reading the analog values from the joystick axis and the button
  joy_x_value = analogRead(JOY_X);
  joy_y_value = analogRead(JOY_Y);
  joy_b_value = analogRead(JOY_B);  

  //------------------------------------------------------------------------------------------------------------------------------------------------------\-
  //CALIBRATE - activate the following lines, compile and run the Serial Monitor from inside the Arduino IDE (menu Tools -> Serial Monitor or Ctrl+Shift+M)
  //Note: if the Serial Monitor doesn't work (shows garbage, etc.) check the serial speed in the Serial Monitor is set to 115200 
  /*  
  Serial.print("-- joy_x_value = "); Serial.print(joy_x_value); 
  Serial.print("-- joy_y_value = "); Serial.print(joy_y_value); 
  Serial.print("-- joy_b_value = "); Serial.print(joy_b_value); 
  Serial.println("");
  delay(100);  
  */
  //------------------------------------------------------------------------------------------------------------------------------------------------------/-

  
  
  //reset the internal led status. It starts with a zero value that will be incremented at any joystick movement or fire.
  //at the end of the loop function, if the value is >0 we will switch the internal led on.
  //NOTE: the internal led is only used as feedback to know the joystick is working.
  internalledstatus = 0;  

  //-----------------------------------------------------------------------------------------------------------------------------------\-
  //fire button.  

  //-autofire OFF - begin --------------------------------------------------------------------------\-
  if (digitalRead(JOY_AUTOFIRE) == HIGH) {  
     //if the autofire is off, we just fire if the button is pressed.
     if (joy_b_value < joy_b_l_ths) { digitalWrite(JOY_FIRE ,HIGH) ; internalledstatus++; }   
     else                           { digitalWrite(JOY_FIRE ,LOW); }         
  }
  //-autofire OFF - end ----------------------------------------------------------------------------/-

  //-autofire ON  - begin --------------------------------------------------------------------------\-
  else { 
        
     if (joy_b_value >=joy_b_l_ths) { digitalWrite(JOY_FIRE ,LOW); autofiretoggle = false; }  //the button is not pressed. 
     
     else {  //fire button pressed  -- //sequence: ON - 50 millisec - OFF - 50 millisec - ON
                
          if (!autofiretoggle) {
            if (autofiredelay + autofirespeed < millis()) {  //at least <autofirespeed> milliseconds passed from the last change of status (ON to OFF). Time to switch ON
                autofiretoggle = true;             //at least <autofirespeed> milliseconds passed from the last change of status (was ON to OFF). Time to toggle the switch ON
                autofiredelay = millis();          //resetting the milliseconds counter
                { digitalWrite(JOY_FIRE ,HIGH);   internalledstatus++;  }   //fire ON!
            }           
          }
          else {
          if (autofiredelay + autofirespeed < millis()) {   //at least <autofirespeed> milliseconds passed from the last change of status (OFF to ON). Time to switch OFF
                autofiretoggle = false;           //at least <autofirespeed> milliseconds passed from the last change of status (was OFF to ON). Time to toggle the switch OFF 
                autofiredelay = millis();         //resetting the milliseconds counter
                { digitalWrite(JOY_FIRE ,LOW);  }                           //fire OFF!
            }
        }               
     }    
  }
  //-autofire ON  - end ----------------------------------------------------------------------------/-
  
  
  if (joy_y_value < joy_y_l_ths) { digitalWrite(JOY_DOWN ,HIGH); internalledstatus++; } else { digitalWrite(JOY_DOWN ,LOW); }  //the Y value is below the lower  Y threshold, activating the DOWN  movement  
  if (joy_y_value > joy_y_h_ths) { digitalWrite(JOY_UP   ,HIGH); internalledstatus++; } else { digitalWrite(JOY_UP   ,LOW); }  //the Y value is above the higher Y threshold, activating the UP    movement
  
  if (joy_x_value > joy_x_h_ths) { digitalWrite(JOY_RIGHT,HIGH); internalledstatus++; } else { digitalWrite(JOY_RIGHT,LOW); }  //the X value is below the lower  X threshold, activating the RIGHT movement
  if (joy_x_value < joy_x_l_ths) { digitalWrite(JOY_LEFT ,HIGH); internalledstatus++; } else { digitalWrite(JOY_LEFT ,LOW); }  //the X value is above the higher X threshold, activating the LEFT  movement   


  //if you don't want the internal led blinks, just comment the following line.
  if (internalledstatus > 0)   digitalWrite(LED_BUILTIN, HIGH); else digitalWrite(LED_BUILTIN, LOW);   //activating the Arduino internal LED if there was some movement / fire
  

  delay(50); //some little delay. You can try to remove it and test in your own environment; but in my case the joystick seems to work better (the C=64 is quite slow compared with an Arduino...)
  
}

Credits

giobbino
4 projects • 4 followers

Comments