Introduction: C3PO Droid Construction

The C3PO droid is a display piece I made that stands on a lighted base. The droid is controlled by two Arduino
micro-controllers and a Pololu Maestro USB servo controller. I hired a voice impersonator to read a script I created so that when I press one of twelve remote control push buttons, he will say a specific phrase. The C3PO I created has fading eyes, head turn, and both arms move. I have created a complete construction document along with a electrical drawing package that you can download and review in the event you would like to make one of these for yourself. The documents should help you get started . The construction document is 70 pages and includes the code listing for both Arduino controllers and the Pololu servo controller. Information regarding making the base, the armature, electrical panel, and procuring the parts required is included in the document.

Step 1: C3PO in Action

Step 2: Control - Wireless Remote

Step 3: Control - Arduino Mega Controller

Step 4: Control - Arduino Uno R3 Controller

Step 5: Control - Arduino WAV Shield

Step 6: Control - Audio Tracks

Step 7: Control - Pololu Maestro Servo Controller

Step 8: Control - Pololu RC Switch With Relay

Step 9: Electrical Assembly - Component List

The electrical components are mounted on two white steel sub-panels inside the torso. The materials used for the electrical assembly are listed here.

Step 10: Electrical Assembly - Electrical Component Mounting

Step 11: Electrical Assembly - Electrical Component Mounting

Step 12: Electrical Assembly - Electrical Component Mounting

Step 13: Electrical Wiring

Step 14: Elbow Actuators

Step 15: Head Rotation Servo

Step 16: Armature

Step 17: Armature

Step 18: Armature

Step 19: C3PO Components

Step 20: Resin Head

Step 21: Eyes

Step 22: Ears

Step 23: Ears

Step 24: Resin Feet

Step 25: Shorts

Step 26: Arms

Step 27: Decorative Item Attachment

Step 28: Cylinders

Step 29: Round Trim Ring

Step 30: Upper Arm Attachment

Step 31: Hands

Step 32: Hands

Step 33: Torso

Step 34: Ribbing

Step 35: Paint

Step 36: Paint

Step 37: Base

Step 38: Base Construction Detail

Step 39: Base Construction Detail

Step 40: Base Lighting

Step 41: Program Listing - Arduino Mega 2560

The Arduino Mega 2560 reads the remote relay triggers and sends a 4 digit binary code to the Arduino Uno that represents the desired audio file to be played. The Arduino Mega 2560 also sends a subroutine number to the Maestro servo controller that represents the motion that should be played based on the remote trigger. Refer to the electrical drawings for hookup information.

//John Guarnero

//September 2014

//serial to Maestro config

#include

#define txPin 14

#define rxPin 15

#define sub0 0

SoftwareSerial mySerial(rxPin, txPin);

//Audio and Remote Setup

int Push1 = 0;

int Push2 = 0;

int Push3 = 0;

int Push4 = 0;

int Push5 = 0;

int Push6 = 0;

int Push7 = 0;

int Push8 = 0;

int Push9 = 0;

int Push10 = 0;

int Push11 = 0;

int Push12 = 0;

void setup()

{

//serial to Maestro config

mySerial.begin(9600);

delay(1000);

//define pins to use for binary code to Arduino Uno

pinMode(22, OUTPUT);

pinMode(24, OUTPUT);

pinMode(28, OUTPUT);

pinMode(30, OUTPUT);

//Make Binary Code = 0

digitalWrite (22, LOW); // 1 2 4 8

digitalWrite (24, LOW); // 1 2 4 8

digitalWrite (28, LOW); // 1 2 4 8

digitalWrite (30, LOW); // 1 2 4 8

//Define all remote trigger pins as inputs with internal pullup resistor

pinMode(31, INPUT_PULLUP); //Relay 1

digitalWrite (31, HIGH); //Enable Pullup

pinMode(33, INPUT_PULLUP); //Relay 2

digitalWrite (33, HIGH); //Enable Pullup

pinMode(35, INPUT_PULLUP); //Relay 3

digitalWrite (35, HIGH); //Enable Pullup

pinMode(37, INPUT_PULLUP); //Relay 4

digitalWrite (37, HIGH); //Enable Pullup

pinMode(39, INPUT_PULLUP); //Relay 5

digitalWrite (39, HIGH); //Enable Pullup

pinMode(41, INPUT_PULLUP); //Relay 6

digitalWrite (41, HIGH); //Enable Pullup

pinMode(43, INPUT_PULLUP); //Relay 7

digitalWrite (33, HIGH); //Enable Pullup

pinMode(45, INPUT_PULLUP); //Relay 8

digitalWrite (45, HIGH); //Enable Pullup

pinMode(47, INPUT_PULLUP); //Relay 9

digitalWrite (47, HIGH); //Enable Pullup

pinMode(49, INPUT_PULLUP); //Relay 10

digitalWrite (49, HIGH); //Enable Pullup

pinMode(51, INPUT_PULLUP); //Relay 11

digitalWrite (51, HIGH); //Enable Pullup

pinMode(53, INPUT_PULLUP); //Relay 12

digitalWrite (51, HIGH); //Enable Pullup

}

void loop()

{

//read the remote trigger inputs

Push1 = digitalRead(31);

Push2 = digitalRead(33);

Push3 = digitalRead(35);

Push4 = digitalRead(37);

Push5 = digitalRead(39);

Push6 = digitalRead(41);

Push7 = digitalRead(43);

Push8 = digitalRead(45);

Push9 = digitalRead(47);

Push10 = digitalRead(49);

Push11 = digitalRead(51);

Push12 = digitalRead(53);

//If show 1 triggered, initate sequence

if (Push1 == 0)

{

//Serial.println("Happy Birthday Emily");

if (mySerial.available())

Serial.write(mySerial.read());

if (Serial.available())

mySerial.write(Serial.read());

mySerial.write(0xA7); //run maestro subroutine

mySerial.write((byte)0x00); //device id

//Send Value of Binary 1 To Uno to Play WAV File 1

digitalWrite (22, HIGH); // 1 2 4 8

digitalWrite (24, LOW); // 1 2 4 8

digitalWrite (28, LOW); // 1 2 4 8

digitalWrite (30, LOW); // 1 2 4 8

delay (500);

digitalWrite (22, LOW); // 1 2 4 8

digitalWrite (24, LOW); // 1 2 4 8

digitalWrite (28, LOW); // 1 2 4 8

digitalWrite (30, LOW); // 1 2 4 8

}

//If show 2 triggered, initate sequence

if (Push2 == 0)

{

//Serial.println("Happy Birthday Kim");

if (mySerial.available())

Serial.write(mySerial.read());

if (Serial.available())

mySerial.write(Serial.read());

mySerial.write(0xA7); //run subroutine

mySerial.write((byte)0x01); //device id

//Send Value of Binary 2 To Uno to Play WAV File 2

digitalWrite (22, LOW); // 1 2 4 8

digitalWrite (24, HIGH); // 1 2 4 8

digitalWrite (28, LOW); // 1 2 4 8

digitalWrite (30, LOW); // 1 2 4 8

delay (500);

digitalWrite (22, LOW); // 1 2 4 8

digitalWrite (24, LOW); // 1 2 4 8

digitalWrite (28, LOW); // 1 2 4 8

digitalWrite (30, LOW); // 1 2 4 8

}

//If show 3 triggered, initate sequence

if (Push3 == 0)

{

//Serial.println("Happy Birthday Megan");

if (mySerial.available())

Serial.write(mySerial.read());

if (Serial.available())

mySerial.write(Serial.read());

mySerial.write(0xA7); //run subroutine

mySerial.write((byte)0x02); //device id

//Send Value of Binary 3 To Uno to Play WAV File 3

digitalWrite (22, HIGH); // 1 2 4 8

digitalWrite (24, HIGH); // 1 2 4 8

digitalWrite (28, LOW); // 1 2 4 8

digitalWrite (30, LOW); // 1 2 4 8

delay (500);

digitalWrite (22, LOW); // 1 2 4 8

digitalWrite (24, LOW); // 1 2 4 8

digitalWrite (28, LOW); // 1 2 4 8

digitalWrite (30, LOW); // 1 2 4 8

}

//If show 4 triggered, initate sequence

if (Push4 == 0)

{

//Serial.println("Guarnero House");

if (mySerial.available())

Serial.write(mySerial.read());

if (Serial.available())

Step 42: Arduino Uno R3

The Arduino Uno R3 reads a 4 digit binary value from the Arduino Mega 2560 and plays an audio file associated with the binary value. This is the voice of the C3PO droid. Refer to the electrical drawings for hookup information.

//John Guarnero

//September 2014

#include

#include

#include

#include

int inp6 = 0;

int inp7 = 0;

int inp8 = 0;

int inp9 = 0;

int TC_Value = 8;

int Last_TC = 0;

int Demonstration_Value = 0;

int Last_Demonstration_Value = 1;

SdReader card; // This object holds the information for the card

FatVolume vol; // This holds the information for the partition on the card

FatReader root; // This holds the information for the filesystem on the card

FatReader f; // This holds the information for the file we're play

WaveHC wave; // This is the only wave (audio) object, since we will only play one at a time

uint8_t dirLevel; // indent level for file/dir names

dir_t dirBuf; // buffer for directory reads

/*

* Define macro to put error messages in flash memory

*/

#define error(msg) error_P(PSTR(msg))

// Function definitions (we define them here, but the code is below)

void play(FatReader &dir);

// SETUP

void setup() {

Serial.begin(9600); // set up Serial library at 9600 bps for debugging

putstring_nl("\nWave test!"); // say we woke up!

putstring("Free RAM: "); // This can help with debugging, running out of RAM is bad

Serial.println(FreeRam());

// if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you

if (!card.init()) { //play with 8 MHz spi (default faster!)

error("Card init. failed!"); // Something went wrong, lets print out why

}

// enable optimize read - some cards may timeout. Disable if you're having problems

card.partialBlockRead(true);

// Now we will look for a FAT partition!

uint8_t part;

for (part = 0; part < 5; part++) { // we have up to 5 slots to look in

if (vol.init(card, part))

break; // we found one, so get out

}

if (part == 5) { // if we ended up not finding one :(

error("No valid FAT partition!"); // Say what went wrong

}

// Lets tell the user about what happened

putstring("Using partition ");

Serial.print(part, DEC);

putstring(", type is FAT");

Serial.println(vol.fatType(), DEC); // FAT16 or FAT32?

// Try to open the root directory

if (!root.openRoot(vol)) {

error("Can't open root dir!"); // Something went wrong,

}

//

putstring_nl("Files found (* = fragmented):");

// Print out all of the files in all the directories.

root.ls(LS_R | LS_FLAG_FRAGMENTED);

}

// HELPERS

/*

* print error message and halt

*/

void error_P(const char *str) {

PgmPrint("Error: ");

SerialPrint_P(str);

sdErrorCheck();

while(1);

}

void sdErrorCheck(void) {

if (!card.errorCode()) return;

PgmPrint("\r\nSD I/O error: ");

Serial.print(card.errorCode(), HEX);

PgmPrint(", ");

Serial.println(card.errorData(), HEX);

while(1);

}

//Start of Loop

void loop()

{

byte i;

inp6 = digitalRead(6); // Binary 1

inp7 = digitalRead(7); // Binary 2

inp8 = digitalRead(8); // Binary 4

inp9 = digitalRead(9); // Binary 8

if (inp6 == HIGH && inp7 == LOW && inp8 == LOW && inp9 == LOW)// 1 2 4 8

{ //Happy Birthday Emily

Serial.println("1");

TC_Value = 1;

playcomplete("1.WAV");

delay (1);

}

if (inp6 == LOW && inp7 == HIGH && inp8 == LOW && inp9 == LOW)// 1 2 4 8

{//Happy Birthday Kim

Serial.println("2");

TC_Value = 2;

playcomplete("2.WAV");

delay (1);

}

if (inp6 == HIGH && inp7 == HIGH && inp8 == LOW && inp9 == LOW)// 1 2 4 8

{//Happy Birthday Megan

Serial.println("3");

TC_Value = 3;

playcomplete("3.WAV");

delay (1);

}

if (inp6 == LOW && inp7 == LOW && inp8 == HIGH && inp9 == LOW)// 1 2 4 8

{//Welcome to the Guarnero House

Serial.println("4");

TC_Value = 4;

playcomplete("4.WAV");

delay (1);

}

if (inp6 == HIGH && inp7 == LOW && inp8 == HIGH && inp9 == LOW)// 1 2 4 8

{//Happy Halloween

Serial.println("5");

TC_Value = 5;

playcomplete("5.WAV");

delay (1);

}

if (inp6 == LOW && inp7 == HIGH && inp8 == HIGH && inp9 == LOW)// 1 2 4 8

{//Happy Anniversary

Serial.println("6");

TC_Value = 6;

playcomplete("6.WAV");

delay (1);

}

if (inp6 == HIGH && inp7 == HIGH && inp8 == HIGH && inp9 == LOW)// 1 2 4 8

{//Say Some Jokes

Serial.println("7");

TC_Value = 7;

playcomplete("7.WAV");

delay (1);

}

if (inp6 == LOW && inp7 == LOW && inp8 == LOW && inp9 == HIGH)// 1 2 4 8

{//Kim Saint dialog & Kim R2D2 dialog

Serial.println("8");

TC_Value = 8;

playcomplete("8.WAV");

delay (1);

}

if (inp6 == HIGH && inp7 == LOW && inp8 == LOW && inp9 == HIGH)// 1 2 4 8

{//Happy Thanksgiving

Serial.println("9");

TC_Value = 9;

playcomplete("9.WAV");

delay (1);

}

if (inp6 == LOW && inp7 == HIGH && inp8 == LOW && inp9 == HIGH)// 1 2 4 8

{//Kims Buns dialog

Serial.println("10");

TC_Value = 10;

playcomplete("10.WAV");

delay (1);

}

if (inp6 == HIGH && inp7 == HIGH && inp8 == LOW && inp9 == HIGH)// 1 2 4 8

{//Master John & Lost In Space

Serial.println("11");

TC_Value = 11;

playcomplete("11.WAV");

delay (1);

}

if (inp6 == LOW && inp7 == LOW && inp8 == HIGH && inp9 == HIGH)// 1 2 4 8

{//Merry Christmas

Serial.println("12");

TC_Value = 12;

playcomplete("12.WAV");

delay (1);

}

Last_TC = TC_Value;

//Serial.println(Last_TC);

//

}

// End of loop

// Plays a full file from beginning to end with no pause.

void playcomplete(char *name) {

// call our helper to find and play this name

playfile(name);

while (wave.isplaying) {

// do nothing while audio playing

}

// Audio is done playing

}

void playfile(char *name) {

// see if the wave object is currently doing something

if (wave.isplaying) {// audio already playing so stop it

wave.stop(); // stop it

}

// look in the root directory and open the file

if (!f.open(root, name)) {

putstring("Couldn't open file "); Serial.print(name); return;

}

// OK read the file and turn it into a wave object

if (!wave.create(f)) {

putstring_nl("Not a valid WAV"); return;

}

// ok time to play! start playback

wave.play();

}

Step 43: Pololu Maestro

The Pololu Maestro controls the head, left elbow, and right elbow servos. The Pololu Maestro also controls the eye leds. To allow the eyes to fade up and down, a servo is attached to the Maestro with the motor removed and the leds are connected to where the motor leads of the servo went. The feedback pot was left in the servo circuit so when you give a servo command, the leds will brighten due to feedback error and more voltage being sent to the motor since the servo will try to get the feedback to match the command. The head servo would hum from time to time when the servo was stopped, this is due to some mechanical imbalance that I could not correct with the current setup. Therefore, I added a Pololu servo controlled relay that removes power from the servo when the head move is complete. The relay is just connected in series via a normally contact to the head servo power. This setup saves the servo from excessive wear and eliminates all noise when there is no motion. This has worked out great since I can move the head without the servo fighting me when I want to wipe things down for cleaning. Refer to the electrical drawings for hookup information.

Enchanted Objects

Participated in the
Enchanted Objects