Introduction: Killer Candy Robot 3000
I am an 11-year-old boy who loves to make stuff with electronics and programming. This year for Halloween, I decided to make a robot costume.
This robot costume that took me about 4 weeks to make, most of the time was soldering and programming/testing the microcontroller code. This costume has 4 microcontrollers inside (2 in the head, and 2 in the body). In the head, an Arduino Nano is controlling the voice-activated LEDs, and an mbed Nucleo is controlling the eyes which are made from two MAX7219s. I programmed both the nano and the mbed using C++. In the body, an Arduino Nano is controlling the 14 LEDs that randomly blink (two on each side of the body, and 12 in an array on the front to look like computer read-outs from old movies), and a LinkIt One from Mediatek Labs is using a Grove shield to control the LEDs and LED Bar next to the candy drawer, the servo that opens and closes the candy drawer, the touch-sensitive button for controlling the servo, and it plays sound from a speaker on the side of the body. I had to wear special gloves that are used for texting on touch screen phones in order to be able to activate the candy drawer using the touch-sensitive button. The eyes look left, right, and forward, and they blink. The arms are made from aluminum heating ducts as well as plastic paint buckets with the bottoms cut out and painted silver. The legs are made from aluminum heating ducts and two cardboard boxes. The head and body are made from three cardboard boxes painted silver. The mouth of the robot is covered with a nylon so that I can see through it, yet it looks black (when I am not talking) I wore this costume to my local Halloween festival and came in second place, and then I wore it during Trick-or-Treating without the legs (because it was impossible to go up and down stairs wearing them)
Here is the full list of materials I used to create this costume:
1 14x10x8 box for head
1 14x20x18 box for body (top)
1 14x20x10 box for body (bottom)
1 custom sized box for candy drawer (used scraps from other boxes to make)
2 8x14x4 boxes for feet (these were boxed from Samsung Series 7 Slates, very heavy cardboard)
21 8mm Red LEDs
1 8mm Green LED
1 Nylon Stocking (for mouth)
14 Images for "stickers" printed
1 baseball cap with the brim removed
1 aluminum air duct 4"x25' (cut into two parts for arms) - had some left over
1 aluminum air duct 8"x50' (cut into two parts for legs) - had some left over
2 Paint buckets (small from Lowes) with the bottoms removed
1 LinkIt One from Mediatek (SeeedStudio)
1 Grove Sheidl for LinkIt One from Mediatek
2 custom made Grove Connectors for the LED near the candy drawer
1 Grove LED bar for LinkIt One
1 Grove Servo for the candy drawer
1 Grove touch-sensitive control
1 pair texting gloves
2 Arduino Nanos
1 mbed NucleoF411RE
1 light switch and metal cover
4 lithium ion batteries
1 SD card for storing sounds
1 rechargeable speaker with audio cable plugged into LinkIt One
many wires soldered and heat-shrunk together
The programming for the mbed was done using their online IDE/compiler.
The programming for the Arduinos was done using the Arduino IDE.
The LinkIt One used the Grove libraries for LinkIt One as well as SD card and sound libriaries.
Step 1: Making the Head
I started by making the head, I measured and cut out the eye slots (for the MAX7219s) and the mouth (which is actually where I looked out of the head). I used hot glue to attach a baseball cap (with the brim removed) in the center of the top so that I could wear the head.
I programmed the sound-activated LEDs for the Arduino Nano:
int sensorPin = 4;
int sensorValue = 0; int LED_1 = 2; int LED_2 = 3; int LED_3 = 4; int LED_4 = 5; int LED_5 = 6; int LED_6 = 7;
void setup() { pinMode(LED_1, OUTPUT); pinMode(LED_2, OUTPUT); pinMode(LED_3, OUTPUT); pinMode(LED_4, OUTPUT); pinMode(LED_5, OUTPUT); pinMode(LED_6, OUTPUT); Serial.begin(9600); }
void loop() { // read the value from the sensor: sensorValue = analogRead(sensorPin); Serial.println(sensorValue); if (sensorValue<100){ //The 'silence' sensor value is 509-511 digitalWrite(LED_1,HIGH); digitalWrite(LED_2,HIGH); digitalWrite(LED_3,HIGH); digitalWrite(LED_4,HIGH); digitalWrite(LED_5,HIGH); digitalWrite(LED_6,HIGH); delay(5); // The red LEDs stay on for 2 seconds } else { digitalWrite(LED_1,LOW); digitalWrite(LED_2,LOW); digitalWrite(LED_3,LOW); digitalWrite(LED_4,LOW); digitalWrite(LED_5,LOW); digitalWrite(LED_6,LOW); } }
Then I programmed the eyes for the mbed::
#include "mbed.h"
#include #include /* printf, scanf, puts, NULL */ #include /* srand, rand */ #include /* time */ using std::string; // p5: DIN, p7: CLK, p8: LOAD/CS SPI max72_spi(SPI_MOSI, NC, SPI_SCK); DigitalOut load(D5); Serial pc(SERIAL_TX, SERIAL_RX); InterruptIn mybutton(USER_BUTTON); int maxInUse = 2; //change this variable to set how many MAX7219's you'll use
int lastMode = -1; int currMode = 0;
// define max7219 registers #define max7219_reg_noop 0x00 #define max7219_reg_digit0 0x01 #define max7219_reg_digit1 0x02 #define max7219_reg_digit2 0x03 #define max7219_reg_digit3 0x04 #define max7219_reg_digit4 0x05 #define max7219_reg_digit5 0x06 #define max7219_reg_digit6 0x07 #define max7219_reg_digit7 0x08 #define max7219_reg_decodeMode 0x09 #define max7219_reg_intensity 0x0a #define max7219_reg_scanLimit 0x0b #define max7219_reg_shutdown 0x0c #define max7219_reg_displayTest 0x0f
#define LOW 0 #define HIGH 1 #define MHZ 1000000
void maxSingle( int reg, int col) { load = LOW; // begin max72_spi.write(reg); // specify register max72_spi.write(col); // put data load = HIGH; // make sure data is loaded (on rising edge of LOAD/CS) }
void maxAll (int reg, int col) { // initialize all MAX7219's in the system load = LOW; // begin for ( int c=1; c<= maxInUse; c++) { max72_spi.write(reg); // specify register max72_spi.write(col); // put data } load = HIGH; }
void maxOne(int maxNr, int reg, int col) { int c = 0; load = LOW;
for ( c = maxInUse; c > maxNr; c--) { max72_spi.write(0); // no-op max72_spi.write(0); // no-op }
max72_spi.write(reg); // specify register max72_spi.write(col); // put data
for ( c=maxNr-1; c >= 1; c--) { max72_spi.write(0); // no-op max72_spi.write(0); // no-op } load = HIGH; }
void setup () { // initiation of the max 7219 // SPI setup: 8 bits, mode 0 max72_spi.format(8, 0); // going by the datasheet, min clk is 100ns so theoretically 10MHz should work... // max72_spi.frequency(10*MHZ); maxAll(max7219_reg_scanLimit, 0x07); maxAll(max7219_reg_decodeMode, 0x00); // using an led matrix (not digits) maxAll(max7219_reg_shutdown, 0x01); // not in shutdown mode maxAll(max7219_reg_displayTest, 0x00); // no display test for (int e=1; e<=8; e++) { // empty registers, turn all LEDs off maxAll(e,0); } maxAll(max7219_reg_intensity, 0x0f & 0x0f); // the first 0x0f is the value you can set // range: 0x00 to 0x0f }
int getBitValue(int bit) { pc.printf("bit = %d\n\r", bit); switch(bit) { case 0: return 1; case 1: return 2; case 2: return 4; case 3: return 8; case 4: return 16; case 5: return 32; case 6: return 64; case 7: return 128; } return 0; } void OpenEyes() { maxAll(7,60); maxAll(6,126); maxAll(5,102); maxAll(4,102); maxAll(3,126); maxAll(2,60); } void Blink() { maxAll(3,0); maxAll(4,0); maxAll(5,0); maxAll(6,0); maxOne(1, 7, 0); maxOne(2, 2, 0); } void LookAhead() { maxAll(4,102); maxAll(5,102); } void LookLeft() { maxOne(1,4,78); maxOne(1,5,78); maxOne(2,4,114); maxOne(2,5,114); } void LookRight() { maxOne(2,4,78); maxOne(2,5,78); maxOne(1,4,114); maxOne(1,5,114); } int looky = 0; int looking = 10; int lookMode = 0; int main() { srand (time(NULL)); setup (); OpenEyes();
while(true) { if(looky > looking) { looky = 0; switch(lookMode) { case 0: currMode = 1; break; case 1: currMode = 0; break; case 2: currMode = 1; break; case 3: currMode = 2; break; case 4: currMode = 1; break; case 5: currMode = 0; break; case 6: currMode = 1; break; case 7: currMode = 3; break; case 8: // ahead; currMode = 1; break; case 9: currMode = 0; break; } lookMode++; if(lookMode > 9) lookMode = 0; if(lastMode != currMode) { lastMode = currMode; switch(currMode) { case 0: // blink Blink(); wait(.25f); OpenEyes(); break; case 1: // look ahead LookAhead(); break; case 2: LookLeft(); break; case 3: LookRight(); break; } } } else looky++; wait(.25f); } }
Step 2: Body Functions
I put two boxes together in order to make a body that was big enough for me to wear, house the electronics,and have a candy drawer. I initially taped it together with duct tape, painted it silver, then added stickers and some metallic tape as well. The Grove Bar and candy drawer indicators (LEDs) had to be taped onto the body using metallic tape so that it looked better. The Grove touch sensor I put on the top of the body so that I could reach it with my finger. The speaker had to be mounted using tape inside the body so that it could play sounds when I opened and closed the drawer.
I used an Arduino Nano to drive 14 LEDs arranged on the body to resemble computer displays seen in old movies my dad and I watch and riff on (like MST3K does).
int demoMode = 0;
void setup() { for(int l = 0; l < 15; l++) { pinMode(l, OUTPUT); } randomSeed(analogRead(0)); }
// the loop routine runs over and over again forever: void loop() { for(int LedIndex = 0; LedIndex < 15; LedIndex++) { if(demoMode ==1 ) { digitalWrite(LedIndex, HIGH); delay(1000); } else { int onOff = random(10); if(onOff % 2 == 0) { // on digitalWrite(LedIndex, HIGH); } else { // off digitalWrite(LedIndex, LOW); } } } delay(1000); }
The LinkIt One provided the majority of the robot's functions inside the body. This took a while to figure everything out, especially how to attach the servo to the candy drawer so that it opened and closed when I presses and released the touch-sensitive control.
Here is the code for the LinkIt One.
#include "Suli.h"
#include #include #include #include "Seeed_LED_Bar_Arduino.h" #include const int ROBOT_START = 1; const int ROBOT_ON = 2; const int ROBOT_OFF = 3; const int TRICK_TREAT = 4; const int THANK_YOU = 5; const int pinTouch = 4; const int pinLed = 8; const int REDLED = 8; const int GREENLED = 7; int lastState = LOW; int barLevel = 1; int maxOpenCount = 5; int openCount = 0; int tray; Servo myservo; int maxTray = 90; int minTray = 10; SeeedLedBar bar(6, 5); // CLK, DTA
void PlaySound(int soundId) { AudioStatus status; switch(soundId) { case ROBOT_START: LAudio.playFile( storageSD,(char*)"RobotStart.mp3"); break; case ROBOT_ON: LAudio.playFile( storageSD,(char*)"RobotOn.mp3"); break; case ROBOT_OFF: LAudio.playFile( storageSD,(char*)"RobotOff.mp3"); break; case OPEN_TRAY: LAudio.playFile( storageSD,(char*)"RobotCandyDrawerOpen.wav"); break; case CLOSE_TRAY: LAudio.playFile( storageSD,(char*)"RobotCandyDrawerClose.wav"); break; } }
void setup() { tray = maxTray; LAudio.begin(); LSD.begin(); // Init SD card bar.begin(6, 5); pinMode(pinTouch, INPUT); pinMode(pinLed, OUTPUT); LAudio.setVolume(3); bar.setLevel(1); myservo.attach(3); myservo.write(tray); pinMode(REDLED, OUTPUT); pinMode(GREENLED, OUTPUT); // PlaySound(ROBOT_START);
}
void OpenTray() { PlaySound(OPEN_TRAY); tray = minTray; myservo.write(tray); digitalWrite(REDLED, LOW); digitalWrite(GREENLED, HIGH); openCount++; if(openCount > maxOpenCount) { openCount = 0; barLevel++; if(barLevel > 10) barLevel = 1; bar.setLevel(barLevel); } } void CloseTray() { PlaySound(CLOSE_TRAY); tray = maxTray; myservo.write(tray); digitalWrite(REDLED, HIGH); digitalWrite(GREENLED, LOW); } void toggleTray() { if(tray == minTray)CloseTray(); else OpenTray(); } void checkButton() { int state = digitalRead(pinTouch); if(state != lastState) { lastState = state; toggleTray(); } } void loop() { checkButton(); }
Step 3: Finishing Touches
I found a bunch of funny and cool images on the Internet and printed them on my dad's laser printer, then attached them all over the robot's body using rubber cement. We got numbers and letters from Lowes as well as the light switch (which provides no functionality other than letting others flip it up and down while I stood in line at houses to get candy).
I am happy with how this project turned out, however, there are some things that would have improved the costume.
1) shoulder padding inside - my arms got a bit numb and sore from wearing this all night
2) additional support in the head - the cap worked well, but the head wobbled about and eventually my microphone for the voice sensor broke off from being rubbed by the neck foam.
3) usable feet - although they looked really cool, I couldn't really wear my legs and feet during Trick-or-Treating because they were clumsy and difficult for me to walk up and down stairs while wearing.
Thanks for looking at my costume.