Animatronic W/ Spongebob Face & Audio

by goodhsieh in Circuits > Arduino

292 Views, 0 Favorites, 0 Comments

Animatronic W/ Spongebob Face & Audio

animatronic w/ spongebob face & sound

This is an open-source project using a microcontroller, two types of sensors, and actuators, designed for the UIC Mechatronics Fall 2025 course. The 3D-printed eye mechanism is modeled and printed with PLA, utilizing latches and joints to create movement. The mechanism has a total of six servo motors: two drive the eyeballs to move along the XY-axis, and four control the eyelids. A PIR sensor triggers a 2W-8Ω mono speaker, which plays back a pre-recorded file stored on a mini SD card, read by the DFPlayer Mini.

Below is a step-by-step guide on how this animatronic is built, with open-source resources provided.

*You will need access to 3D filament, printers, and slicer software.

*You will also need an IDE (Integrated Development Environment) installed to write and upload code.

Supplies

1 x breadboard

1 x external 5V 3A power supply

1 x microcontroller, this demo specifically uses Arduino Uno

1 x PIR sensor

1 x B103 joystick sensor

1 x DFPlayer Mini MP3

1 x 2W-8Ω mono speaker

6 x S90 servo motors

3D Print & Assemble

The Most Realistic Animatronic Eyes I've Ever Built

*You will need access to 3D filament, printers, and slicer software.

I followed the Animatronic Eye Mechanism workshop by Ikkalebob. I downloaded and modified the 3D models, printed the parts, and followed the assembly instructions.

The attached file is my modified version of Ikkalebob's "EyeMech ε3.2 All Plates.3mf"

Joystick-to-ServoMotor Actuation

ME411_ProjectDoc_JoystickServo_goodhsieh
servomotor_pinout.png
joystick_pinout.png

*You will also need an IDE (Integrated Development Environment) installed to write and upload code.

After assembling the eye mechanism, the next step is to connect the servo motors and joystick to your microcontroller, using a breadboard for prototyping. In this step, you will wire one joystick (sensor) to control multiple servos (actuators):

Joystick → Microcontroller → Servo Motors

2.1 — Power & Ground Bus Setup

  1. On the breadboard, create a power rail and a ground rail by connecting the external +5 V supply to the power rail and the external GND to the ground rail.
  2. Plug each servo’s VCC and GND wires into the respective rails.

(This ensures all servos receive stable 5 V power during prototyping.)

2.2 — Wire the Servo Motors

Each SG90 servo has three pins: VCC, GND, and PWM (signal). In your Arduino code, declare a variable for each servo corresponding to a mechanical part of the eye mechanism. Suggested pin assignments:

  1. X‑axis eye movement → Arduino D3
  2. Y‑axis eye movement → Arduino D5
  3. Left upper eyelid → Arduino D6
  4. Left lower eyelid → Arduino D7
  5. Right upper eyelid → Arduino D8
  6. Right lower eyelid → Arduino D9

(Adjust pin numbers as desired, but keep wiring and code consistent.)

2.3 — Wire the Joystick Module

A typical joystick breakout board has five pins: VCC, GND, VRx, VRy, SW. Suggested pin assignments:

  1. VCC → Arduino 5V
  2. GND → shared ground
  3. VRx: outputs horizontal axis analog signal (X-axis) → Arduino A0
  4. VRy: outputs vertical axis analog signal (Y-axis) → Arduino A1
  5. SW: push-button switch (digital HIGH/LOW when pressed) →Arduino D2

(Power the joystick from the Arduino’s 5 V pin. This ensures stable reference levels for accurate analog-to-digital converter readings.)

2.4 — Step Summary

  1. Joystick pins used: VCC, GND, VRx, VRy, SW
  2. Servo pins used: 6 signal pins (D3, D5, D6, D7, D8, D9) + shared VCC/GND from external supply
  3. Arduino connections:
  4. Analog inputs: A0 (X-axis), A1 (Y-axis)
  5. Digital input: D2 (joystick button)
  6. Digital outputs: D3–D9 (servo motors control)

Once everything is wired, upload the control code to read the joystick inputs and move the servos accordingly.

PIRSensor-to-Speaker Actuation

PIR_pinout.PNG
dfplayermini_pinout.png

*You will also need an IDE (Integrated Development Environment) installed to write and upload code.

Next, you will connect the PIR sensor to the microcontroller and wire the DFPlayer Mini MP3 module to a mono speaker. This allows the system to play audio whenever motion is detected:

PIR Sensor → Microcontroller → DFPlayer Mini MP3 → Mono Speaker

3.1 — Wire the PIR Sensor

A PIR (Passive Infrared) sensor has three pins: VCC, GND, and OUT. It outputs a digital HIGH/LOW signal whenever motion is detected. Suggested pin assignments:

  1. VCC → external 5 V power rail
  2. GND → shared ground
  3. OUT → Arduino D4

(The PIR detects changes in infrared heat from moving objects. In code, you may initialize a state variable such as int pirState = LOW; to track transitions from no-motion to motion.)

3.2 — Wire the DFPlayer Mini MP3 Module

The DFPlayer Mini includes 16 pins, but only 6 are needed for basic audio playback: VCC, GND, RX, TX, SPK_1, and SPK_2. Suggested pin assignments:

  1. VCC → external 5 V power rail
  2. GND → shared ground
  3. RX: DFPlayer receives serial commands → Arduino D11: defined Arduino TX
  4. TX: DFPlayer sends serial feedback → Arduino D10: defined Arduino RX
  5. SPK_1 → Speaker +
  6. SPK_2 → Speaker –

(Serial communication requires crossing the lines: the Arduino’s TX pin must connect to the DFPlayer’s RX, and the Arduino’s RX must connect to the DFPlayer’s TX. Pins D10 and D11 are defined as the SoftwareSerial receive/transmit pins in the example code. Adjust the pin numbers if needed, but keep wiring and code consistent. The onboard amplifier can directly drive mono speakers up to 3W.)

3.3 — Prepare & Load the microSD Card

To enable audio playback, load your sound files (e.g., a recording from SpongeBob or any audio of your choice) onto a microSD card and insert it into the DFPlayer Mini.

  1. The microSD card must be formatted as FAT32.
  2. The DFPlayer Mini supports MP3, WAV, and WMA file formats.
  3. For reliable playback—especially when selecting tracks by number—use numeric filenames, such as:
  4. 0001.mp3
  5. 0002.wav

(Although the DFPlayer can read other names, numeric naming ensures consistent behavior across all playback modes.)

3.4 — Step Summary

  1. PIR pins used: VCC, GND, OUT
  2. DFPlayer pins used: VCC, GND, RX, TX, SPK_1, SPK_2
  3. Arduino connections:
  4. Digital input: D4 (PIR sensor)
  5. Software serial TX/RX: D11, D10 (DFPlayer Mini MP3 serial communication)

Once everything is wired, upload the control code to trigger audio whenever motion is detected.

Upload Code to Arduino Using IDE

#include <Servo.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>

// Eye servos
Servo eyeX;
Servo eyeY;

// Eyelid servos
Servo lidLeftTop;
Servo lidLeftBottom;
Servo lidRightTop;
Servo lidRightBottom;

// Joystick pins
const int joyX = A0;
const int joyY = A1;
const int joySW = 2;

// Eyelid positions (tune separately)
const int LID_LEFT_TOP_OPEN = 30;
const int LID_LEFT_TOP_CLOSED = 0;

const int LID_LEFT_BOTTOM_OPEN = 20;
const int LID_LEFT_BOTTOM_CLOSED = 100;

const int LID_RIGHT_TOP_OPEN = 0;
const int LID_RIGHT_TOP_CLOSED = 60;

const int LID_RIGHT_BOTTOM_OPEN = 10;
const int LID_RIGHT_BOTTOM_CLOSED = 0;

unsigned long lastBlink = 0;
const unsigned long blinkDelay = 300; // ms

// PIR sensor
const int pirPin = 4;
int pirState = LOW;

// DFPlayer Mini
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;

void setup() {
// Attach servos
eyeX.attach(3);
eyeY.attach(5);
lidLeftTop.attach(6);
lidLeftBottom.attach(7);
lidRightTop.attach(8);
lidRightBottom.attach(9);

pinMode(joySW, INPUT_PULLUP);
pinMode(pirPin, INPUT);

// DFPlayer setup
Serial.begin(9600);
mySoftwareSerial.begin(9600);

Serial.println("Waiting for DFPlayer to power up...");
delay(1000);

Serial.println("Initializing DFPlayer...");
if (!myDFPlayer.begin(mySoftwareSerial)) {
Serial.println("DFPlayer Mini not found! Check wiring, SD card, and common ground.");
} else {
Serial.println("DFPlayer Mini online.");
myDFPlayer.volume(25); // Set volume (0–30)
}
}

void loop() {
// --- Joystick control for eyes ---
int xVal = analogRead(joyX);
int yVal = analogRead(joyY);
int swVal = digitalRead(joySW);

int eyeXAngle = map(xVal, 0, 1023, 30, 150);
int eyeYAngle = map(yVal, 0, 1023, 30, 150);

eyeX.write(eyeXAngle);
eyeY.write(eyeYAngle);

if (swVal == LOW && millis() - lastBlink > 500) {
blink();
lastBlink = millis();
}

// --- PIR motion detection for sound ---
int val = digitalRead(pirPin);
if (val == HIGH && pirState == LOW) {
Serial.println("Motion detected! Playing audio...");
myDFPlayer.play(1); // Play 0001.wav
pirState = HIGH;
}
else if (val == LOW && pirState == HIGH) {
Serial.println("Motion ended.");
pirState = LOW;
}
}

void blink() {
lidLeftTop.write(LID_LEFT_TOP_CLOSED);
lidLeftBottom.write(LID_LEFT_BOTTOM_CLOSED);
lidRightTop.write(LID_RIGHT_TOP_CLOSED);
lidRightBottom.write(LID_RIGHT_BOTTOM_CLOSED);

delay(blinkDelay);

lidLeftTop.write(LID_LEFT_TOP_OPEN);
lidLeftBottom.write(LID_LEFT_BOTTOM_OPEN);
lidRightTop.write(LID_RIGHT_TOP_OPEN);
lidRightBottom.write(LID_RIGHT_BOTTOM_OPEN);
}

Reflection

In Step 1, I used epoxy to attach the 3D-printed eye mechanism to the servo motors and then calibrated the angles to achieve the desired range of motion. However, this setup limits the mechanism’s movement due to the angle constraints of the servo motors. This could be improved by calibrating the servo motors before gluing the joints. If you listen closely to the video, you can hear the servo motors straining against the mechanism.

Additionally, the project could be further enhanced by adding another servo motor to drive the mouth, allowing for more facial expression and creating a more immersive experience. Incorporating more advanced 3D modeling and design could also better integrate the eye mechanism with the rest of the animatronic, further supporting a cohesive and engaging interaction.