*Todos los archivos de programas y fuentes aquí indicados los puedes descargar al final de la entrada.
Aspecto final |
A raíz de adquirir un “Wiimote + Nunchuck” (baratos de Ebay) para mi consola de videojuegos Wii y como consecuencia de que el Wiimote comenzó a dar problemas al poco tiempo, decidí experimentar con el Nunchuck sobrante y darle una utilidad mejor que la de de estar en el cajón de los trastos.
Tras un rápido vistazo por la Web, me lleve la agradable sorpresa de que su funcionamiento ya estaba bastante documentado.
Así que un poco de lectura “en diagonal” y manos a la obra.
Resumen de su funcionamiento:
El Nunchuck es un mando que dispone de un joystick analógico de 2 ejes, 2 botones digitales y un preciado acelerómetro de 3 ejes.
Este mando va conectado a su hermano, el Wiimote, a través de un cable por el que circulan los datos de dichos sensores, utilizando un protocolo de comunicación compatible con I2C con un direccionamiento estándar de 7 bits (por experiencia a 100Khz sin errores). Dentro de este protocolo, el Nunchuck funciona como esclavo, con lo que solo se necesitaría implementar la comunicación I2C en modo Maestro, para lo que se utilizó un micro-controlador PIC de Microchip.
Bueno, bonito y si echamos un vistazo en Ebay, su precio es bastante atractivo (incluso más barato que un simple sensor acelerómetro, 3€ a fecha de hoy).
Adaptador |
Personalmente hice las primeras pruebas cortando el cable, pero existen unos pequeños adaptadores para sacar los pines fuera sin modificar el mando.
Pinout |
La tensión de trabajo adecuada es de 3.3v (aunque hay quienes usan 5v y afirman no tener problemas, pero no es muy recomendable).
Las resistencias de pullUp necesarias para mantener las líneas del bus a nivel alto ya las incorpora el mando internamente con lo que no es necesario añadirlas.
La dirección I2C de del mando es 0x52 (0b1010010), por lo que para realizar lecturas direccionaremos a 0xA5 (0b10100101) y para escrituras a 0xA4 (0b10100100).
El procedimiento para obtener los datos es el siguiente:
Paso 1.- Abrir comunicación, Inicializar el mando escribiendo en 0xA4 los bytes 0x40, 0x00, cerrar comunicación (solo necesario una vez mientras se mantenga alimentado).
Paso 2.-Abrir comunicación, Posicionar el puntero de lectura escribiendo en 0xA4 el byte 0x00, cerrar comunicación.
Paso 3.- Abrir comunicación, Leer 6 bytes de 0xA5, cerrar comunicación.
Paso 4.-Una vez obtenidos los 6 bytes deberemos decodificarlos realizándole la operación “XOR” 0x17 y seguidamente SUMARLE 0x17 a cada uno de ellos. Tras este tratamiento, los bytes contendrán la siguiente información (byte 1 es el primer byte recibido).
*Como se puede observar, los valores del joystick son de 8 bits de longitud (0..255), los del acelerómetro de 10 bits (0..1024) y los botones de 1 bit (0..1).
*El acelerómetro puede medir aceleraciones de hasta 2 G. Con lo que si realizamos movimientos “bruscos” obtendremos mínimos menores y máximos mayores que si realizamos el mismo movimiento a “baja velocidad”.
Byte 1: Valor del joystick X.
Byte 2: Valor del joystick Y.
Byte 3: Bits 9 a 2 del valor del acelerómetro X.
Byte 4: Bits 9 a 2 del valor del acelerómetro Y.
Byte 5: Bits 9 a 2 del valor del acelerómetro Z.
Byte 6:
bits 7-6: Bits 1 y 0 del valor del acelerómetro Z.
bits 5-4: Bits 1 y 0 del valor del acelerómetro Y.
bits 3-2: Bits 1 y 0 del valor del acelerómetro X.
bit 1: Botón C (0=accionado, 1=no accionado).
bit 0: Botón Z (0=accionado, 1=no accionado).
Paso 5.-Tratar los datos como queramos y actuar en consecuencia.
Paso 6.-Volver al paso 2 para realizar otra lectura.
Pruebas BusPirate |
Dejo aquí un log de ejemplo de cómo realizar la lectura del I2C utilizando BusPirate.
#
RESET
Bus Pirate v3a
Firmware v5.9 (r539) Bootloader v4.1
DEVID:0x0447 REVID:0x3042 (24FJ64GA002 B4)
http://dangerousprototypes.com
HiZ> m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
x. exit(without change)
(1)> 4
Set speed:
1. ~5KHz
2. ~50KHz
3. ~100KHz
4. ~400KHz
(1)> 2
Ready
I2C> W
Power supplies ON
I2C> (0)
0.Macro menu
1.7bit address search
2.I2C sniffer
I2C> (1)
Searching I2C address space. Found devices at:
0xA4(0x52 W) 0xA5(0x52 R)
I2C> [0xa4 0x40 0x00]
I2C START BIT
WRITE: 0xA4 ACK
WRITE: 0x40 ACK
WRITE: 0x00 ACK
I2C STOP BIT
I2C> [0xa4 0x00][0xa5 r:6]
I2C START BIT
WRITE: 0xA4 ACK
WRITE: 0x00 ACK
I2C STOP BIT
I2C START BIT
WRITE: 0xA5 ACK
READ: 0x7E ACK 0x74 ACK 0xA4 ACK 0x6D ACK 0x6E ACK 0xB3
NACK
I2C STOP BIT
I2C> [0xa4 0x00][0xa5 r:6]
I2C START BIT
WRITE: 0xA4 ACK
WRITE: 0x00 ACK
I2C STOP BIT
I2C START BIT
WRITE: 0xA5 ACK
READ: 0x7E ACK 0x74 ACK 0xA2 ACK 0x6C ACK 0x6E ACK 0xAB
NACK
I2C STOP BIT
I2C> [0xa4 0x00][0xa5 r:6]
I2C START BIT
WRITE: 0xA4 ACK
WRITE: 0x00 ACK
I2C STOP BIT
I2C START BIT
WRITE: 0xA5 ACK
READ: 0x1E ACK 0x86 ACK 0x66 ACK 0x47 ACK 0x80 ACK 0xBB
NACK
I2C STOP BIT
I2C>
Una vez realizadas las pruebas pertinentes, me puse manos a la obra
Pic salida UART |
Pruebas protoboard |
Además de esto, el PIC también acepta que se le envié un único comando consistente en una “R” (mayúscula). Al recibirlo, el PIC se Resetea y saca por la UART Una breve explicación de los datos que, desde ese momento ira “escupiendo” por la UART de forma continua.
El circuito también dispone de un LED rojo conectado a una salida del PIC, el cual, cambia de estado cada vez que se realiza una nueva lectura, con lo que, si el PIC está alimentado y todo esta correcto veremos un ligero parpadeo del mismo.
Resumiendo: del PIC se utilizarán 2 pines para el bus I2C, otros 2 pines para la UART y 1 pin para el LED rojo, así de simple, sin ni siquiera oscilador externo, ni reset, ni mas historias.
Este es el firmware el PIC, está escrito en C# utilizando CCS (Excluyo el archivo estándar de definiciones del PIC “16F628A.h” que está incluido en CCS):
Archivo main.h:
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOBROWNOUT //No Reset when brownout detected
#FUSES NOMCLR //Master Clear pin desactivado
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES RESERVED //Used to set the reserved FUSE bits
#use delay(clock=4000000)
//Asiognacion de I/O. Los Aux son pines no utilizados
#define I2CDTA PIN_A2
#define I2CCLK PIN_A3
#define AUX1 PIN_A4 //(salida open drain)
#define ICSPVPP PIN_A5 //(es solo entrada)
#define AUX2 PIN_B0
#define UART_RX PIN_B1
#define UART_TX PIN_B2
#define AUX3 PIN_B3
#define AUX4 PIN_B4
#define LED PIN_B5
#define ICSPCLK PIN_B6
#define ICSPDTA PIN_B7
#define AUX5 PIN_A6
#define AUX6 PIN_A7
#define AUX7 PIN_A0
#define AUX8 PIN_A1
#use rs232(baud=9600, parity=N,xmit=PIN_B2,rcv=PIN_B1,bits=8)//UART por hadware
#use I2C(master, scl=I2CCLK, sda=I2CDTA,slow)//I2C por software
Archivo main.c:
///////////////////////////////////////////////////////////////////////////////////////////
//NunChuckBlueHack rev.1 para PIC16F628A por villamany 14/11/2010. mail: villamany@msn.com
///////////////////////////////////////////////////////////////////////////////////////////
//Hack para el mando de Wii NunChuck. Su funcion es decodificar via I2C el estado
//de los sensores del mando y enviar estos valores a traves de una UART en formato
//texto y de una forma humanamente entendible finalizados por una suma de verificacion.
//A esta UART se pueden conectar dispositivos conversores tales como:
//UART TTL a rs232, UART TTL a USB, UART TTL a bluetooth SPP...
//Leer el archivo readme`para mas info.
#include "main.h"
////////////////////
//Variables globales
////////////////////
int chksm;//para calculo de cheksum
////////////////////
//Metodos globales auxiliares
////////////////////
// lee un byte del nunchuck, lo decodifica (haciendo xor 0x17 y + 0x17) y lo
// devuelve
int8 decodeByte();
//va sumando el caracter pasado al checksum (var chksm) y lo imprime por la UART
void PrintCheck(char c);
///////////////////
//Interrupciones
///////////////////
// Decodificacion de comandos recibidos por interrupcion UART
#int_RDA
void UART_isr(void){
int8 chr=getc();
switch (chr) {
case 'R':reset_cpu();}}//comando resetear
///////////////////
//Definicion de metodos
///////////////////
int8 decodeByte(){
return((i2c_read()^0x17)+0x17);}
void PrintCheck(char c){
chksm^=c;
putc(c);}
void main(){
delay_ms(500);//estabilizacion y evitar que arranque el programa cuando se use ICSP
// (ICSP trabaja a 5v y hay partes que trabajan a 3v);
//setear I/O todos los no usados como salidas para no dejar entradas flotantes
output_low(AUX1);
output_low(AUX2);
output_low(AUX3);
output_low(AUX4);
output_low(AUX5);
output_low(AUX6);
output_low(AUX7);
output_low(AUX8);
output_low(ICSPCLK);
output_low(ICSPDTA);
//Configurar pic
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
enable_interrupts(INT_RDA);//habilitar interrupcion por recepcion UART
enable_interrupts(GLOBAL);// hablilitar las int. globales
int Jx,Jy,Bz,Bc,aux;//valores joystic analogico, botones, el ultimo byte
int16 x,y,z;//valores acelerometro;
// Imprimir Bienvenida
printf("\r\nReset...");
printf("\r\nNunchuckBlueHack r1 for PIC16F628A by villamany@msn.com.");
printf("\r\nType \"R\" for Reset");
printf("\r\n\r\nHelp:\r\n");
printf("\r\nx=3 Digits Joy X position (000..255).");
printf("\r\ny=3 Digits Joy Y position (000..255).");
printf("\r\nc=1 Digit C button status. 1=Pressed, 0=Depressed.");
printf("\r\nz=1 Digit Z button status. 1=Pressed, 0=Depressed.");
printf("\r\nX=4 Digits X Accelerometer value (0000..1023).");
printf("\r\nX=4 Digits Y Accelerometer value (0000..1023).");
printf("\r\nX=4 Digits Z Accelerometer value (0000..1023).");
printf("\r\n*=3 Digits checkSum value (000..255)(All bytes Xor'ed ");
printf("between first of line and prior to \"*\"");
printf("\r\nFinally a carry return byte (0x0D) is send for return back ");
printf("of line and perform a new sensors read.\r\n\r\n");
// Buccle principal
while (true) {
output_toggle(LED);//monitorizar estado en led
// la inicializacion del nunchuck la quengo que realizar en cada lectura
// de lo contrario falla (no se por que)
i2c_start();i2c_write(0xa4);i2c_write(0x40);i2c_write(0x00);i2c_stop();//inicializacion nunchuck
delay_ms(1); //estos delays de 1ms son necesarios sin ellos falla
i2c_start();i2c_write(0xa4);i2c_write(0x00);i2c_stop();//Posicionar puntero
delay_ms(1);
i2c_start();
i2c_write(0xa5);//Leer los 6 bytes de datos
Jx=decodeByte();//valor joystic X
Jy=decodeByte();//valor joystic Y
x=decodeByte();//valor vcelerometro X (los 8 bytes mas significativos)
y=decodeByte();//valor vcelerometro Y (los 8 bytes mas significativos)
z=decodeByte();//valor vcelerometro Z (los 8 bytes mas significativos)
aux=decodeByte();//Aqui estan los 2 bytes menos significativos del acelorometro y el estado de los botones
i2c_stop();//Datos recibidos
if ((aux & 0x01)==1)bz=0; else bz=1;//extraer estado boton z
if ((aux & 0x02)==0x02)bc=0; else bc=1;//extraer estado boton c
//Comentar estas 6 lineas si se quiere resolucion de 8 bits en el
// en lugar de 10 en el acelerometro
x=x <<2; //Extraer los 2 bytes menos significativos de los ejes del acelerometro
x=x + ((aux >> 2) & 0x03);
y=y <<2;
y=y + ((aux >> 4) & 0x03);
z=z <<2;
z=z + ((aux >> 6) & 0x03);
chksm=0;//Reseteo del checkSum
//imprimir e ir calculando el checkSum
printf(PrintCheck,"x%03uy%03uc%01uz%01uX%04LuY%04LuZ%04Lu",Jx,Jy,bc,bz,x,y,z);//
printf("*%03u\r",chksm);
delay_ms(1);}//Volver al bucle
}
La salida la podremos ver mediante una aplicación de consola conectada al puerto correspondiente, a través de un PC. Si lo prefieres ver gráficamente puedes utilizar una pequeña aplicación para Windows, de la que también se incluyen las fuentes en C# en el archivo de descarga, a final de la entrada.
Módulo bluetooth |
Todo esto está muy bien, pero ahora quería utilizar esos datos en otros dispositivos que carecen de puertos externos como por ejemplo mi PocketPc.
Por fin encontré un motivo para comprar esos módulos UART-bluetooth que tanto tiempo llevo queriendo tener. Comentar que he adquirido 2, para tener uno de reserva, a través de Ebay por algo menos de 15€ ambos.
Estos módulos incorporan varias funciones, entre ellas una que permite configurarlos vía comandos AT, pero por motivos de no complicar el firmware del PIC e ir a lo más fácil, opte por dejarlo tal cual me llegó, esto es clave de emparejamiento “1234”, velocidad 9600, 8 bits de datos, 1 de stop y sin paridad ni control de flujo, bueno lo admito, únicamente modifique el nombre del dispositivo y la clave de acceso, por trastear un poco con él.
Probando conjunto |
Podéis mirar la hoja de datos para saber cómo configurarlo vía UART física.
Este modulo trabaja a 3.3v, asimismo, el PIC acepta esta tensión de trabajo también, con lo que solo habrá que utilizar una única tensión de funcionamiento en todo el circuito: 3.3v.
Entonces tenemos que se van a utilizar únicamente los 2 pines de alimentación, los 2 pines de TX y RX de la UART y una de las salidas que trae, a la que conectare un LED azul. Esta salida la mantiene a nivel alto el módulo mientras este enlazado a otro dispositivo bluetooth, poniéndola a nivel bajo en caso contrario.
El resultado, ha sido un Nunchuck que reporta los datos de sus sensores a través de Bluetooth utilizando el servicio SPP. Como se aprecia en las imágenes, se ha introducido todo dentro del mando y se ha añadido un conector externo para darle alimentación, bien a la red eléctrica mediante fuente de alimentación o bien con baterías.
Consola PDA |
Consola PC |
Cabe destacar que para abrir el mando, se necesita el destornillador habitual de Nintendo de 3 puntas, posteriormente se pueden sustituir los tornillos por otros similares de cabeza estándar.
* 28/09/2012 Actualizado enlace descarga de archivos