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...
Components and supplies
1
D Sub Connector, DB9
1
Modulo Joystick
1
Arduino Nano R3
5
General Purpose Transistor NPN
1
Slide Switch
1
Pushbutton Switch, Momentary
Project description
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.
1/********************************************************************************************************** 2 * 3 * ARDUINO BASED, ATARI 2600 COMPATIBLE JOYSTICK - SUITABLE FOR COMMODORE 64 & VIC-20 (TESTED ON C=64) 4 * 5 * Schematics: 6 * 7 *-------------------------------------------| 8 * Joystick port [1] | Arduino Nano/Uno/etc. | 9 * ----------------- | --------------------- | 10 * 1 up [NPN] D2 | [NPN] means one NPN transistor like BC547 (see below) 11 * 2 down [NPN] D4 | 12 * 3 left [NPN] D6 | 13 * 4 right [NPN] D8 | 14 * 5 paddle Y | (Not Connected) | 15 * 6 fire [NPN] D10 | 16 * 7 +5v 100mA | +5V | 17 * 8 GND | GND | 18 * 9 paddle X | (Not Connected) | 19 * | | 20 * Analog Joystick | | 21 * (movement) | | 22 * ----------------- | | 23 * Y axis | A0 | 24 * X axis | A1 | 25 * Switch | (Not Connected [2])| 26 * +5V | +5V | 27 * GND | GND | 28 * | | 29 * Momentary switch | | 30 * (fire button) | | 31 * ----------------- | | 32 * 1 | A2 | 33 * 2 | GND | 34 * | | 35 * switch 2 way [3] | | 36 * (autofire on/off) | | 37 * ----------------- | | 38 * 1 | D12 | 39 * COM | GND | 40 * 2 | (Not Connected) | 41 *-------------------------------------------| 42 * 43 * Notes 44 * [1]: DON'T CONNECT THE JOYSTICK PORT DIRECTLY TO THE ARDUINO PINS (EXCEPT FOR +5V AND GND)!!!! 45 * Every connection (up, down, left, right, fire) is driven by an NPN transistor (see below) 46 * 47 * [2]: at first I connected the analog joystick switch to the other switch, parallel, So there were 48 * two switches doing the same thing. Ufortunately it didn't work fine for me: it often had 49 * strange behaviors and returned wrong values to Arduino, so I decided to leave it unconnected. 50 * 51 * [3]: if you don't need an autofire switch, simply doesn't connect any switch and the autofire 52 * will be disable by default 53 * 54 * -------------------------------------------------------------------------------------------------- 55 * 56 * You can't connect the joystick port directly to Arduino, it doesn't work. You need to drive it 57 * using an NPN transistor (I used five BC547) connecting it as shown here. 58 * Note: you must to replicate it for every connection between the joystick port and Arduino; 59 * it means you will need 5 BC547 transistors (2N3904 should be ok too, but I didn't test them) 60 * 61 * NPN transistor 62 * 63 * Commodore 64 Joystick pin 1 (UP) ---- ----C-. .-E--> -------- GND 64 * \ / 65 * --B-- 66 * | 67 * Arduino pin D2 ------------------------------+ 68 * 69 * 70 *********************************************************************************************************/ 71 72//defining where any input and output is connected on the Arduino pins 73 74//analog ports for the analog joystick and the fire button 75#define JOY_Y A1 76#define JOY_X A0 77#define JOY_B A3 78 79//autofire switch (optional) 80#define JOY_AUTOFIRE D12 81 82//digital output, to the C=64 joystick port (through transistor) 83#define JOY_UP D2 84#define JOY_DOWN D4 85#define JOY_LEFT D6 86#define JOY_RIGHT D8 87#define JOY_FIRE D10 88 89int joy_x_value = 0; 90int joy_y_value = 0; 91int joy_b_value = 0; 92 93int internalledstatus = 0; //this is just for the Arduino onboard LED (that will blink at every movement of fire) 94 95//declaring the threshold as int value (instead of a #define constant) so we can add a calibration function later 96//these values are the thresholds for the X and Y values and the button. You can change them in case your 97//joystick doesn't work in some position or the axis or the button activate too soon. 98//in the setup() and loop() function there are some code lines you can activate to read these values 99//using the serial monitor. Search for "CALIBRATE" inside this code 100int joy_x_l_ths = 600; //low X threshold 101int joy_x_h_ths = 4000; //high X threshold 102int joy_y_l_ths = 600; //low Y threshold 103int joy_y_h_ths = 4000; //high Y threshold 104int joy_b_l_ths = 10; //analog button threshold 105 106//autofire variables 107float autofiredelay = millis(); 108bool autofiretoggle = false; 109int autofirespeed = 50; //millisec 110 111 112//=================================================================================================================================================== 113 114void setup() { 115 116 pinMode(LED_BUILTIN, OUTPUT); //setting the Arduino LED as an output 117 118 digitalWrite(LED_BUILTIN, HIGH); //switching the Arduino LED on 119 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...) 120 digitalWrite(LED_BUILTIN, LOW); //switching the Arduino LED off so we know when the joystick starts to work 121 122 pinMode(JOY_AUTOFIRE, INPUT_PULLUP); //the autofire switch 123 124 //declaring the output pins 125 pinMode(JOY_UP ,OUTPUT); 126 pinMode(JOY_DOWN ,OUTPUT); 127 pinMode(JOY_LEFT ,OUTPUT); 128 pinMode(JOY_RIGHT ,OUTPUT); 129 pinMode(JOY_FIRE ,OUTPUT); 130 131 132 //------------------------------------------------------------------------------------------------------------------------------------------------------\\- 133 //CALIBRATE - activate the following lines, compile and run the Serial Monitor from inside the Arduino IDE (menu Tools -> Serial Monitor or Ctrl+Shift+M) 134 //Note: if the Serial Monitor doesn't work (shows garbage, etc.) check the serial speed in the Serial Monitor is set to 115200 135 /* 136 Serial.begin(115200); 137 Serial.println("Start --- "); 138 */ 139 //------------------------------------------------------------------------------------------------------------------------------------------------------/- 140} 141 142//=================================================================================================================================================== 143 144void loop() { 145 146 //reading the analog values from the joystick axis and the button 147 joy_x_value = analogRead(JOY_X); 148 joy_y_value = analogRead(JOY_Y); 149 joy_b_value = analogRead(JOY_B); 150 151 //------------------------------------------------------------------------------------------------------------------------------------------------------\\- 152 //CALIBRATE - activate the following lines, compile and run the Serial Monitor from inside the Arduino IDE (menu Tools -> Serial Monitor or Ctrl+Shift+M) 153 //Note: if the Serial Monitor doesn't work (shows garbage, etc.) check the serial speed in the Serial Monitor is set to 115200 154 /* 155 Serial.print("-- joy_x_value = "); Serial.print(joy_x_value); 156 Serial.print("-- joy_y_value = "); Serial.print(joy_y_value); 157 Serial.print("-- joy_b_value = "); Serial.print(joy_b_value); 158 Serial.println(""); 159 delay(100); 160 */ 161 //------------------------------------------------------------------------------------------------------------------------------------------------------/- 162 163 164 165 //reset the internal led status. It starts with a zero value that will be incremented at any joystick movement or fire. 166 //at the end of the loop function, if the value is >0 we will switch the internal led on. 167 //NOTE: the internal led is only used as feedback to know the joystick is working. 168 internalledstatus = 0; 169 170 //-----------------------------------------------------------------------------------------------------------------------------------\\- 171 //fire button. 172 173 //-autofire OFF - begin --------------------------------------------------------------------------\\- 174 if (digitalRead(JOY_AUTOFIRE) == HIGH) { 175 //if the autofire is off, we just fire if the button is pressed. 176 if (joy_b_value < joy_b_l_ths) { digitalWrite(JOY_FIRE ,HIGH) ; internalledstatus++; } 177 else { digitalWrite(JOY_FIRE ,LOW); } 178 } 179 //-autofire OFF - end ----------------------------------------------------------------------------/- 180 181 //-autofire ON - begin --------------------------------------------------------------------------\\- 182 else { 183 184 if (joy_b_value >=joy_b_l_ths) { digitalWrite(JOY_FIRE ,LOW); autofiretoggle = false; } //the button is not pressed. 185 186 else { //fire button pressed -- //sequence: ON - 50 millisec - OFF - 50 millisec - ON 187 188 if (!autofiretoggle) { 189 if (autofiredelay + autofirespeed < millis()) { //at least <autofirespeed> milliseconds passed from the last change of status (ON to OFF). Time to switch ON 190 autofiretoggle = true; //at least <autofirespeed> milliseconds passed from the last change of status (was ON to OFF). Time to toggle the switch ON 191 autofiredelay = millis(); //resetting the milliseconds counter 192 { digitalWrite(JOY_FIRE ,HIGH); internalledstatus++; } //fire ON! 193 } 194 } 195 else { 196 if (autofiredelay + autofirespeed < millis()) { //at least <autofirespeed> milliseconds passed from the last change of status (OFF to ON). Time to switch OFF 197 autofiretoggle = false; //at least <autofirespeed> milliseconds passed from the last change of status (was OFF to ON). Time to toggle the switch OFF 198 autofiredelay = millis(); //resetting the milliseconds counter 199 { digitalWrite(JOY_FIRE ,LOW); } //fire OFF! 200 } 201 } 202 } 203 } 204 //-autofire ON - end ----------------------------------------------------------------------------/- 205 206 207 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 208 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 209 210 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 211 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 212 213 214 //if you don't want the internal led blinks, just comment the following line. 215 if (internalledstatus > 0) digitalWrite(LED_BUILTIN, HIGH); else digitalWrite(LED_BUILTIN, LOW); //activating the Arduino internal LED if there was some movement / fire 216 217 218 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...) 219 220} 221
Downloadable files
schematics
schematics

Comments
Only logged in users can leave comments