# Animatronics

# Tombstone Ghoul



# WLED

[https://install.wled.me/](https://install.wled.me/)

DO NOT select a beta version - wifi settings will not work.

[![wled-settings.png](https://docs.impressto.ca/uploads/images/gallery/2024-09/scaled-1680-/wled-settings.png)](https://docs.impressto.ca/uploads/images/gallery/2024-09/wled-settings.png)

# MP3 sound control

This simple project will use a mp3 player, a simple, recycled speaker, and arduino and a HLK-LD2420 human motion sensor to play sounds when a human walks past the device.

sketch code:

```c++
/*
 * Arduino Sketch for Motion-Activated MP3 Playback, Servo Control, and Display
 * 
 * This code interfaces with an LD2420 MMWave Sensor to detect motion. Upon
 * detecting motion, it commands a DF Player Mini to play an MP3 file, controls
 * an MG996R servo motor to rotate 45 degrees, and updates an SSD1306 I2C
 * display. The system uses an Arduino Nano as the main controller.
 */

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

// Pin definitions
#define SERVO_PIN A7
#define DFPLAYER_RX_PIN A2
#define DFPLAYER_TX_PIN A3
#define SENSOR_PIN A6
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// Create objects
SoftwareSerial mySoftwareSerial(DFPLAYER_RX_PIN, DFPLAYER_TX_PIN);
DFRobotDFPlayerMini myDFPlayer;
Servo myServo;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  // Initialize serial communication
  mySoftwareSerial.begin(9600);
  Serial.begin(9600);

  // Initialize DFPlayer
  if (!myDFPlayer.begin(mySoftwareSerial)) {
    Serial.println("Unable to begin DFPlayer Mini");
    while (true);
  }
  myDFPlayer.volume(20);  // Set volume level (0-30)

  // Initialize Servo
  myServo.attach(SERVO_PIN);
  myServo.write(0);  // Set initial position

  // Initialize sensor pin
  pinMode(SENSOR_PIN, INPUT);

  // Initialize display
  if (!display.begin(SSD1306_I2C_ADDRESS, 0x3C)) {
    Serial.println("SSD1306 allocation failed");
    for (;;);
  }
  display.display();
  delay(2000);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("System Ready");
  display.display();
}

void loop() {
  // Check for motion detection
  if (digitalRead(SENSOR_PIN) == HIGH) {
    // Play MP3 file
    myDFPlayer.play(1);  // Play the first track

    // Rotate servo 45 degrees
    myServo.write(45);
    delay(1000);  // Wait for 1 second

    // Return servo to initial position
    myServo.write(0);
    delay(1000);  // Wait for 1 second

    // Update display
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Motion Detected!");
    display.display();
    delay(2000);
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("System Ready");
    display.display();
  }
}
```

Basic Wiring

[![motion_motor_sound.png](https://docs.impressto.ca/uploads/images/gallery/2025-08/scaled-1680-/motion-motor-sound.png)](https://docs.impressto.ca/uploads/images/gallery/2025-08/motion-motor-sound.png)

# Jolly Roger

<article class="text-token-text-primary w-full focus:outline-none scroll-mt-[calc(var(--header-height)+min(200px,max(70px,20svh)))]" data-scroll-anchor="false" data-testid="conversation-turn-10" data-turn="assistant" data-turn-id="request-WEB:1e24290f-3f4f-4f86-8358-631deb0b90ae-4" dir="auto" id="bkmrk-overview-this-projec" tabindex="-1">#### Overview

This project implements a distributed network of Halloween animatronics controlled by **ESP32 microcontrollers**. The system is organized around a **master/slave architecture**, with the *Jolly Roger* animatronic acting as the master unit and several additional animatronics operating as synchronized slaves. Communication between units is handled wirelessly over Wi-Fi (esp-now), allowing for coordinated, scalable effects.

<div class="text-base my-auto mx-auto [--thread-content-margin:--spacing(4)] @[37rem]:[--thread-content-margin:--spacing(6)] @[72rem]:[--thread-content-margin:--spacing(16)] px-(--thread-content-margin)"><div class="[--thread-content-max-width:32rem] @[34rem]:[--thread-content-max-width:40rem] @[64rem]:[--thread-content-max-width:48rem] mx-auto max-w-(--thread-content-max-width) flex-1 group/turn-messages focus-visible:outline-hidden relative flex w-full min-w-0 flex-col agent-turn" tabindex="-1"><div class="flex max-w-full flex-col grow"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 text-start break-words whitespace-normal [.text-message+&]:mt-5" data-message-author-role="assistant" data-message-id="6df53a95-919b-4c91-87da-01a66afa64ed" data-message-model-slug="gpt-5" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]"></div></div></div></div></div></article><article class="text-token-text-primary w-full focus:outline-none scroll-mt-[calc(var(--header-height)+min(200px,max(70px,20svh)))]" data-scroll-anchor="false" data-testid="conversation-turn-14" data-turn="assistant" data-turn-id="d145c45f-d16a-4a98-b690-a9cc2ed45049" dir="auto" id="bkmrk-overview-each-animat" tabindex="-1"><div class="text-base my-auto mx-auto [--thread-content-margin:--spacing(4)] @[37rem]:[--thread-content-margin:--spacing(6)] @[72rem]:[--thread-content-margin:--spacing(16)] px-(--thread-content-margin)"><div class="[--thread-content-max-width:32rem] @[34rem]:[--thread-content-max-width:40rem] @[64rem]:[--thread-content-max-width:48rem] mx-auto max-w-(--thread-content-max-width) flex-1 group/turn-messages focus-visible:outline-hidden relative flex w-full min-w-0 flex-col agent-turn" tabindex="-1"><div class="flex max-w-full flex-col grow"><div class="flex flex-col gap-2"><div class="popover bg-token-bg-primary dark:bg-token-bg-elevated-secondary border-token-border-default dark:border-token-border-light relative z-0 border [--canvas-bg:var(--bg-primary)] dark:[--canvas-bg:var(--bg-elevated-secondary)] overflow-visible cursor-text font-regular rounded-3xl w-full w-full pt-1 pb-3 sm:-mx-6 sm:w-[calc(100%+3rem)] sm:max-w-none" id="bkmrk-overview-each-animat-1"><div class="relative flex min-h-0 w-full flex-1 flex-col self-end"><div class="relative flex min-h-0 flex-auto flex-col overflow-hidden border-t transition-colors border-transparent"><section class="popover flex h-full w-full flex-col bg-transparent"><section class="relative flex min-h-0 flex-auto grow flex-col overflow-hidden"><div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"></div></div></div></div></div>#### Overview

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- Each animatronic is controlled by an ESP32. Master has an LD2410 radar sensor to detect human presence and broadcasts triggers to slaves.
- Audio: DFPlayer Mini on every unit.
- Motion: SG90 hobby servos (powered from a 5V supply).
- Lighting: addressable LEDs (WS2812-style) or simple LED strings — powered from 5V.
- Eyes: GC9A01 round SPI LCD.

<div contenteditable="false">---

</div></div></div></div></div></div></div>#### Power &amp; decoupling

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- Supply rails:
    
    
    - **5V** for servos, DFPlayer, LEDs, speakers (if using amp) — recommended common supply for power-hungry parts.
    - **3.3V** for ESP32 logic (ESP32's regulator when using 5V VIN).
- **Common ground**: absolutely tie the 5V and ESP32 ground together.
- **Decoupling for servos &amp; LEDs**:
    
    
    - Add a 1000 μF electrolytic capacitor (or larger depending on the number of servos) close to the servo/LED power feed.
    - Add 0.1 μF ceramic capacitors near ESP32 Vcc pins.
- **Wire gauge**: use thicker wires (20–18 AWG) for servo + LED power if several are in parallel to avoid voltage drop.

<div contenteditable="false">---

</div></div></div></div></div></div></div>#### Master (Jolly Roger) wiring (high level)

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- **ESP32** VCC -&gt; 3.3V (or VIN from 5V via onboard regulator)
- **LD2410**
    
    
    - VCC -&gt; 5V (or module-specified supply)
    - GND -&gt; common GND
    - Output -&gt; ESP32 BUSY/INT pin (see section 5)
- **DFPlayer Mini**
    
    
    - VCC -&gt; 5V
    - GND -&gt; GND
    - RX/TX -&gt; ESP32 UART (use hardware UART, see section 6)
- **Servos (SG90)**
    
    
    - VCC -&gt; 5V rail
    - GND -&gt; GND
    - Signal -&gt; ESP32 PWM-capable pin
- **LEDs** -&gt; 5V + data pin to ESP32 (with 470Ω series on the data line recommended for WS2812)
- **GC9A01** -&gt; SPI bus pins + CS/DC/RST (see section 7)

<div contenteditable="false">---

</div></div></div></div></div></div></div>#### Slave animatronic wiring (same as master but no LD2410)

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- ESP32, DFPlayer, servos, LEDs, GC9A01 wired exactly like master nodes.
- Each slave listens for the master broadcast and runs its local routine.

<div contenteditable="false">---

</div></div></div></div></div></div></div>#### Level shifting &amp; BUSY-pin handling

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- **Problem:** DFPlayer BUSY may be open-drain/floating or output 5V.
- **If BUSY is 5V TTL**: use a resistor divider (example below) or logic level shifter.

</div></div></div></div></div></div>**Voltage divider (5V -&gt; 3.3V)**

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no"><div class="cm-editor ͼ1 ͼ2 ͼ4 ͼar ͼas" contenteditable="false" data-is-code-block-view="true"><div aria-live="polite" class="cm-announced">  
</div><div class="cm-scroller" tabindex="-1"><div aria-multiline="true" autocapitalize="off" autocorrect="off" class="cm-content" contenteditable="true" role="textbox" spellcheck="false" translate="no"><div class="cm-line">BUSY (DFPlayer) ---- R1 ----+----&gt; ESP32 input</div><div class="cm-line">|</div><div class="cm-line">R2</div><div class="cm-line">|</div><div class="cm-line">GND</div><div class="cm-line">  
</div><div class="cm-line">Use R1 = 10kΩ, R2 = 20kΩ -&gt; Vout ≈ 3.33V when BUSY = 5V</div></div><div aria-hidden="true" class="cm-layer cm-layer-above cm-cursorLayer"><div class="cm-cursor cm-cursor-primary">  
</div></div><div aria-hidden="true" class="cm-layer cm-selectionLayer">  
</div></div></div></div></div></div></div></div></div>**BUSY to input-only pins (GPIO34–39)**

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- These pins **do not support internal pull-ups**. If the BUSY line can float (open-drain), add an external **10k pull-up to 3.3V**.
- If you wire BUSY to GPIOs that support `INPUT_PULLUP` (e.g., 25, 26, 32, 33), you can use `pinMode(pin, INPUT_PULLUP)`.

<div contenteditable="false">---

</div></div></div></div></div></div></div>#### DFPlayer wiring notes (UART)

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- DFPlayer VCC -&gt; 5V, GND -&gt; common ground.
- DFPlayer TX -&gt; ESP32 RX (no level shift needed if DFPlayer TX ≈ 3.3V). If unsure, measure with a multimeter.
- DFPlayer RX -&gt; ESP32 TX (ESP32 TX is 3.3V, OK for DFPlayer RX).
- Use one of ESP32 hardware UARTs (UART2 is convenient): e.g. TX2=GPIO17, RX2=GPIO16. These pins are commonly free on dev boards.
- If these pins conflict in your build, pick other UART-capable pins and configure `Serial2.begin(9600, SERIAL_8N1, rxPin, txPin);`.

<div contenteditable="false">---

</div></div></div></div></div></div></div>#### GC9A01 round LCD (SPI) wiring

Example mapping (shared SPI bus; one CS per display):

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- SCLK -&gt; GPIO18
- MOSI -&gt; GPIO23
- MISO -&gt; (not used)
- CS -&gt; GPIO5 (per display choose unique CS if multiple)
- DC -&gt; GPIO21
- RST -&gt; GPIO22

</div></div></div></div></div></div>> These pins are examples — SPI can be remapped. Keep MOSI/SCLK on the same SPI peripheral for best performance.

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no"><div contenteditable="false">---

</div></div></div></div></div></div></div>#### Servo wiring &amp; recommendations

<div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"><div class="relative z-10 flex max-w-full h-fit"><div class="_main_5jn6z_1 z-10 markdown prose dark:prose-invert contain-inline-size focus:outline-hidden bg-transparent ProseMirror" contenteditable="false" translate="no">- SG90 signal pins are 3.3V-logic-friendly.
- Use separate 5V supply for servos to avoid brownouts on ESP32.
- Add a 1000 μF cap across 5V and GND near servo power feed.
- Use PWM pins for servo signals; each ESP32 can drive several servos using `ledc` or `servo` libraries.

<div contenteditable="false">---

</div></div></div></div></div></div></div><div class="block h-auto"><div class="h-full w-full"><div class="flex h-full justify-center"><div class="z-0 flex w-full flex-col items-center"></div></div></div></div></section></section></div></div></div></div></div></div></div></article>[![jolly_roger.png](https://docs.impressto.ca/uploads/images/gallery/2025-08/scaled-1680-/jolly-roger.png)](https://docs.impressto.ca/uploads/images/gallery/2025-08/jolly-roger.png)

# animated eyes

# [![tft_displays.jpg](https://docs.impressto.ca/uploads/images/gallery/2025-09/scaled-1680-/tft-displays.jpg)](https://docs.impressto.ca/uploads/images/gallery/2025-09/tft-displays.jpg)

Find a ralatively square image, and if not square, use gimp or an equivalent image editor to crop it to square. Idealy resize it to match your display (240x240 for round displays), then uoload the image to [https://impressto.ca/bmp\_converter.php](https://impressto.ca/bmp_converter.php) Source: [https://github.com/impressto/bmp-to-c](https://github.com/impressto/bmp-to-c)

If the image was not the correct size, you can resize it in the tool above. Once you save the new c header file, you can save it to your esp32 or arduino source folder and include it in the head.

Source code for the esp32 is here:[ https://github.com/impressto/esp\_lcd\_eyes](https://github.com/impressto/esp_lcd_eyes)

Demo:

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" frameborder="0" height="315" src="https://www.youtube.com/embed/3VUYphgKUfU?si=h9qEMN8IF2jjPL2W" title="YouTube video player" width="560"></iframe>

# ESP32 Mesh Network for Halloween Prop Control

This project uses a **mesh network of ESP32 microcontrollers** communicating via **ESP-NOW** to coordinate a set of Halloween props. The goal is to create synchronized, responsive effects triggered by motion — perfect for haunted house setups or outdoor displays.

#### **System Overview**

The network consists of one **master controller** and multiple **slave controllers**:

- **Master Node:**  
    Acts as the central coordinator. It’s equipped with a motion sensor (such as a PIR sensor) that detects when someone approaches.  
    When motion is detected, the master broadcasts a **“trigger” message** over ESP-NOW to all slave nodes.
- **Slave Nodes:**  
    Each slave node controls an individual prop (e.g., fog machine, servo motor, LED lighting, sound module, etc.).  
    Upon receiving the master’s **trigger message**, the slave performs its assigned action — for example, moving a prop, flashing lights, or playing a sound.  
    Once the action is complete, the slave sends a **“completed” message** back to the master.

#### **Communication Flow**

1. **Motion Detection:**  
    The master’s PIR sensor detects movement.
2. **Broadcast Trigger:**  
    The master sends a “motion detected” message via ESP-NOW to all registered slave devices.
3. **Action Execution:**  
    Each slave performs its programmed effect or animation.
4. **Completion Feedback:**  
    After finishing, each slave sends a “done” signal to the master.
5. **Reset or Cooldown:**  
    Once the master receives all “done” signals (or after a timeout), it resets the system and waits for the next motion event.

#### **Key Features**

- **ESP-NOW protocol** allows for **low-latency**, **Wi-Fi-free** communication — no router or internet required.
- **Scalable design** — easily add or remove props by pairing additional ESP32s.
- **Bidirectional communication** ensures the master knows when all props have completed their sequences.
- **Energy-efficient** — nodes can sleep between triggers.
- **Highly customizable** — each slave can have its own timing, behavior, or randomized effect.

#### **Example Use Case**

- Master detects motion → broadcasts “TRIGGER”
- Slave 1 (fog machine): starts fog → sends “DONE” after 10 seconds
- Slave 2 (LED lights): flashes red and orange → sends “DONE” after 5 seconds
- Slave 3 (servo skeleton): waves arm → sends “DONE” after 8 seconds
- Once all “DONE” signals are received, the master resets and waits for the next visitor.

List of mac addresses currently in use:

master (detects motion): FC:B4:67:55:A5:24

sound player: 08:D1:F9:EE:A0:B0

[![master-animatronic-controller-wiring.png](https://docs.impressto.ca/uploads/images/gallery/2025-10/scaled-1680-/master-animatronic-controller-wiring.png)](https://docs.impressto.ca/uploads/images/gallery/2025-10/master-animatronic-controller-wiring.png)