Wii nunchuck no arduino

Este produto está disponível nos modelos com fio e sem fio. Ambos acompanham adaptador wiichuck.

O Wii nunchuck é um dispositivo I2C que responde no endereço 0x52 e possui um joystick, dois botões e um acelerômetro cujas posições você pode “ler” usando rotinas adequadas no arduino.  Apenas quatro fios são necessários: VCC, GND, SCL e SDA.

Teoricamente ele é um dispositivo de 3V, mas a prática mostrou que você pode alimentá-lo com 5V no arduino sem problemas.

Existem dois tipos de nunchuck no mercado: o original (ou “OEM”) e o genérico. Embora os dois funcionem no Wii aparentemente sem diferença, quando tentamos usar no Arduino três diferenças são percebidas:

  • O genérico tem um acelerômetro menos preciso;
  • A calibração do joystick varia bastante entre genéricos;
  • O genérico pode ou não suportar criptografia na comunicação I2C.

A terceira diferença é a mais importante, pois o nunchuck que não suporta criptografia precisa ser usado de maneira diferente. O Wii se encarrega de fazer isso de forma transparente, mas uma grande quantidade de exemplos de uso do nunchuck no arduino que você encontra na internet prevê o uso de um original e por isso pode não funcionar com muitos genéricos. É preciso  procurar por exemplos que tenham sido feitos para genéricos.

Este sketch de teste funciona com originais e genéricos, usando ou não o adaptador Wiichuck:

/*
* NunchuckPrint
*
* 2007 Tod E. Kurt, http://todbot.com/blog/
*
* The Wii Nunchuck reading code is taken from Windmeadow Labs
*   http://www.windmeadow.com/node/42
*/

#include <Wire.h>

void setup()
{
Serial.begin(19200);
nunchuck_setpowerpins(); // use analog pins 2&3 as fake gnd & pwr
nunchuck_init(); // send the initilization handshake
Serial.print ("Finished setup\n");
}

void loop()
{
nunchuck_get_data();
nunchuck_print_data();
delay(100);
}

//
// Nunchuck functions
//

static uint8_t nunchuck_buf[6];   // array to store nunchuck data,

// Uses port C (analog in) pins as power & ground for Nunchuck
static void nunchuck_setpowerpins()
{
#define pwrpin PORTC3
#define gndpin PORTC2
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |=  _BV(pwrpin);
delay(100);  // wait for things to stabilize
}

// initialize the I2C system, join the I2C bus,
// and tell the nunchuck we're talking to it
void nunchuck_init()
{
Wire.begin();                    // join i2c bus as master
Wire.beginTransmission(0x52);    // transmit to device 0x52

//Só funciona com o original
//  Wire.send(0x40);        // sends memory address
//  Wire.send(0x00);        // sends sent a zero.

//Funciona com a versão xing-ling
#if (ARDUINO >= 100)
Wire.write(0xF0);
Wire.write(0x55);
#else
Wire.send(0xF0);
Wire.send(0x55);
#endif
Wire.endTransmission();
delay(1);

Wire.beginTransmission(0x52);
#if (ARDUINO >= 100)
Wire.write(0xFB);
Wire.write(0x00);
#else
Wire.send(0xFB);
Wire.send(0x00);
#endif
Wire.endTransmission();

}

// Send a request for data to the nunchuck
// was "send_zero()"
void nunchuck_send_request()
{
Wire.beginTransmission(0x52);    // transmit to device 0x52
#if (ARDUINO >= 100)
Wire.write(0x00);
#else
Wire.send(0x00);
#endif
Wire.endTransmission();    // stop transmitting
}

// Receive data back from the nunchuck,
int nunchuck_get_data()
{
int cnt=0;
Wire.requestFrom (0x52, 6);    // request data from nunchuck
while (Wire.available ()) {
// receive byte as an integer

#if (ARDUINO >= 100)
nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.read());
#else
nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.receive());
#endif

cnt++;
}
nunchuck_send_request();  // send request for next data payload
// If we recieved the 6 bytes, then go print them
if (cnt >= 5) {
return 1;   // success
}
return 0; //failure
}

// Print the input data we have recieved
// accel data is 10 bits long
// so we read 8 bits, then we have to add
// on the last 2 bits.  That is why I
// multiply them by 2 * 2
void nunchuck_print_data()
{
static int i=0;
int joy_x_axis = nunchuck_buf[0];
int joy_y_axis = nunchuck_buf[1];
int accel_x_axis = nunchuck_buf[2]; // * 2 * 2;
int accel_y_axis = nunchuck_buf[3]; // * 2 * 2;
int accel_z_axis = nunchuck_buf[4]; // * 2 * 2;

int z_button = 0;
int c_button = 0;

// byte nunchuck_buf[5] contains bits for z and c buttons
// it also contains the least significant bits for the accelerometer data
// so we have to check each bit of byte outbuf[5]
if ((nunchuck_buf[5] >> 0) & 1)
z_button = 1;
if ((nunchuck_buf[5] >> 1) & 1)
c_button = 1;

if ((nunchuck_buf[5] >> 2) & 1)
accel_x_axis += 2;
if ((nunchuck_buf[5] >> 3) & 1)
accel_x_axis += 1;

if ((nunchuck_buf[5] >> 4) & 1)
accel_y_axis += 2;
if ((nunchuck_buf[5] >> 5) & 1)
accel_y_axis += 1;

if ((nunchuck_buf[5] >> 6) & 1)
accel_z_axis += 2;
if ((nunchuck_buf[5] >> 7) & 1)
accel_z_axis += 1;

// Numera sequencialmente as linhas do log
//  Serial.print(i,DEC);
//  Serial.print("\t");

Serial.print(" joystick: ");
Serial.print(joy_x_axis,DEC);
Serial.print(",");
Serial.print(joy_y_axis, DEC);
Serial.print("  \t");

Serial.print("\t acc x:");
Serial.print(accel_x_axis, DEC);
Serial.print("\t\t y:");
Serial.print(accel_y_axis, DEC);
Serial.print("\t\t z:");
Serial.print(accel_z_axis, DEC);
Serial.print("\t");

Serial.print(" but:");
Serial.print(z_button, DEC);
Serial.print(",");
Serial.print(c_button, DEC);

Serial.print("\r\n");  // newline
i++;
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char nunchuk_decode_byte (char x)
{
// x = (x ^ 0x17) + 0x17;
return x;
}

As diferenças são pequenas e estão em dois pontos do código: inicialização e leitura.

Inicialização

  • No original, é preciso escrever 0x00 no endereço 0x40;
  • No genérico que não suporta criptografia, é preciso escrever 0x00 no endereço 0xFB e 0x55 no endereço 0xF0.

Original

Wire.beginTransmission(0x52);// transmit to device 0x52
Wire.write(0x40);// sends memory address
Wire.write(0x00);// sends sent a zero.
Wire.endTransmission();// stop transmitting

Genérico

Wire.beginTransmission(0x52);      // endereço do nunchuck
Wire.write(0xF0);                    // registro de incialização 1
Wire.write(0x55);                  //dado
Wire.endTransmission();        //faz a transmissão
delay(1);
Wire.beginTransmission(0x52);
Wire.write(0xFB);                   // registro de incialização 2
Wire.write(0x00);                   //dado
Wire.endTransmission();             //faz a transmissão

Procure a função que faz a decodificação (geralmente procurar por “0x17” o levará direto a ela

uint8_t _nunchuk_decode_byte (uint8_t x)
{
x = (x ^ 0x17) + 0x17;
return x;
}

e desative a decodificação

uint8_t _nunchuk_decode_byte (uint8_t x)
{
//x = (x ^ 0x17) + 0x17;
return x;
}

Se você não desativar a decodificação o nunchuck parecerá funcionar, mas você encontrará dois problemas:

  • A leitura do acelerômetro ficará bagunçada;
  • O comportamento do botão Z ficará errado.

O adaptador wiichuck

A finalidade do adaptador wiichuck é meramente evitar que você precise cortar o conector original do nunchuck. Isso acelera bastante os testes com diferentes modelos de nunchuck (inclusive o que você conseguir emprestado), sem precisar danificá-lo.

Cuidado: conecte o nunchuck ao adaptador com o chanfro para cima, como mostrado na foto. Conectá-lo ao contrário vai inverter positivo e negativo da alimentação possivelmente destruindo o seu nunchuck.  Eu estou inclinado a acreditar que o nunchuck tenha proteção contra inversão de polaridade, porque é muito raro encontrar um alerta sobre isso. Porém nem todo nunchuck é igual (principalmente os genéricos) então é melhor prestar atenção à polaridade.

O wiichuck não funciona diretamente no arduino Mega. Para usá-lo no mega, faça as seguintes conexões com jumpers entre o wiichuck e o mega:

1: GND
2: 5V
3: SDA (pino 20)
4: SCL (pino 21)

Segurando o adaptador com a palavra “wiichuck” de pé, o pino 1 é o da esquerda.

Mas mesmo no Arduino UNO/Duemilanove o wiichuck depende um “truque” de programação para funcionar. Esse truque transforma as entradas analógicas A2 e A3 em pinos de alimentação. O código que faz isso se parece com este:

static void nunchuck_setpowerpins()
{
#define pwrpin PORTC3 //entrada A3
#define gndpin PORTC2 //entrada A2
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |=  _BV(pwrpin);
delay(100);  // wait for things to stabilize
}

Se você não estiver usando esse recurso, desative-o, ou não conseguirá usar as entradas A2 e A3 para outras coisas.

Links Úteis

Wiibrew – Explica a questão da criptografia, endereçamento, registros, etc.

Tags: ,

  1. Jardim’s avatar

    fala mano, blz?
    cara… to com um nunchuck da Mutilaser, e to com problemas para configura-lo…
    tipo, depois de uma semana, perguntei no Lab de Garagem e me mandaram seu link… testei (sem modificações) e deu o erro:
    sketch_jun04a.ino:10:9: error: #include expects “FILENAME” or
    sketch_jun04a.ino: In function ‘void nunchuck_init()’:
    sketch_jun04a:48: error: ‘Wire’ was not declared in this scope
    sketch_jun04a.ino: In function ‘void nunchuck_send_request()’:
    sketch_jun04a:82: error: ‘Wire’ was not declared in this scope
    sketch_jun04a.ino: In function ‘int nunchuck_get_data()’:
    sketch_jun04a:95: error: ‘Wire’ was not declared in this scope

    tentei fazer algumas adaptações que você colocou aqui, mas continua esse erro…
    o que pode ser?
    (Arduino UNO Rev 2, não sei se influência em algo…)

    Responder

  2. Jefferson’s avatar

    Isso é um problema aqui na formatação automática do blog. A linha “#include ” do sketch foi corrompida. Tente de novo e veja se funciona.

    Responder

  3. Jardim’s avatar

    Obrigado, já consegui upar o código…
    mas, ainda não foi cara
    por acaso o código grande já está pronto pro Nunchuck Genérico? testei ele e não foi…
    e, como eu sei se eu queimei o meu controle?

    Responder

    1. Jefferson’s avatar

      Se você grava sem erros, então é isso aí mesmo. E o único jeito que eu conheço de ter certeza de que o nunchuck estar funcionando é testar no Wii.

      Responder

    2. Jardim’s avatar

      ah!!
      acabo de lembrar, ele tem 2GND’s!!!
      tento com os dois e as variáveis permanecem constantes….

      Responder

      1. Jefferson’s avatar

        Eu já testei com oito genéricos. Antes de eu entender onde estavam os problemas, nenhum funcionava direito, quando funcionava. Mas todos funcionaram com esse sketch.

        Claro, eu não testei ESSE (não copiei e colei do site para meu PC), mas se está compilando sem erros…

        Responder

      2. Jardim’s avatar

        a cara… esse controle é mais do que uma bagunça…
        fui abrir ele… a Mutilaser tacou cola quente nos cabos para colar (tem a solda na placa… mas, pqp… poderiam fazer algo bem melhor) quando você olha, sem contar a cor, você não identifica qual cabo é qual… :/
        se quiser, eu mando uma foto…

        acho que a solução é juntar uma grana para comprar o Nunchuck OEM, e tirar a solda de todos os componentes desse Nunchuck para tentar usar de maneira independente…
        obrigadão mano!
        já tinha achado uns 3 modelos diferentes… (só um deles que eu não testei, o do site do Arduino… não entendi essas “atualizações feitas…) ambas não deram certo… bem, vou providenciar esse controle…
        flw!

        Responder

Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *