Introduction: Remote Control Pleo With Wii Nunchuck
Step 1: Get the Parts
- 2x XBee RF modems. I used XBee2, one with chip antenna and one with rpsma connector (the last one also requires duck antenna typically sold separately)
- 1x 3.3V Arduino Pro board that will decode nunchuck signals and turn them into Pleo commands. Other types of Arduino board will work just fine as long as they are 3.3V. The 5V version will require some additional tweaking to make it wrok with 3.3V
- 1x XBee breakout board is not necessary but will greatly simplify your life
- 1x Altoids box or similar project case
- 1x Molex connector housing to connect XBee to Pleo. I would actually recommend getting 2-3 of those. You can also solder wires to Pleo directly, but that's tricky. You can get it here: http://www.mouser.com/Search/ProductDetail.aspx?qs=hSmm4fxMIuNmj4UzNb1XZA%3d%3d
- At least 4x 1.25mm sockets For the Pleo connector. In fact 20-30 of those as they are very easy to ruin while assembling the connector. To clarify this: you will need to insert these sockets into the housing to get the complete connector. Here are the sockets:
- 1x switch to turn the Arduino on/off. Anything of suitable size from RadioShack is good
- 1x 9V battery
- 1x 9V battery connector (get it from RAdioShack or make from old 9V battery)
- 1x RP-SMA antenna for the XBee (if you chose to get RPSMA version)
- 2x low power LEDs for mode indicators
- 2x XBee headers
- Some straight F headers and angle M headers will make your life easier
- Wires, solder, soldering iron, heat shrink and other standard project stuff
- 1x XBee Explorer USB is required, 2x are recommended for programming and debugging your XBees
- A 1Gb SD card. Don't try to buy a large card, really old small card will work better here.
Step 2: Get Access to Pleo's Serial Interface and Connect the Transceivers
Pleo’s serial interface is a part of 7-pin connector hidden under a plastic cover next to the power switch. Taking out this cover is the most difficult operation in the entire process since the cover is very well glued to the surroundings. It took me about 15 minutes of intensive operations with knife to take it out. You can use Dremel or something similar to do this operation faster, but then you will loose the cover. If you accurately cut it out with a knife or cardboard cutter you can always glue it back so Pleo will be as new.
The connector opened under the cover has 7 pins but you need just 4 of them. If you have a crimping tool you can use it, otherwise just solder the wires to the terminals and insert into the housing at positions 1, 3, 4 and 5 as shown on the picture.
Cut the XBee breakout board in half, solder one of the 10-pin XBess sockets to one half and solder the molex connector to the half-board. After you insert the chip antenna Xbee into this adapter your Pleo side of the receiver is ready. Observe the orientation of XBee when you're inserting it into the socket. Use the other breakout board for reference - they have all pins marked.
The resulting connector is shown on the picture.
Please pay attention to the fact that Pleo is very RF-noisy. That means you want to keep the wires in the connector you just built short and well shielded. Wrap the wires with an aluminum foil (get some from a candy, not shown on the picture) without shortcutting the contacts.
XBee on Arduino’s side is connected to 6-pin serial connector available on the left side of Arduino. Here is the mapping:
Pin GND on Arduino’s 6-pin connector goes to pin 10 on XBee side
Pin 3.3V on Arduino’s side goes to pin 1 on XBee side
Pin RX-I on Arduino’s side goes to pin 2 on XBee side
Pin TX-I on Arduino’s side goes to pin 3 on XBee side
If you’re looking at XBee from top (where antenna is mounted) pin #1 is the one in the top-left corner.
Step 3: Pair the XBees
Now let’s configure XBees. The tricky part here is that Pleo’s serial port uses 115200 bps baud rate while Arduino can’t do more then 19200 (Actually 9600 is much safer). Insert each XBee connecter into its XBee USB adapter, connect the USB adapters to to your computer and run two instances of program called X-CTU. Use this user manual to configure the XBees. If you find it confusing (I did!) just do the following:
- On the “PC Settings” page select 9600 8N1 No Flow control for both XBees
- Select appropriate serial ports to connect the two X-CTU programs to DIFFERENT XBees. Test Hit/Query button and make sure both programs can communicate with the XBees
- Go to Modem configuration page in both programs and hit “Read” button
- Check “Always update firmware” checkboxes
- Make the following modifications to the XBee with RP-SMA connector (we will be using it as coordinator):
- In function Set drop down select ZNET 2.5 COORDINATOR AT
- Change “ID – Pan ID” to something random
- Change “NI – Node identifier” to something meaningfully random (like “Coordinator123″)
- Change “SE – Source Endpoint” to something meaningfully random (like “A1″)
- Change “DE – DestinationEndpoint” to the same value as SE
- Change “DH – Destination Address High” to router’s “SH – Serial Number High” value
- Change “DL – Destination Address Low” to router’s “SL – Serial Number Low” value
- Make the follwoing modifications to the XBee with built-in antenna (we will be calling it router):
- In function Set drop down select ZNET 2.5 ROUTER/END DEVICE AT
- Change “ID – Pan ID” to the same value as coordinator’s ID
- Change “NI – Node identifier” to something meaningfully random (like “Node1234″)
- Change “SE – Source Endpoint” to coordinator’s SE
- Change “DE – DestinationEndpoint” to coordinator’s DE (which should be the same as SE, right?)
- Change “BD – Baud Rate” to “7-115200″
- Hit “Write” buttons in each X-CTU’s instance
- In the router’s X-CTU change the Baud rate in PC Settings to 115200, hit Test/Query to verify that X-CTU is capable fo communicating with the XBee using the new baud rate
- Test everything. Go to terminal page in each X-CTU and type something in the terminal window. Whatever you type in one window should appear in another and vice verse.
- Pin 1 on Pleo’s 7-pin connector goes to pin 10 on XBee
- Pin 3 on Pleo’s side goes to pin 3 on XBee side
- Pin 2 on Pleo’s side goes to pin 2 on XBee side
- Pin 1 on Pleo’s side goes to pin 1 on XBee side
Step 4: Assemble the Control Unit
Wii nunchuck is using a proprietary connector I'm still unable to find part number for. So you can’t get it in the store and here are a few options you have to attach nunchuck to Arduino:
- Cut the nunchuck wire, expose the wires and solder them to Arduino directly. This way you won’t be able to use your nunchuck with Wii again. A bit better solution is not soldering to Arduino, but to some connector. This way you can use the opposite genter connectors for connecting the nunchuck either to Arduino or to the rest of the wire with the connector.
- Use a “Wiichuck”. This method badly works for this project’s enclosure, but you can design a better case instead of Altoids box.
- Use an old ISA board, floppy drive connector or similar type of old-fasioned boards to make your own connector (like I did in thisproject). Just cut out a piece of PCB that has 3 connectors on one side and 2-3 connectors on the other, check if it matches the contacts in nunchuck’s connector and solder wires to it. Optionally you can fix the wires with a glue gun.
- Surpisingly FireWire (IEEE1394) connector fits the contacts. For instance you can insert regular firewire cable into wiimote. Some work is required to make itperfect fit too.
- Possibly the best solution is buying an extension cable for wiimote and getting the connector from it. You can get one for about $6 with free shipping.
Now turn the nunchuck connector so the side with 3 connectors faces up (see the picture) and connect the wires to Arduino as the following:
- Contact 1 on Nunchuck’s side goes to pin 3 on Arduino
- Contact 3 on Nunchuck’s side goes to pin 4 on Arduino
- Contact 4 on Nunchuck’s side goes to pin 5 on Arduino
- Contact 5 on Nunchuck’s side goes to pin 2 on Arduino
- Yes, contact #2 on Nunchuck’s side hangs free
The last (and optional) connections you need to make are from Arduino to LED’s. I used two leds: red and green to indicate the current state of the system. Green LED is lit when arduino is power on. Red LED islit when the system is waiting for connection with Pleo to be established. When the system is ready only the green LED should be on and red LED should be blinking every 2 seconds indicationg that data from nunchuck is being read. So get your two 3V LEDs, find their polarity and connect as described below:
- Connect negative leads from both LEDs to GND pin on Arduino
- Connect green LED positive lead to pin 13
- Red LED’s positive lead goes to pin 12
Finally get a 9V battery and connect it to BATT connector on the Arduino board thru a switch. Done! Your final project box’s content might look like shown on the picture or better.
Step 5: Load the Software
Extract the contents of this archive to the empty SD card (old SD card smaller than 2Gb is preferrable) and insert it into Pleo's SD slot. Don't turn Pleo on yet.
You also need to load the software to Arduino. For that you will need to download program called Arduino from here: http://arduino.cc/en/Main/Software. Install and run the Arduino program and run it. In the Tool->Board and Tools->Port menus select the type of Arduino and serial port it is connected to. Now copypaste this code into the Arduino sketch window and write it to the Arduino memory (File->Upload to I/O Board).
#include
#undef int
#include
#define BAUD_RATE 9600 // 19200 for programming, 115200 for working with Pleo, 9600 for transmission via xbee
#define LED_PIN 13 // the pin the LED is attached to
#define GROUND_PIN PC2
#define POWER_PIN PC3
#define POWER_ON_LED_PIN 13
#define RECEIVE_LED_PIN 12
#define DELTA 20 // the joystick position tolerance
#define ACTION "motion play roar" // playes on Z anc C buttons pressed
#define J_TOP_LEFT "motion play com_walk_fl_v1"
#define J_TOP "motion play com_walk_fs_v1"
#define J_TOP_RIGHT "motion play com_walk_fr_v1"
#define J_LEFT "motion play hun_sniff_l" //???
#define J_RIGHT "motion play hun_sniff_r" //???
#define J_BOTTOM_LEFT "motion play com_walk_bl_v1"
#define J_BOTTOM "motion play hun_exit" //???
#define J_BOTTOM_RIGHT "motion play com_walk_br_v1"
#define CMD_DISABLE_LOG "log disable all"
#define MIN_X 316
#define MAX_X 730
#define MIN_Y 380
#define MAX_Y 630
uint8_t buf[6]; // global buffer for storing nunchuck data, bad, bad ...
// setup everything
void setup()
{
pinMode( POWER_ON_LED_PIN, OUTPUT );
pinMode( RECEIVE_LED_PIN, OUTPUT );
digitalWrite( POWER_ON_LED_PIN, HIGH );
delay( 100 ); // wait for things to stabilize
Serial.begin( BAUD_RATE ); // configure serial output
wait_for_pleo(); // wait for Pleo to boot up
Wire.begin(); // join i2c bus with address 0x52
power_on( PC3, PC2 ); // poweron the nunchuck
init_nunchuck(); // send the initilization handshake
}
// waits for pleo to boot. It is important that XBee coordinator connected to arduino is turned on first
// then the XBee node connected to Pleo should be turned on.
static void wait_for_pleo()
{
digitalWrite( RECEIVE_LED_PIN, HIGH );
int wait = 1;
int count = 0;
int read = 0;
while( wait )
{
if( Serial.available() )
{
read = Serial.read();
if( read == '>' )
{
wait = 0;
}
}
else
{
delay( 500 );
Serial.print( " " );
}
}
command( CMD_DISABLE_LOG, 500 ); // disable Pleo logging otherwise serial gets trashed and everything stops working in a minute
digitalWrite( RECEIVE_LED_PIN, LOW );
}
void delay_read( int sleep )
{
int read;
while( Serial.available() )
{
read = Serial.read();
}
delay( sleep );
}
// turns on power for the nunchuck
static void power_on( byte pwr, byte gnd )
{
DDRC |= _BV(pwr) | _BV(gnd); // make outputs
PORTC &=~ _BV(gnd);
PORTC |= _BV(pwr);
delay(100); // wait for things to stabilize
}
// initialize nunchuck
void init_nunchuck()
{
Wire.beginTransmission( 0x52 ); // transmit to device 0x52
Wire.send( 0x40 ); // sends memory address
Wire.send( 0x00 ); // sends memory address
Wire.endTransmission(); // stop transmitting
}
// sending zero to nunchuck makes it send its current vital data back
void send_zero()
{
Wire.beginTransmission( 0x52 ); // transmit to device 0x52
Wire.send( 0x00 ); // sends one byte
Wire.endTransmission(); // stop transmitting
}
void loop()
{
digitalWrite( RECEIVE_LED_PIN, LOW );
Serial.print( " " ); // I don't know why without this line the loop is just not working.
int cnt = 0; // number of bytes read from input
Wire.requestFrom( 0x52, 6 ); // request 6 bytes from nunchuck
while( Wire.available() )
{
buf[cnt++] = decrypt( Wire.receive() ); // receive byte as an integer
}
if( cnt >= 5 )
{
process_buffer(); // does the job
}
send_zero(); // send the request for next bytes
digitalWrite( RECEIVE_LED_PIN, HIGH );
}
// positions head according to the joystick position
void position_head( int x, int y )
{
int nh = ( ( x - 320 ) * 13 / 41 ) - 65;
nh = nh < -65 ? -65 : nh;
nh = nh > 65 ? 65 : nh;
int nv = ( ( 380 - y ) * 15 / 25 ) + 75;
nv = nv < -75 ? -75 : nv;
nv = nv > 75 ? 75 : nv;
Serial.print( "joint move 11 " );
Serial.print( nh );
Serial.print( " " );
Serial.print( "joint move 12 " );
Serial.print( nv );
Serial.print( " " );
delay_read( 1000 );
}
// positions tail according to the joystick position
void position_tail( int x, int y )
{
int nh = ( ( x - 320 ) * 18 / 41 ) - 90;
nh = nh < -90 ? -90 : nh;
nh = nh > 90 ? 90 : nh;
int nv = ( ( 380 - y ) * 18 / 25 ) + 90;
nv = nv < -90 ? -90 : nv;
nv = nv > 90 ? 90 : nv;
Serial.print( "joint move 9 " );
Serial.print( nh );
Serial.print( " " );
Serial.print( "joint move 10 " );
Serial.print( nv );
Serial.print( " " );
delay_read( 1000 );
}
void command( char cmd[], int sleep )
{
Serial.print( cmd );
Serial.print( " " );
delay_read( sleep );
}
// parses return from nunchuck (6 bytes)
void process_buffer()
{
int jx = buf[0];
int jy = buf[1];
int ax = buf[2] << 2 | ( (buf[5] >> 2 ) & 0x03 );
int ay = buf[3] << 2 | ( (buf[5] >> 4 ) & 0x03 );
int az = buf[4] << 2 | ( (buf[5] >> 6 ) & 0x03 );
int bz = buf[5] & 1;
int bc = ( buf[5] >> 1 )& 1;
if( check( jx, jy, 53, 205 ) )
{
command( J_TOP_LEFT, 2000 );
}
else if( check( jx, jy, 123, 226 ) )
{
command( J_TOP, 2000 );
}
else if( check( jx, jy, 202, 202 ) )
{
command( J_TOP_RIGHT, 2000 );
}
else if( check( jx, jy, 25, 127 ) )
{
command( J_LEFT, 2000 );
}
else if( check( jx, jy, 128, 132 ) )
{
if( ( bz == 1 ) && ( bc == 1 ) )
{
position_head( ax, ay );
}
else if( ( bz == 0 ) && ( bc == 1 ) )
{
position_tail( ax, ay );
}
else if( ( bz == 0 ) && ( bc == 0 ) )
{
command( ACTION, 4000 );
}
}
else if( check( jx, jy, 226, 130 ) )
{
command( J_RIGHT, 2000 );
}
else if( check( jx, jy, 54, 59 ) )
{
command( J_BOTTOM_LEFT, 2000 );
}
else if( check( jx, jy, 126, 30 ) )
{
command( J_BOTTOM, 2000 );
}
else if( check( jx, jy, 200, 57 ) )
{
command( J_BOTTOM_RIGHT, 2000 );
}
else
{
//print_data( jx, jy, ax, ay,az, bz, bc ); // for debug purposes only leave it here
}
}
// checks if the joystick position is in the given position with DELTA tolerance
int check( int sx, int sy, int tx, int ty ) {
return( abs( sx - tx ) < DELTA ) && ( abs( sy - ty ) < DELTA );
}
// debug output of nunchuck's data
void print_data( int jx, int jy, int ax, int ay, int az, int bz, int bc )
{
Serial.print( jx, DEC );
Serial.print( " " );
Serial.print( jy, DEC );
Serial.print( " " );
Serial.print( ax, DEC );
Serial.print( " " );
Serial.print( ay, DEC );
Serial.print( " " );
Serial.print( az, DEC );
Serial.print( " " );
Serial.print( bc, DEC );
Serial.print( " " );
Serial.print( bz, DEC );
Serial.print( " " );
Serial.print( " " );
}
// decrypts nunchuck data, famous "formula", I didn't find why it is working
char decrypt( char x )
{
return( x ^ 0x17 ) + 0x17;
}
Note bunch of delays in the program. Pleo doesn't provide feedback that the command is completed. Also once the command is issued you can't abort it. And, as I mentioned before, Pleo's servos are really slow to avoid overheating. All this means that there is a delay between the nunchuck move and Pleo move.
You can mod this code to work with other robots by rewriting position_head and position_tail methods to translate to commands specific to your robot.
That's all, folks , you've got your very own remote-controlled Pleo. Enjoy!