Two pals enjoy a romantic candlelit evening.

When the flame sensor attached to Puppet A’s arm detects candlelight, Puppet A will slowly extend their tongue into Puppet B’s mouth, closing a circuit that powers the LEDs in Puppet B’s eyes. Careless Whisper will play/pause when the flame sensor is on/off. Puppet A’s eyes will also light up and their eyebrows will furrow the further their tongue extends.

Reflection:

In this time of social distancing, we lack the ability to reach out and touch one another as we once did, whether these relationships be mundane or intimate is irrelevent as they are all a vital part of our human nature, which up until now we have taken for granted.

I wanted to create an interaction between two puppets/animatronics, but didn’t want to over complicate said interaction. Ideally, I would have liked to include a conversation between the two puppets, however due to the lack of time and the scope of the assignment, I decided to move forward with a non-verbal interaction.

I considered preparing audio for this piece, but as far as I know, the dfmini is unable to play multiple mp3 files at the same time. So if I wanted both puppets to speak at once, I would have to put both voices on the same mp3 file, and manually sync that with the puppets respective mouths. I thought that it might be too much of an undertaking.

After some sketching, I landed on a funny little idea, where by extending one puppets tongue into the others mouth, the eyes of the puppet light up. This would be triggered by candlelight, with some goofy music to set the mood.

Thinking of all the obstacles people go through to create a connection, I thought it might be fun to add some more hurdles for my automaton representation.

I was moving the hand (with the flame sensor) around manually, but I could have had it triggered by some different input, and turn this into some sort of Rube Goldberg machine. For instance, the hand will only move into position after 8pm, the tongue will only extend if the hand is next to a flame, and the eyes will only light up if the puppets are near enough for the tongue to touch the copper tape in Puppet B’s mouth. Or maybe it has to be the third date, and the lighting has to be in a certain range. Just adding more reasons for things to go wrong, just like they do in real life.

It was interesting to hear others interpretations of this in class. It was not my intention to make something uncomfortable to watch, but I understand how it can be. Upon reflection, I found that, with very minor changes, these animatronics could represent much darker themes if I so choose. I think it goes to show the power of the face, and a puppets ability to mimic them. When all is said and done, it’s just some foam glued to some cardboard, but when it’s put together in a certain way, we can attach meaning and emotion to it.

Since I was experimenting with making these puppets, I thought I would play with the shapes a little more, thus the recoiled position of Puppet B. If I had some foresight, I probably would have realized that it might end up looking a little alarming.

Upon further reflection I decided it was unproductive to assign genders to the two. I don’t view either of them in any way, and in my mind, it’s up to the viewer to make those assumptions if they choose. Again, this was meant to be a lighthearted project, but I feel that under a heteronormative assumption, there are issues with the act of giving and the act of receiving, those being roles stereotypically anchored to men and women respectively.

It was a cool project, and I learned a lot. Definitely gained some perspective, specifically that I need to pay more attention to others perspectives.

Electronics:

    • 2 Arduinos (I found it easier to use a second Arduino for audio)
    • Flame sensor
    • Servo
    • Micro Servo
    • 2 red LED, 2 yellow LED
    • Dfmini
    • Speaker
    • Wires, bread boards, resistor

Materials:

    • Chipboard (cereal boxes etc.)
    • Mattress foam
    • Oil pastels
    • plastic cup
    • Lego gears, beams, axles, and pins
    • Hot glue
    • Copper tape
    • Electrical tape
Code:

Movement:

#include <Servo.h>

Servo tongue;
Servo brow;

int flame_sensor = 3;
int angle = 170;
int old_angle;

int brow_angle = 90;


void setup() {
  pinMode(flame_sensor, INPUT);
  tongue.attach(5);
  brow.attach(6);

  brow.write(brow_angle);
  tongue.write(angle);
}

void loop() {
  old_angle = angle;
  
  if (digitalRead(flame_sensor) == HIGH) { 
    if (angle < 170) {
      angle = angle + 2;
    }
  } else {
    if (angle > 10) {
        angle = angle - 2;
      }  
  }
  
  if (old_angle != angle) {
    brow_angle = map(angle, 10, 170, 45, 90);
    tongue.write(angle);
    brow.write(brow_angle);
  }
  delay(50);
}

Audio:

/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
 <https://www.dfrobot.com/index.php?route=product/product&product_id=1121>

 ***************************************************
 This example shows the basic function of library for DFPlayer.

 Created 2016-12-07
 By [Angelo qiao](Angelo.qiao@dfrobot.com)

 GNU Lesser General Public License.
 See <http://www.gnu.org/licenses/> for details.
 All above must be included in any redistribution
 ****************************************************/

/***********Notice and Trouble shooting***************
 1.Connection and Diagram can be found here
 <https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
 2.This code is tested on Arduino Uno, Leonardo, Mega boards.
 ****************************************************/

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

int flame_sensor = 3;

bool song_paused = false;

void setup()
{
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  
  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
  
  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));

  pinMode(flame_sensor, INPUT);
  
  myDFPlayer.volume(30);  //Set volume value. From 0 to 30
  myDFPlayer.play(1);  //Play the first mp3
}

void loop()
{
  static unsigned long timer = millis();
  if (digitalRead(flame_sensor) == LOW) { 
    if (song_paused == true) {
      myDFPlayer.start();
      song_paused = false;
    }
  } else {
    if (song_paused == false) {
      myDFPlayer.pause();
      song_paused = true;
    }
  }

  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
  }
}

void printDetail(uint8_t type, int value){
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

Alarm Clock:

An alarm clock determined to fulfill its purpose.

Servo in the mouth “synced” up with audio (close enough). Neo pixel ring light hidden behind eyes, color changes to red and the brightness increases for each “wake up”. Eyebrows also furrow a bit further with each “wake up”. Snooze can be triggered after each “wake up”, the audio will stop, eyebrows will raise, and the eyes will turn green.

Reflection:

This was a lot of fun to make.

Took awhile to get the mouth to move with the audio in a way that I wanted. The servo had to rotate fully to open the mouth, and I realized that it wasn’t moving fast enough to fully reach a position before the next command was given. So although the open angle and closed angle are the same for each word, the servo doesn’t always have enough time to fully extend. Only the last one where the “wake up” was dragged out does the mouth fully open.  I could have moved the servo so it wouldn’t have to rotate so much to open the mouth, but I thought that it kind of worked out for the best. Also the face kind of shakes when the mouth is fully opened which makes it look a little angrier, happy accident.

I wanted to add a lot more to this, at the moment it’s not really an alarm clock, it turns on with a button and turns off with another button. But I do plan on finishing it in the future.

Ideally, I wanted to add in a real time clock, an lcd to show the current time, some simple buttons to set the alarm, which would also be shown on the lcd, a motion sensor on top to act as a snooze button, and a passive buzzer to make it a little more disruptive to your sleep. I’d also like to give the face some color, and maybe add some thin rings around the eyes.

Also also, there are a few things I’d like to debug about this as it is now. The code for the audio is run on  a second Arduino, and I think it would be for the best if it wasn’t, but because the code for the mouth/eyebrows/eyes uses the delay function, I found it a lot easier to just separate the two. Also concerning sound, every time I’ve used speakers I end up with a weird clicking noise, even when no audio is playing. You can hear it at the end of the video. I’m sure there are simple solutions for these issues, and I look forward to figuring them out.

Build wise, ideally ideally, I would laser cut the body, use a kerf cut (I think it’s called that) to make the round part, probably add some supports, laser cut acrylic for the eyes, and laser cut or 3d print gears for the eyebrows and maybe mouth. The legos gears were useful but a bit limited, I had a small range of gear sizes and the positions they could be placed in. Also, cardboard and scissors are great for prototyping, but there’s no replacing the precision of a laser cutter, and wood also happens to looks nicer. Maybe I’ll try this again now that I have the experience.

Electronics:

    • 2 Arduino (I found it easier to use a second Arduino for audio)
    • Servo
    • Micro Servo
    • Neo pixel ring
    • Dfmini
    • Speaker
    • 2 buttons
    • Wires, bread boards, resistors

Materials:

    • Chipboard (cereal boxes etc.)
    • Mattress foam
    • plastic cup
    • Lego gears, beams, axles, and pins
    • Paper towel
    • Hot glue
    • Copper tape
    • Electrical tape
Code:

Movement:

#include "Arduino.h"
#include <Servo.h>

#include <Adafruit_NeoPixel.h>

#define PIN            9
#define NUMPIXELS      12

int alarm_button = 3;
int snooze_button = 4;

int mouthPin = 7;
Servo mouth;

int openAngle = 70;
int closedAngle = 170;

int browPin = 8;
Servo brow;

int brow_1 = 80;
int brow_2 = 100;
int brow_3 = 130;
int brow_4 = 145;
int brow_5 = 160;

bool wake_up = false;
int act = 1;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

uint32_t color_1 = pixels.Color(210,250,250); //light blue
uint32_t color_2 = pixels.Color(220,150,100); 
uint32_t color_3 = pixels.Color(230,100,25); 
uint32_t color_4 = pixels.Color(250,50,0);    //orange
uint32_t color_5 = pixels.Color(250,0,0);     //red
uint32_t color_6 = pixels.Color(0,250,0);     //green

void setup() {
  Serial.begin(9600);

  pixels.begin(); 
  pixels.setBrightness(0); 
  pixels.show(); 
  
  pinMode(alarm_button, INPUT);
  pinMode(snooze_button, INPUT);
  
  mouth.attach(mouthPin);
  mouth.write(closedAngle);

  brow.attach(browPin);
  brow.write(brow_1);
}

void loop() {

  if (wake_up == true) {

    // first wake-up
    if (act == 1) {
      pixels.setBrightness(70);
      pixels.fill(color_1);
      pixels.show();

      brow.write(brow_1);
      
      delay(790);
      
      //"wake up" at ~.85 seconds
      mouth.write(openAngle);
      delay(270);
      
      //close end word
      mouth.write(closedAngle);
      delay(1850); //1780 was a real sweet spot
    }
    
    if (act == 2) {
      pixels.setBrightness(100);
      pixels.fill(color_2);
      pixels.show();

      brow.write(brow_2);
      
      //"wake up" at ~3.1 seconds
      mouth.write(openAngle);
      delay(270);
     
      //close end word
      mouth.write(closedAngle);
      delay(1460);
    }
    
    if (act == 3) {
      pixels.setBrightness(120);
      pixels.fill(color_3);
      pixels.show();

      brow.write(brow_3);
      
      //"wake up" at ~4.8 seconds
      mouth.write(openAngle);
      delay(230);
      
      //close end word
      mouth.write(closedAngle);
      delay(1400);
    }
    
    if (act == 4) {
      pixels.setBrightness(160);
      pixels.fill(color_4);
      pixels.show();

      brow.write(brow_4);
      
      //"wake" at ~6.4 seconds
      mouth.write(openAngle);
      delay(220);

      //close end word
      mouth.write(closedAngle);
      delay(1400);
    }

    if (act == 5) {
      pixels.setBrightness(180);
      pixels.fill(color_5);
      pixels.show();

      brow.write(brow_5);
      
      //"wake" 
      mouth.write(openAngle);
      delay(310);
      //close between words
      mouth.write(closedAngle);
      delay(200);
      //"up"
      mouth.write(openAngle);
      delay(700);
      //close end word
      mouth.write(closedAngle);
      delay(1500);
    }
  
    act = act + 1;
    if (act > 5) {
      act = 1;
    }

  }

  if (digitalRead(alarm_button) == HIGH) {
    wake_up = true;    
  }
  
  if (digitalRead(snooze_button) == HIGH) {
    if (wake_up == true) {
      wake_up = false;
      
      pixels.setBrightness(120);
      pixels.fill(color_6);
      pixels.show();
      
      brow.write(brow_1);
      
      delay(1000);
    }
  }
}

Audio:

/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
 <https://www.dfrobot.com/index.php?route=product/product&product_id=1121>

 ***************************************************
 This example shows the basic function of library for DFPlayer.

 Created 2016-12-07
 By [Angelo qiao](Angelo.qiao@dfrobot.com)

 GNU Lesser General Public License.
 See <http://www.gnu.org/licenses/> for details.
 All above must be included in any redistribution
 ****************************************************/

/***********Notice and Trouble shooting***************
 1.Connection and Diagram can be found here
 <https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
 2.This code is tested on Arduino Uno, Leonardo, Mega boards.
 ****************************************************/

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

int alarm_button = 2;
int snooze_button = 3;
bool song_paused = true;

void setup()
{
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  
  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
  
  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));

  pinMode(alarm_button, INPUT);
  pinMode(snooze_button, INPUT);
  
  myDFPlayer.volume(30);  //Set volume value. From 0 to 30
}

void loop()
{
  static unsigned long timer = millis();
  if (digitalRead(alarm_button) == HIGH) { 
    if (song_paused == true) {
      myDFPlayer.play(2);
      song_paused = false;
    }
  } 
  
  if (digitalRead(snooze_button) == HIGH) { 
    if (song_paused == false) {
      myDFPlayer.stop();
    }
  } 
  
  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
  }
}

void printDetail(uint8_t type, int value){
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

Other:

During the process, I experimented with a couple different puppets, but eventually settled on those above. These are the other animatronics I made during this project, albeit without the heartbeat of an Arduino Uno.

Progress Pics: