Use a Wii-Nunchuk as an NXT Sensor!
The Wii Nunchuk is designed to work together with the Wii Remote on the Wii console. Contoured to fit perfectly in your hand, the Nunchuk adds extra buttons, an analogue joystick, and additional motion-sensing control to expand the game play possibilities. It is also an I2C type device which makes interfacing to the Lego Mindstorms NXT easy. You can pick-up a Wii Nunchuk from eBay for under $10, and a pack of 5x NXT Compatible Male Plug from Mindsensors for $5.95. In essence a new NXT Sensor/Controller for under $20.00.
I produced the original NXT-Nunchuk a couple of years ago and have posted details in other forums, but I never my own till now to post it on my blog.
And it Uses the I2C 2-Wire Data Protocol …………….
Bill of Materials Required:
1x Nunchuk
2x resistors 33k
1x diode 1N4148
Lets Begin the Construction Process:
Open the Nunchuk case with a small screw-driver (1,5mm) as shown in the Video.

You should now have the Wii Nunchuk’s Circuit Board as pictured above.
Now de-solder the 1,8k pull-up-resistors R1 and R2. No fear of SMD. SMDs sticks to the soldering iron!

Now solder the new pullup resistors. The Lego reference design suggests 82kohm pullup resistors for I2C I found out, that 33kohm is the best value for stable connection. I changed the SMD resistors, but if you have no SMS resistors use common 1/4W size and solder the resistors on the connection cables. Additionally I mounted a drop-voltage-diode to reduce the voltage of the nunchuk.

NXT Zamor Bot with the Modified Wii Nunchuck Controller
Now cut the original Nunchuk cable connector of and strip the white insulation back by about 10mm to expose the conductors. Carefully spread them out in the correct order matching the circuit diagram order. Now using an original Lego Mindstorms NXT Black Connector Cable as a reference, slide the ordered coloured wires into the NXT Compatible Male Plug.
Carefully place the NXT Compatible Male Plug on a solid surface and take a small “Blade” Screw Driver and individually push the 6 Brass pins in the plug home. Next test that the new plug fits easily into the NXT’s Sensor Sockets.
To finish the plug and make it secure. Get some 5 Minute Araldite and fill the back of the plug where the cable enters. Leave to dry for an hour, before carefully reassembling your Wii Nunchuk
Video of My NXT-Nunchuk Controlled Zammor Bot in Action!
View RobotC Nunchuk Library
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include "NXCDefs.h" byte CMDNunchukInit[] = {0x03, 0xA4, 0x40, 0x00}; byte CMDNunchukWrite[] = {0x02, 0xA4, 0x00}; byte CMDNunchukRead[] = {0x01, 0xA4}; byte outbuf[6]; int outbuf1[6]; //ubyte workaround for ROBOTC // This function allows conversion of an unsigned byte to a signed int // This is a workaround for ROBOTC int ubyteToInt(ubyte _byte) { int _ret = 0; _ret = (_byte & 0x80) ? (_byte & 0x7F) + 0x80 : _byte; return _ret; } // port is the NXT port, i2c_cmd is the nunchuk internal address register // at i2c_cmd=0...5 are 6 Byte of sensor values // at i2c_cmd=0x20 ... 0x2F you get 16 Byte of calibration data sub NunchukGetParamter(tSensors port, byte i2c_cmd) { // Initialize the Nunchuk SensorType[port] = sensorI2CCustom; //Ask for 0 bytes sendI2CMsg(port, CMDNunchukInit[0], 0); while (nI2CStatus[port] == STAT_COMM_PENDING) ; // ROBOTC Wait for I2C bus to be ready //Write at 0x00: 6 Bytes of sensor values CMDNunchukWrite[2] =i2c_cmd; //Write Nunchuk register address sendI2CMsg(port, CMDNunchukWrite[0], 0); while (nI2CStatus[port] == STAT_COMM_PENDING) ; // ROBOTC Wait for I2C bus to be ready byte count = 6; //Ask for count bytes sendI2CMsg(port, CMDNunchukRead[0], count); while (nI2CStatus[port] == STAT_COMM_PENDING) ; // Wait for I2C bus to be ready if(nI2CBytesReady[port] == count){ //Read data from buffer readI2CReply(port, outbuf[0], 6); for(int i=0; i<count> </count> |
RobotC Example Source Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include "Nunchuk-lib.c" #include "NXTServo-lib.c" task main() { int pos; nI2CBytesReady[kSc8Port] = 0; SensorType[kSc8Port] = sensorI2CCustom9V; NXTServo_SetPosition(1, 2500); NXTServo_SetPosition(2, 0); NXTServo_SetPosition(3, 0); while (true) { NunchukGetParamter(S1, 0x00); //see comment for i2c_cmd if (sizeof(outbuf)==6) { char Nunchuk[16]; strcpy(Nunchuk, "aXaYgXgYgZCZ"); if ((outbuf1[5]&0x01)!=0) {FireCannon()} //Show Z-Button if ((outbuf1[5]&0x02)!=0) {Nunchuk[10]=99;} //Show C-Button eraseDisplay(); for(int i=0; i<6; i++ ) { nxtDisplayStringAt(15*i,50,"%c", Nunchuk[2*i]); nxtDisplayStringAt(15*i+5,50,"%c", Nunchuk[2*i+1]); nxtDisplayStringAt(15*i,40,"%02X", outbuf1[i]); } // analog stick nxtDrawCircle((outbuf1[0]-0x1E)/2, (outbuf1[1]-0x1E)/3, 3); // acceleration value nxtDrawCircle((outbuf1[2]-0x46), (outbuf1[3]-0x46)/2, 6); } wait1Msec(100); } } void FireCannon() { if ((outbuf1[5]&0x01)!=0) NXTServo_SetPosition(1, 500); wait1Msec(100); NXTServo_SetPosition(1, 2500); wait1Msec(100); NXTServo_SetPosition(1, 0); wait1Msec(100); } |
JAVA Source Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
import lejos.nxt.I2CPort; import lejos.nxt.I2CSensor; /** * http://trandi.wordpress.com */ public class WiiNunchuck extends I2CSensor { private static final int NUNCHUCK_ADDR = 0x52; // 7bit addressing. It would be 0xA4 in 8bit private static final int NUNCHUCK_MEM_ADDR = 0x40; // array to store RAW nunchuck data private byte[] _buff = new byte[6]; private byte[] _initBuff = new byte[2]; private int joy_x_axis; private int joy_y_axis; private int accel_x_axis; private int accel_y_axis; private int accel_z_axis; private boolean z_button = false; private boolean c_button = false; public WiiNunchuck(I2CPort port){ super(port); setAddress(NUNCHUCK_ADDR); } public boolean updateData() { // ??? somehow it gets disabled.... getPort().i2cEnable(I2CPort.LEGO_MODE); // INITIALIZE - tell the nunchuck we're talking to it _initBuff[0] = 0x00; sendData(NUNCHUCK_MEM_ADDR, _initBuff, 1); try{ Thread.sleep(10);}catch(InterruptedException e){} // send a request for data sendData(0x00, (byte)0x00); if(getData(0x00, _buff, 6) != 0) return false; try{ Thread.sleep(10);}catch(InterruptedException e){} for(int i = 0; i < 6; i++) _buff[i] = nunchuk_decode_byte(_buff[i]); // transform into something meaningful joy_x_axis = _buff[0]; joy_y_axis = _buff[1]; accel_x_axis = _buff[2]; accel_y_axis = _buff[3]; accel_z_axis = _buff[4]; // byte nunchuck_buf[5] contains bits for z and c buttons z_button = (_buff[5] & 0x01) == 0; c_button = (_buff[5] & 0x02) == 0; // it also contains the least significant bits for the accelerometer data so we have to check each bit of byte outbuf[5] if ((_buff[5] & 0x03) > 0) accel_x_axis += 2; if ((_buff[5] & 0x04) > 0) accel_x_axis += 1; if ((_buff[5] & 0x05) > 0) accel_y_axis += 2; if ((_buff[5] & 0x06) > 0) accel_y_axis += 1; if ((_buff[5] & 0x07) > 0) accel_z_axis += 2; if ((_buff[5] & 0x08) > 0) accel_z_axis += 1; joy_x_axis = updateRange(joy_x_axis); joy_y_axis = updateRange(joy_y_axis); accel_x_axis = updateRange(accel_x_axis); accel_y_axis = updateRange(accel_y_axis); accel_z_axis = updateRange(accel_z_axis); return true; } /** * Transforms a 0..127 -128 .. 0 range into a 128..0..-128 one */ private static int updateRange(int init){ int result = init; boolean negative = result < 0; if(negative) result = -result; result = - result + 128; if(negative) result = -result; return result; } // Encode data to format that most wiimote drivers except only needed if you use one of the regular wiimote drivers private static byte nunchuk_decode_byte (byte x){ return (byte)((x ^ 0x17) + 0x17); } public int getAccelX() { return accel_x_axis; } public int getAccelY() { return accel_y_axis; } public int getAccelZ() { return accel_z_axis; } public int getJoyX() { return joy_x_axis; } public int getJoyY() { return joy_y_axis; } public boolean isC_button() { return c_button; } public boolean isZ_button() { return z_button; } } |
NXC Source Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include "NXCDefs.h" byte CMDNunchukInit[] = {0xA4, 0x40, 0x00}; byte CMDNunchukWrite[] = {0xA4, 0x00}; byte CMDNunchukRead[] = {0xA4}; byte nbytes; byte outbuf[]; // port is the NXT port // i2c_cmd is the nunchuk internal address register // at i2c_cmd=0...5 are 6 Byte of sensor values // at i2c_cmd=0x20 ... 0x2F you get 16 Byte of calibration data sub NunchukGetParamter(byte port, byte i2c_cmd) { // Initialize the Nunchuk SetSensorLowspeed(port); LowspeedWrite(port, 0, CMDNunchukInit); //ask for 0 bytes while(LowspeedStatus(port, nbytes)>0); // Wait(1) is needless, the NXT command interpreter delays next i2c access 3ms CMDNunchukWrite[1] =i2c_cmd; //at 0x00: 6 Bytes of sensor values LowspeedWrite(port, 0, CMDNunchukWrite); // write Nunchuk register address while(LowspeedStatus(port, nbytes)>0); byte count = 6; LowspeedWrite(port, count, CMDNunchukRead); //ask for count bytes while(LowspeedStatus(port, nbytes)>0); if(nbytes == count){ LowspeedRead(port, count, outbuf) //read data from buffer for(int i=0; i< count) |
Mindstorms NXT FlexiBot - Test #1
This Lego Mindstorms NXT FlexiPicker is based on the ABB IRB 360 FlexPicker. The IRB 360 FlexPicker™, is the SECOND GENERATION delta robot for precision pick and place applications. The ... Read more
Mindstorms NXT FlexiPicker - Test #2
This Lego Mindstorms NXT FlexiPicker is based on the ABB IRB 360 FlexPicker. The IRB 360 FlexPicker™, is the SECOND GENERATION delta robot for precision pick and place applications. The ... Read more
Leave a Reply