# 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)