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.
C3PO in Action
Control - Wireless Remote
Control - Arduino Mega Controller
Control - Arduino Uno R3 Controller
Control - Arduino WAV Shield
Control - Audio Tracks
Control - Pololu Maestro Servo Controller
Control - Pololu RC Switch With Relay
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.
Electrical Assembly - Electrical Component Mounting
Electrical Assembly - Electrical Component Mounting
Electrical Assembly - Electrical Component Mounting
Electrical Wiring
Elbow Actuators
Head Rotation Servo
Armature
Armature
Armature
C3PO Components
Resin Head
Eyes
Ears
Ears
Resin Feet
Shorts
Arms
Decorative Item Attachment
Cylinders
Round Trim Ring
Upper Arm Attachment
Hands
Hands
Torso
Ribbing
Paint
Paint
Base
Base Construction Detail
Base Construction Detail
Base Lighting
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())
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();
}
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.