I've been learning electronics and playing with an Arduino lately. I realized today I have a functional (but worn) Nintendo 64 joystick which for a beginner like me, it would be a fun challenge to figure out how it works. On a side note: Amazon carries a replacement N64 joystick module that's better engineered than the original - it should never wear out!

Inside of the joystick module, is nice and simple 1-sided board. I couldn't conjure up a datasheet from googling markings on any single component on the inside, so we're on our own! The circuitry inside looked like this:

Simply put, pins 2 and 3 are power and ground (respectively), 1&4 is data for left/right, and 5/6 for up/down. I measured approx 3.3v volts across pins 2&3 in a working controller, so we're in luck! - The arduino provides a 3.3v rail. I measured that the joystick circuit pulls about 15mA, so we're more than in the clear to power it from the Arduino. R1/R2 are 320Ω; I didn't have equipment to measure C1/C2.

In the image above, two slotted disks pass through the cutouts near U1 and U2, one for each axis. The movement of the joystick is geared to each slotted disk, so the precision actually looks pretty high.

I also don't own an oscilloscope so figuring out the format that the sensors return data could be pretty tricky - guesswork, really. But again, we're in luck! - Someone's created an easy way to use the arduino as a basic oscilloscope! Aren't general purpose processors awesome?

However, viewing the output from any data pin all looked the same - a flat HIGH or LOW signal when the stick is moving, and rapid oscilation between high and low during motion. The demo code only supports a single input, but theoretically could be expanded to 6 as the Arduino contains 6 analog inputs, but I'm not wanting to dive into that yet. After some research, I found that the sensors in the joystick appear to be two linear sensors! This kind of sensor uses optics to track when a slotted medium interrupts some beams of light and encodes the result into two waveforms who's period indicates speed of motion and offset from eachother determines direction of motion. This video does a great job of explaining it, and even provides sample code (which I stole).

So, how to use this component with an Arduino? Pins 2 & 3 can be connected directly to the 3.3v rail & ground. 1 & 4 can go to any digital inputs; 5 & 6 must go to digital inputs that support interrupts. I modified code from the video linked above to create the following:

#define encoderIx 2 #define encoderQx 3 #define encoderIy 18 #define encoderQy 19 volatile int countx; volatile int county; void setup() { Serial.begin(9600); countx=0; county=0; pinMode(encoderIx, INPUT); pinMode(encoderQx, INPUT); pinMode(encoderIy, INPUT); pinMode(encoderQy, INPUT); attachInterrupt(0, handleEncoderX, CHANGE); attachInterrupt(5, handleEncoderY, CHANGE); } void loop() { Serial.print("X: "); Serial.print(countx); Serial.print(" Y: "); Serial.println(county); delay(10); } void handleEncoderX() { if(digitalRead(encoderIx) == digitalRead(encoderQx)) { countx++; } else{ countx--; } } void handleEncoderY() { if(digitalRead(encoderIy) == digitalRead(encoderQy)) { county++; } else{ county--; } }

How this works is pretty simple - X and Y are handled separately, and store in two ints. We'll look at just a single axis. When the signal to the interrupt-supporting pin is changed, it is compated to the other pin on the same axis. The joystick's linear encoders express forward/backward as one wave lagging or leading the other - and at the instant the interrupt function is ran, comparing the interrupt pin to the 2nd input from the same sensor allows us to determine this. Depending if the other input is high or low, we either increment or decrement the position counter for this axis.

The sample code above will print the X and Y position of the stick to the arduino's serial console. I used an arduino mega with the code above; the interrupt-supporting pin numbers might not necessarily be the same for other arduino models.

Happy hacking!