El Nunchuk utiliza el bus de trasmisión de datos I²C. I²C es un bus de comunicaciones en serie. Su principal característica es que utiliza dos lineas de transmisión de información. Una de ellas es la señal de reloj(SCL) y la otra para los datos(SDA). Aunque también es necesaria una tercera línea de tierra(GND).


Cada dispositivo que se conecte al bus tienen una dirección única. Además, existen dos tipos de dispositivos en este protocolo:
  • Maestros: Inician la transmisión de datos y genera la señal de reloj.
  • Esclavos: Reciben la señal de reloj y envían datos al maestro.


     I²C también permite que el maestro no sea siempre el mismo, se trata de un bus multimaestro.

    wikipedia.org

    Las transmisiones en este tipo de bus tienen la siguiente estructura:

    | start | A7 A6 A5 A4 A3 A2 A1 | R/W | ACK | ... DATA ... | ACK | stop | idle |

    Características de  I²C:
    • El bus esta libre cuando SDA y SCL están en estado lógico alto.
    • En estado bus libre, cualquier dispositivo puede ocupar el bus I²C como maestro.
    • El maestro comienza la comunicación enviando un patrón llamado "start condition". Esto alerta a los dispositivos esclavos, poniéndolos a la espera de una transacción.
    • El maestro se dirige al dispositivo con el que quiere hablar, enviando un byte que contiene los siete bits (A7-A1) que componen la dirección del dispositivo esclavo con el que se quiere comunicar, y el octavo bit (A0) de menor peso se corresponde con la operación deseada (L/E), lectura=1 (recibir del esclavo) y escritura=0 (enviar al esclavo).
    • La dirección enviada es comparada por cada esclavo del bus con su propia dirección, si ambas coinciden, el esclavo se considera direccionado como esclavo-transmisor o esclavo-receptor dependiendo del bit R/W.
    • El esclavo responde enviando un bit de ACK que le indica al dispositivo maestro que el esclavo reconoce la solicitud y está en condiciones de comunicarse.
    • Seguidamente comienza el intercambio de información entre los dispositivos.
    • El maestro envía la dirección del registro interno del dispositivo que se desea leer o escribir.
    • El esclavo responde con otro bit de ACK
    • Ahora el maestro puede empezar a leer o escribir bytes de datos. Todos los bytes de datos deben constar de 8 bits, el número máximo de bytes que pueden ser enviados en una transmisión no está restringido, siendo el esclavo quien fija esta cantidad de acuerdo a sus características.
    • Cada byte leido/escrito por el maestro debe ser obligatoriamente reconocido por un bit de ACK por el dispositivo maestro/esclavo.
    • Se repiten los 2 pasos anteriores hasta finalizar la comunicación entre maestro y esclavo.
    • Aun cuando el maestro siempre controla el estado de la línea del reloj, un esclavo de baja velocidad o que deba detener la transferencia de datos mientras efectúa otra función, puede forzar la línea SCL a nivel bajo. Esto hace que el maestro entre en un estado de espera, durante el cual, no transmite información esperando a que el esclavo esté listo para continuar la transferencia en el punto donde había sido detenida.
    • Cuando la comunicación finaliza, el maestro transmite una "stop condition" para dejar libre el bus.
    • Después de la "stop condition", es obligatorio para el bus estar idle durante unos microsegundos.
     Muy por encima, esto es el bus I²C.

    Si se desea ampliar información:


    Librería Wire

    Muy bien, ¿y ahora como aplico y uso esto con Arduino?

    El IDE de Arduino viene por defecto con una serie de librerías "preinstaladas". Una de ellas es "Wire". Está será la que nos haga las cosas tremendamente más fáciles a la hora de comunicarnos con el Nunchuk.

    Se puede descargar desde aquí:
    Aunque no hará falta, ya que como dije, viene instalada por defecto.

    En la mayorí­a de las placas Arduino, SDA (lí­nea de datos) está en el pin analógico 4, y SCL (lí­nea de reloj) está en el pin analógico 5. En Arduino Mega, SDA esta en el pin digital 20 y SCL en el 21.

    Funciones de Wire:
    • begin() | begin(dirección): Inicializa la librería Wire y configura el bus I2C como maestro o esclavo.  Normalmente solo será llamada una vez. Si le pasamos una dirección (7 bits), se configura como esclavo. Si no se le pasa ningún parámetro, se configurará como maestro.

      • requestFrom(dirección, cantidad) : Solicita bytes de otro dispositivo. Se le pasarán dos parámetros, la dirección del dispositivo al que solicitar los bytes, y la cantidad de bytes a pedir. 

      • beginTransmission(dirección) : Comienza una transmisión a un dispositivo I2C esclavo con la dirección dada.

      • endTransmission() :  Finaliza una transmisión a un esclavo que fue empezada por beginTransmission() y realmente transmite los bytes que fueron preparados por send(). 

      • send(valor) | send(cadena) | send(datos,cantidad) : Envía datos desde un esclavo en respuesta a una petición de un maestro, o prepara los bytes para transmitir de un maestro a un esclavo. Podemos enviar un valor (byte), una cadena (char *) o un vector de datos (byte *) indicando el número de bytes.

      • byte available() : Devuelve el numero de bytes disponibles para recuperar con receive(). Debería ser llamada por un maestro después de llamar a requestFrom() o por un esclavo dentro de la función a ejecutar por onReceive().

      • byte receive() : Recupera un byte que fue transmitido desde un dispositivo esclavo a un maestro después de una llamada a requestFrom o que fue transmitido de un maestro a un esclavo. 

      • onReceive(manejador) : Registra una función que será lllamada cuando un dispositivo esclavo reciba una transmisión desde un maestro. void miManejador(int numBytes)
      • onRequest(manejador) : Registra una función que será llamada por el dispositivo esclavo cuando un maestro solicite datos. void miManejador()

      Ejemplos:

      Envío de datos por un maestro:

      #include <Wire.h>

      void setup()
      {
        Wire.begin(); //
      Se une con el bus I2C
      }

      byte x = 0;

      void loop()
      {
        Wire.beginTransmission(4); // Transmite al dispositivo #4
        Wire.send("x es ");        // Envía 5 bytes
        Wire.send(x);              //
      Envía 1 byte
        Wire.endTransmission();    // Detiene la transmisión

        x++;
        delay(500);
      }



      Recepción de datos por un maestro:

      #include <Wire.h>

      void setup()
      {
        Wire.begin();        //
      Se une con el bus I2C
        Serial.begin(9600); 
      // Inicia Serial para la salida
      }

      void loop()
      {
        Wire.requestFrom(2, 6);    // Solicita 6 bytes del dispositivo esclavo #2

        while(Wire.available())    // Esclavo puede enviar menos de lo solicitado

        {
          char c = Wire.receive();
      // Recibe un byte como carácter
          Serial.print(c);         // Imprime el carácter
        }

        delay(500);
      }



      Envío de datos por un esclavo:

      #include <Wire.h>

      void setup()
      {
        Wire.begin(2);          // Une el bus I2C con la dirección #2
        Wire.onRequest(evento); // Registra el evento
      }

      void loop()
      {
        delay(100);
      }


      // Esta función se ejecuta cuando el maestro pide información
      void evento()
      {
        Wire.send("holaa "); // Envía un mensaje de 6 bytes esperado por el maestro
      }



      Recepción de datos por un esclavo:

      #include <Wire.h>

      void setup()
      {
        Wire.begin(4);                //
      Une el bus I2C con la dirección #4
        Wire.onReceive(evento);       //
      Registra el evento
        Serial.begin(9600);           // Inicia Serial para la salida
      }

      void loop()
      {
        delay(100);
      }

      //
      Esta función se ejecuta cuando se recibe información del maestro
      void evento(int cantidad)
      {
        while(1 < Wire.available())
        {
          char c = Wire.receive(); // Recibe un byte como carácter
          Serial.print(c);         // Imprime el carácter
        }
        int x = Wire.receive();    //
      Recibe un byte como entero
        Serial.println(x);         //
      Imprime el entero
      }



      Referencias: 

      edit post