Go Down

Topic: N64 Controller: Done reasearch, need a pros help (Read 7034 times) previous topic - next topic


Jan 02, 2010, 06:49 pm Last Edit: Jan 02, 2010, 06:49 pm by dashdanw Reason: 1
okay now that that is figured out i want to get a way to send the information over wireless, what do you think is the best way to do this?

ive been reading up on

but theyre mostly talking about transmitting serial data and thats going to a be a bit different, im sure with the arduino it will be alot more simple to just use xbee or something like that

any ideas?


but theyre mostly talking about transmitting serial data and thats going to a be a bit different, im sure with the arduino it will be alot more simple to just use xbee or something like that


Jan 03, 2010, 09:33 pm Last Edit: Jan 03, 2010, 09:34 pm by dashdanw Reason: 1
ive been fooling around with that code but i can not for the life of me get it to work, it will not budge, ive been through the code and followed it for par, any ideas?
heres the code:

Code: [Select]
* Gamecube controller to Nintendo 64 adapter
* by Andrew Brown
* Rewritten for N64 to HID by Peter Den Hartog

* To use, hook up the following to the Arduino Duemilanove:
* Digital I/O 2: N64 serial line
* All appropriate grounding and power lines

#include "pins_arduino.h"

#define N64_PIN 2
#define N64_PIN_DIR DDRD
// these two macros set arduino pin 2 to input or output, which with an
// external 1K pull-up resistor to the 3.3V rail, is like pulling it high or
// low.  These operations translate to 1 op code, which takes 2 cycles
#define N64_HIGH DDRD &= ~0x04
#define N64_LOW DDRD |= 0x04
#define N64_QUERY (PIND & 0x04)

// 8 bytes of data that we get from the controller
struct {
   // bits: 0, 0, 0, start, y, x, b, a
   unsigned char data1;
   // bits: 1, L, R, Z, Dup, Ddown, Dright, Dleft
   unsigned char data2;
   char stick_x;
   char stick_y;
} N64_status;
char N64_raw_dump[33]; // 1 received bit per byte

void N64_send(unsigned char *buffer, char length);
void N64_get();
void print_N64_status();
void translate_raw_data();

#include "crc_table.h"

void setup()

 // Communication with gamecube controller on this pin
 // Don't remove these lines, we don't want to push +5V to the controller
 digitalWrite(N64_PIN, LOW);  
 pinMode(N64_PIN, INPUT);

 // Initialize the gamecube controller by sending it a null byte.
 // This is unnecessary for a standard controller, but is required for the
 // Wavebird.
 unsigned char initialize = 0x00;
 N64_send(&initialize, 1);

 // Stupid routine to wait for the gamecube controller to stop
 // sending its response. We don't care what it is, but we
 // can't start asking for status if it's still responding
 int x;
 for (x=0; x<64; x++) {
     // make sure the line is idle for 64 iterations, should
     // be plenty.
     if (!N64_QUERY)
         x = 0;

 // Query for the gamecube controller's status. We do this
 // to get the 0 point for the control stick.
 unsigned char command[] = {0x01};
 N64_send(command, 1);
 // read in data and dump it to N64_raw_dump

void translate_raw_data()
   // The get_N64_status function sloppily dumps its data 1 bit per byte
   // into the get_status_extended char array. It's our job to go through
   // that and put each piece neatly into the struct N64_status
   int i;
   memset(&N64_status, 0, sizeof(N64_status));
   // line 1
   // bits: A, B, Z, Start, Dup, Ddown, Dleft, Dright
   for (i=0; i<8; i++) {
       N64_status.data1 |= N64_raw_dump[i] ? (0x80 >> i) : 0;
   // line 2
   // bits: 0, 0, L, R, Cup, Cdown, Cleft, Cright
   for (i=0; i<8; i++) {
       N64_status.data2 |= N64_raw_dump[8+i] ? (0x80 >> i) : 0;
   // line 3
   // bits: joystick x value
   // These are 8 bit values centered at 0x80 (128)
   for (i=0; i<8; i++) {
       N64_status.stick_x |= N64_raw_dump[16+i] ? (0x80 >> i) : 0;
   for (i=0; i<8; i++) {
       N64_status.stick_y |= N64_raw_dump[24+i] ? (0x80 >> i) : 0;

* This sends the given byte sequence to the controller
* length must be at least 1
* Oh, it destroys the buffer passed in as it writes it
void N64_send(unsigned char *buffer, char length)
   // Send these bytes
   char bits;
   bool bit;

   // This routine is very carefully timed by examining the assembly output.
   // Do not change any statements, it could throw the timings off
   // We get 16 cycles per microsecond, which should be plenty, but we need to
   // be conservative. Most assembly ops take 1 cycle, but a few take 2
   // I use manually constructed for-loops out of gotos so I have more control
   // over the outputted assembly. I can insert nops where it was impossible
   // with a for loop
   asm volatile (";Starting outer for loop");
       asm volatile (";Starting inner for loop");
           // Starting a bit, set the line low
           asm volatile (";Setting line to low");
           N64_LOW; // 1 op, 2 cycles

           asm volatile (";branching");
           if (*buffer >> 7) {
               asm volatile (";Bit is a 1");
               // 1 bit
               // remain low for 1us, then go high for 3us
               // nop block 1
               asm volatile ("nop\nnop\nnop\nnop\nnop\n");
               asm volatile (";Setting line to high");

               // nop block 2
               // we'll wait only 2us to sync up with both conditions
               // at the bottom of the if statement
               asm volatile ("nop\nnop\nnop\nnop\nnop\n"  

           } else {
               asm volatile (";Bit is a 0");
               // 0 bit
               // remain low for 3us, then go high for 1us
               // nop block 3
               asm volatile ("nop\nnop\nnop\nnop\nnop\n"  

               asm volatile (";Setting line to high");

               // wait for 1us
               asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
           // end of the if, the line is high and needs to remain
           // high for exactly 16 more cycles, regardless of the previous
           // branch path

           asm volatile (";finishing inner loop body");
           if (bits != 0) {
               // nop block 4
               // this block is why a for loop was impossible
               asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
               // rotate bits
               asm volatile (";rotating out bits");
               *buffer <<= 1;

               goto inner_loop;
           } // fall out of inner loop
       asm volatile (";continuing outer loop");
       // In this case: the inner loop exits and the outer loop iterates,
       // there are /exactly/ 16 cycles taken up by the necessary operations.
       // So no nops are needed here (that was lucky!)
       if (length != 0) {
           goto outer_loop;
       } // fall out of outer loop

   // send a single stop (1) bit
   // nop block 5
   asm volatile ("nop\nnop\nnop\nnop\n");
   // wait 1 us, 16 cycles, then raise the line
   // 16-2=14
   // nop block 6
   asm volatile ("nop\nnop\nnop\nnop\nnop\n"


void N64_get()
   // listen for the expected 8 bytes of data back from the controller and
   // blast it out to the N64_raw_dump array, one bit per byte for extra speed.
   // Afterwards, call translate_raw_data() to interpret the raw data and pack
   // it into the N64_status struct.
   asm volatile (";Starting to listen");
   unsigned char timeout;
   char bitcount = 32;
   char *bitbin = N64_raw_dump;

   // Again, using gotos here to make the assembly more predictable and
   // optimization easier (please don't kill me)
   timeout = 0x3f;
   // wait for line to go low
   while (N64_QUERY) {
       if (!--timeout)
   // wait approx 2us and poll the line
   asm volatile (
   *bitbin = N64_QUERY;
   if (bitcount == 0)

   // wait for line to go high again
   // it may already be high, so this should just drop through
   timeout = 0x3f;
   while (!N64_QUERY) {
       if (!--timeout)
   goto read_loop;


void print_N64_status()
   int i;
   // bits: A, B, Z, Start, Dup, Ddown, Dleft, Dright
   // bits: 0, 0, L, R, Cup, Cdown, Cleft, Cright
   Serial.print("Start: ");
   Serial.println(N64_status.data1 & 16 ? 1:0);

   Serial.print("Z:     ");
   Serial.println(N64_status.data1 & 32 ? 1:0);

   Serial.print("B:     ");
   Serial.println(N64_status.data1 & 64 ? 1:0);

   Serial.print("A:     ");
   Serial.println(N64_status.data1 & 128 ? 1:0);


Code: [Select]

  for (i=0; i<16; i++) {
      Serial.print(N64_raw_dump[i], DEC);
   Serial.print(' ');
   Serial.print(N64_status.stick_x, DEC);
   Serial.print(' ');
   Serial.print(N64_status.stick_y, DEC);
   Serial.print(" \n");
  //   Serial.print("         Stick X:");
//   Serial.print(N64_status.stick_x, DEC);
 //  Serial.print("         Stick Y:");
//Serial.println(N64_status.stick_y, DEC);

   // DEBUG: print it


my real hurdle here is i do not understand assembly code in the least so the whole polling step is beyond me


If you want to connect an N64 controller (or a Gamecube controller - they both use a very similar protocol) to an Arduino, the code needs to be done in Assembly. The smallest time unit in the protocol is 1 microsecond (just 16 clock cycles at 16 MHz), and this precision is not possible in C. It is not the usual serial protocol so you cannot connect it to the serial pins! ALSO, the controller is 3.3V not 5V, connecting it directly to the Arduino serial pins may damage it.

The Instructables link Aleator777 posted (http://www.instructables.com/id/Use-an-Arduino-with-an-N64-controller/) seems to have code that should work (I haven't tested). Make sure you are connecting the controller properly. Use this diagram: http://www.instructables.com/id/Use-an-Arduino-with-an-N64-controller/step1/Wiring-the-controller/

Connect ground to the Arduino ground. Connect the +3.3 V line to the 3V3 line on the Arduino (the Arduino has a 3.3V regulator on board). Do not connect it to 5V. For the Signal line, plug it directly to an Arduino digital pin 2 (if you are using the Instructables code) and then connect a 1K pullup resistor between pin 2 and the 3.3V line.


Jan 05, 2010, 04:53 am Last Edit: Jan 05, 2010, 05:38 am by dashdanw Reason: 1
oh i actually have to put the pullup resistor between the circut? ugh its math time.... i have 410ohms, 100 ohms and a few 10 ohms....hope this works, im going to series the resistors to get 920 which should do the trick correct?


hmm, it seems that if the arduino is plugged in when i make the connection the the controller it restarts the arduino i hear the chime on my windows computer for the device being unplugged and then immediately being plugged back in...what should i do?


If the board is resetting like that then you are probably creating a short-circuit somewhere!

The the pullup resistor is needed otherwise there's no way to get the necessary 3.3V for the signal line (directly driving it from an Arduino pin would be 5V). Instead what the code does is set the Arduino pin floating (pinMode(2, INPUT); digitalWrite(2, LOW);) for 3.3V and pull it to ground (pinMode(2, OUTPUT); digitalWrite(2, LOW);) to make 0V on the data line.


okay i jimmy rigged the resistance with 2 2.2k resistors and a 10k resistor in parallel (equals roughtly 1000-950ohm on my multimeter)

i connected that between the 3.3v connection like so:


Yeah that should be fine. But (and this may just be a flaw with your diagram) if looking at the N64 connector, the middle wire is the data line not the outer one. If I get a chance later I'll test the code on Instructables, but right now I'm trying to work on my own implementation.


thanks so much for your help waffles, it is much appreciated

yeah i just switched them around because thats how ive breadboarded it, i cut off the connector of this one because its an old junker, so as long as the colors correspond (which ive verified) its correct.

Ive been mulling through the code (slowly learning assembly as i go!) but if you want to plug it in and give it a whirl you would be my savior


I just gave the Instructables code a try and it's working fine.


Go Up

Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

via Egeo 16
Torino, 10131