N64 Controller: Done reasearch, need a pros help

Okay so how i understand it from this page:
http://www.mixdown.ca/n64dev/

The n64 controller is 3 cables, 1 grnd, one input (i dont know what for) and a "1 wire interface" that handles almost all of the I/O mentioned in the analysis.

Sending 00000001 (ie 0x01) to this data port returns the overall button status in a 32 bit string, the first 16 are for each of the 14 button positions (ie 0 for off 1 for on) and 2 empty spaces, and the last 16 are for controller position, 8 for L/R and 8 for U/D.

My general question is can i plug this data port into any of the digital ports on my arduino board and write 0x01 to serial and then listen for the button stats? because if so that would make this whole thing extremely easy.

OR do i need to get a little more nitty gritty? it talks at the beginning of the article about how you need to make sure the computer doesnt "up" the signal or you can blow out hardware since the controller has a signal "pullup" installed in it already, is that something i need to be weary of?

I believe you can... but im still learning and dont take my word on it. BUMPY :slight_smile:

yeah it seems that you are able to read and write serial data quite easily except for im not quite sure how the difference between the 1wire system of the nintendo controller (1grnd, 1pwr, 1 data) translates to the tx/rx system of normal serial communication.

Should be able to just wire tx/rx respectively and serial.print the data from the pin to see what you're getting... correct? least how my gps shield works from serial data...

yes except for that i believe the n64 might not exactly follow normal serial baud rates, it sends about 31250 bytes per second (4us per bit, 3us down 1 up for 0, 3us up 1 down for 1), which is not consistant with any type of baud rate, also i tried a some simple code after plugging the controller in and plugging both tx/rx both into the single data wire, i set the baud rate to 38400 since that was the closest rate to the controller but it doesnt seem to work.

int incomingByte=0;
void setup(){
  Serial.begin(38400);
}
void loop(){
 Serial.print(01,HEX);
 if (Serial.available() > 0) {
    incomingByte = Serial.read();
    
    }
  delay(1000);
}

but i get

avrdude: stk500_getsync(): not in sync: resp=0x00
avrdude: stk500_disable(): protocol error, expect=0x14, resp=0x51

is there a way to manually read serial high and low signals? if each signal comes at 1us intervals i should be able to send 0x01 as a raw signal with

void loop(){
//then a for loop to send 7 sequential zeros
  for(i=0;i<7;i++){
    digitalWrite(TX,LOW);
    delay(.003);
    digitalWrite(TX,HIGH);
    delay(.001);
  }
digitalWrite(TX,LOW);  //then write the 1
delay(.001);
digitalWrite(TX,HIGH);
delay(.003);
digitalWrite(TX,LOW);  //then wait for response for 32us
delay(.032);

and then plug an LED in to see if there is any kind of response, then i could work with creating a way to recieve the raw bits.

what are you making with this btw?
have you tried different baud rates and testing besides 38400?

yes, i figured since its 4us per bit, that makes 32us per byte, and 1s/32us=31250 then baud rate might be 31250, also tried the standard 4 settings but i think this is going to take a little bit more in-depth bit banging.

http://courses.cit.cornell.edu/ee476/FinalProjects/s2002/jew17/lld.html
I was reading on another page where someone managed to interface it with ps/2 and they did this by sending 7 zeros and 2 ones, im assuming that the first zero needs to be proceeded by a one since it mentions on the same page that the zeros are initiated by a falling edge.

the thing that is peculiar (ie that i dont understand) is that he did this by setting setting it to input for 1 and output for 0 instead of high and low and he uses the 5v ps/2 connection wheras ive been using the 3.3v (which the controller should respond to based on the other article), why is that?

here is a detailed page on the gamecube controller, which is nearly the same, just add another analog stick

http://www.int03.co.uk/crema/hardware/gamecube/gc-control.htm

might provide some clarification

Also, just curious.. I know it's overlooked quite often, but did you connect the Ground from the controller to the ground of the Arduino?

i sure did captain obvious

:smiley: you never know!

I'm kinda lost on this, lol haven't worked with a 1-wire interface of any kind yet. But one thing you could also try, instead of using the regular delay, try using delayMicroseconds.
delayMicroseconds(3);
http://www.arduino.cc/en/Reference/DelayMicroseconds

I'm not sure if it will work.. but it does compile, you might be able to use Serial.begin(31250);

The only 1-wire interface I'm aware of is:

Not sure if that will be any help.. I went looking for my old n64 stuff but turns out my brother took it, lol. :confused:

ill try it out, also i need to work out the wiring of the whole thing because iwas thinking of maybe using a tx and rx and using diodes to seperate the two data lines and then use diodes to descriminate the connection. would that work?

jackpot!
http://www.arduino.cc/playground/Learning/OneWire

This is probably the key to your solution:

awh, no fun

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

xbee...

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:

/**
 * 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()
{
  Serial.begin(115200);



  // 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;
  noInterrupts();
  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
  N64_get();
  interrupts();
  translate_raw_data();  
}

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");
outer_loop:
    {
        asm volatile (";Starting inner for loop");
        bits=8;
inner_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");
                N64_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"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "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"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\nnop\nnop\nnop\nnop\n"  
                              "nop\n");

                asm volatile (";Setting line to high");
                N64_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");
            --bits;
            if (bits != 0) {
                // nop block 4
                // this block is why a for loop was impossible
                asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
                              "nop\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!)
        --length;
        if (length != 0) {
            ++buffer;
            goto outer_loop;
        } // fall out of outer loop
    }

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

}

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)
read_loop:
    timeout = 0x3f;
    // wait for line to go low
    while (N64_QUERY) {
        if (!--timeout)
            return;
    }
    // wait approx 2us and poll the line
    asm volatile (
                  "nop\nnop\nnop\nnop\nnop\n"  
                  "nop\nnop\nnop\nnop\nnop\n"  
                  "nop\nnop\nnop\nnop\nnop\n"  
                  "nop\nnop\nnop\nnop\nnop\n"  
                  "nop\nnop\nnop\nnop\nnop\n"  
                  "nop\nnop\nnop\nnop\nnop\n"  
            );
    *bitbin = N64_QUERY;
    ++bitbin;
    --bitcount;
    if (bitcount == 0)
        return;

    // 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)
            return;
    }
    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.println();
    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);

continued:
.....

   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
    //print_N64_status();
    delay(25);
}