DEV Community

Cover image for R2D2 Creation with Codey πŸ€– Ep.8

R2D2 Creation with Codey πŸ€– Ep.8

Episode 8: The Complete Droid

"The Force Will Be With You. Always." 🌟

The entire crew stands in the workshop. Han Solo, arms folded. Luke, grinning. C-3PO, hands clasped with barely-contained anxiety. Princess Leia's message loops on the OLED β€” a recreation, text only, but the words are right: "Help me, Obi-Wan Kenobi."

And in the centre, dome slowly rotating, blue-white LEDs breathing, speaker playing a quiet ambient whistle β€” R2-D2.

OBI-WAN: quietly "Seven episodes. Seven systems. And now, the droid is nearly complete. There is one thing left β€” the final integration. The moment when all systems stop running in parallel and begin running as one coherent whole. When R2-D2 stops being a collection of components and becomes the galaxy's greatest droid."

R2-D2 beeps once. Confident. Ready.

OBI-WAN: "And then β€” the Force. The Wi-Fi."

Han Solo snorts.

HAN: "The Wi-Fi. Very mystical."

πŸ—‚οΈ SIPOC β€” The Complete Droid

Suppliers Inputs Process Outputs Customers
You (the maker) "Integrate all systems + add Wi-Fi remote command interface" Codey Deep Think mode plans the full architecture before writing A unified state machine with all systems communicating, Wi-Fi endpoint for remote commands The complete R2-D2 β€” one firmware, all systems orchestrated
ESP32-S3 N16R8 16MB Flash, 8MB PSRAM, dual-core, Wi-Fi Dual-core operation: Core 0 for Wi-Fi/networking, Core 1 for real-time sensor/motor/audio loop Smooth operation without Wi-Fi blocking the real-time control loop All R2-D2 systems β€” none starved of processor time
Codey Deep Think mode Complex multi-file integration prompt Takes longer to respond β€” reasons about the whole architecture first The cleanest, most architecturally coherent code in the series Your long-term maintainability
Final Wiring Diagram All 7 systems' component lists Codey draws the complete master diagram with all connections, protection circuits, and power rails A single PDF showing the complete R2-D2 build Your workshop wall β€” the crown jewel of your build log

The Final Component Inventory β€” Every Piece of R2-D2 πŸ”§

C-3PO produces a datapad with the air of someone who has been waiting seven episodes to read this list.

C-3PO: "The complete inventory, as accumulated across eight glorious β€” and at times terrifying β€” episodes:"

Component Episode Added Purpose
ESP32-S3 N16R8 5 The brain β€” all systems connect here
SSD1306 OLED 128Γ—64 5 Holographic display β€” I2C via GPIO8/9
NeoPixel ring 12 LED 3 Dome lights β€” GPIO6 via level shifter
HC-SR04 ultrasonic 4 Distance sensing eye β€” GPIO9/10
PIR HC-SR501 4 Motion sensing β€” GPIO2 (interrupt)
SG90 servo 6 Dome rotation β€” GPIO14 via level shifter
DC gear motors Γ— 2 6 Wheel drive β€” via L298N
L298N motor driver 6 Motor control, also provides 5V rail
DFPlayer Mini 7 Audio playback β€” UART2 GPIO16/17
Speaker 4Ξ©/8Ξ© 7 Voice output
microSD card (FAT32) 7 Audio files 0001–0007.mp3
74AHCT125 level shifter 3/6 NeoPixel + servo: 3.3V β†’ 5V
470Ξ© resistor 3 NeoPixel data protection
1kΞ© resistor 7 DFPlayer RX protection
100Ξ© resistor β€” (buzzer, now removed)
100Β΅F capacitor 3 NeoPixel power decoupling
10Β΅F capacitor 7 DFPlayer power filtering
Voltage dividers (10k+20k) 5 HC-SR04 ECHO + PIR output: 5V β†’ 3.3V
9V battery or 2S LiPo 6 Motor power rail
USB cable All Programming + logic power

C-3PO: "R2-D2. In components. On a breadboard. I find this both impressive and somewhat unsettling."

R2-D2 beeps with what sounds like pride.

Deep Think Mode: The Wisest Path 🧠

Yoda settles into his chair.

YODA: "Complex this prompt is. Many files to touch. Many systems to unify. For this β€” Deep Think mode you shall use. Longer it takes. Wiser the result."

LUKE: "What's Deep Think mode?"

YODA: "Plan mode's elder sibling. Not just a plan β€” a deep analysis of the architecture first. Consider edge cases it will. Identify conflicts between systems it shall. Only then write, it does."

In Codey, click the mode dropdown and select Deep Think (Pro feature). Then type the final integration prompt:

FINAL R2-D2 INTEGRATION β€” Deep Think mode

We have these existing header files for the ESP32-S3 N16R8:
  - animations.h  (NeoPixel dome: GPIO6 via 74AHCT125)
  - sensors.h     (HC-SR04: GPIO9 TRIG / GPIO10 ECHO; PIR: GPIO2)
  - display.h     (SSD1306 OLED: I2C SDA=GPIO8 / SCL=GPIO9 β€” NOTE: conflict!)
  - motion.h      (Servo: GPIO14; L298N: GPIO25-27, GPIO32-34)
  - voice.h       (DFPlayer Mini: UART2 TX=GPIO17 / RX=GPIO16)
  - sounds.h      (LEDC buzzer: now removed, GPIO freed)

CONFLICT TO RESOLVE:
  HC-SR04 TRIG uses GPIO9. OLED I2C SCL also uses GPIO9.
  These cannot share! Remap one of them.
  Suggestion: Move HC-SR04 TRIG to GPIO11, ECHO to GPIO12.
  Update sensors.h and the wiring diagram accordingly.

NEW FEATURE β€” Wi-Fi remote control:
  Use ESP32-S3 Wi-Fi to create a simple HTTP server.
  Endpoints:
    GET /status    β†’ JSON with distance, motion, dome angle, current sound
    GET /command/forward   β†’ drive forward for 2 seconds
    GET /command/backward  β†’ drive backward for 2 seconds
    GET /command/left      β†’ turn left for 1 second
    GET /command/right     β†’ turn right for 1 second
    GET /command/stop      β†’ stop motors immediately
    GET /command/sound/N   β†’ play sound N (1-7)
    GET /command/dome/N    β†’ move dome to angle N degrees

  Use WiFiAP mode (R2-D2 creates its own access point):
    SSID: "R2-D2"
    Password: "beepboop"

  Run HTTP server on Core 0. Real-time systems on Core 1.

FINAL r2d2-main.ino:
  - Resolve the GPIO conflict
  - Add wifi.h header for Wi-Fi + HTTP server
  - Clean unified loop with clear comments
  - All systems operational
  - Serial output at 115200 showing all state transitions

Please think through the architecture fully before writing.

Enter fullscreen mode Exit fullscreen mode

Deep Think Mode Analysis (Codey Responds)

[Deep Think analysis in progress...]

Architecture review:

1. GPIO CONFLICT IDENTIFIED:
   GPIO9 cannot serve both HC-SR04 TRIG and I2C SCL.
   Resolution: Remap I2C to SDA=GPIO4, SCL=GPIO5 (available on ESP32-S3).
   This avoids touching sensors.h; only display.h changes:
     Wire.begin(4, 5) instead of Wire.begin(8, 9).

2. DUAL-CORE STRATEGY:
   Core 0 (Arduino default): Wi-Fi, HTTP server, WebServer library
   Core 1 (pinned task): sensor reading, NeoPixel, servo, display, audio
   Separation prevents Wi-Fi TCP/IP stack from blocking 50Hz servo updates.
   Implementation: xTaskCreatePinnedToCore() for real-time loop.

3. STATE MACHINE:
   Unified DomeState enum: IDLE, SCAN, ALERT, DANGER, REMOTE_CONTROL
   REMOTE_CONTROL state: Wi-Fi command overrides sensor-driven behavior.
   Auto-return to IDLE after 5 seconds of no remote command.

4. MEMORY:
   16MB Flash β€” no concern.
   8MB PSRAM β€” HTTP response buffers can use ps_malloc() if needed.
   RAM: estimated ~68KB used (20.8% of 327KB) β€” safe.

5. POWER:
   Wi-Fi radio: ~240mA peak. USB 500mA should be sufficient for ESP32-S3.
   Motors: separate 9V rail β€” no impact on ESP32 power.

Generating files now...

Enter fullscreen mode Exit fullscreen mode

Generated wifi.h

// wifi.h β€” R2-D2 Wi-Fi Remote Control
// ESP32-S3 Access Point: SSID "R2-D2", password "beepboop"
// HTTP server on port 80 β€” runs on Core 0

#pragma once
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>

const char* AP_SSID     = "R2-D2";
const char* AP_PASSWORD = "beepboop";
const int   HTTP_PORT   = 80;

WebServer server(HTTP_PORT);

// Shared state (volatile β€” accessed from both cores)
volatile float  sharedDistance    = 999.0f;
volatile bool   sharedMotion      = false;
volatile int    sharedDomeAngle   = 90;
volatile bool   remoteCommandPending = false;
volatile String remoteCommand     = "";

// ── Status endpoint ──────────────────────────────────────────────
void handleStatus() {
  StaticJsonDocument<256> doc;
  doc["distance_cm"] = sharedDistance;
  doc["motion"]      = sharedMotion;
  doc["dome_angle"]  = sharedDomeAngle;
  doc["state"]       = "online";
  doc["uptime_s"]    = millis() / 1000;

  String response;
  serializeJson(doc, response);
  server.send(200, "application/json", response);
  Serial.println("[WiFi] GET /status");
}

// ── Command endpoints ────────────────────────────────────────────
void handleCommand() {
  String path    = server.uri();      // e.g. "/command/forward"
  String command = path.substring(9); // strip "/command/"
  remoteCommand        = command;
  remoteCommandPending = true;
  server.send(200, "text/plain", "ACK: " + command);
  Serial.print("[WiFi] Command: ");
  Serial.println(command);
}

// ── Root page (simple UI) ─────────────────────────────────────────
void handleRoot() {
  String html = R"rawhtml(
<!DOCTYPE html>
<html>
<head><title>R2-D2 Control</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
  body { font-family:sans-serif; background:#111; color:#4af;
         display:flex; flex-direction:column; align-items:center; }
  h1 { color:#4af; }
  button { background:#1a3a5a; color:#4af; border:2px solid #4af;
           padding:14px 28px; font-size:1.1em; margin:6px;
           border-radius:6px; cursor:pointer; }
  button:active { background:#4af; color:#111; }
  #status { margin-top:16px; font-size:0.9em; color:#aaa; }
</style></head>
<body>
<h1>R2-D2</h1>
<div>
  <button onclick="cmd('sound/5')">πŸ”Š Startup</button>
  <button onclick="cmd('sound/1')">πŸ˜„ Happy</button>
  <button onclick="cmd('sound/3')">😒 Sad</button>
</div>
<div>
  <button onclick="cmd('forward')">β–² Forward</button>
</div>
<div>
  <button onclick="cmd('left')">β—„ Left</button>
  <button onclick="cmd('stop')">β–  Stop</button>
  <button onclick="cmd('right')">β–Ί Right</button>
</div>
<div>
  <button onclick="cmd('backward')">β–Ό Backward</button>
</div>
<div>
  <button onclick="cmd('dome/0')">Dome β—„</button>
  <button onclick="cmd('dome/90')">Dome ●</button>
  <button onclick="cmd('dome/180')">Dome β–Ί</button>
</div>
<div id="status">Connecting...</div>
<script>
function cmd(c) {
  fetch('/command/'+c).then(r=>r.text()).then(t=>{
    document.getElementById('status').textContent = 'Last: '+c;
  });
}
setInterval(()=>{
  fetch('/status').then(r=>r.json()).then(d=>{
    document.getElementById('status').textContent =
      'Dist: '+d.distance_cm.toFixed(0)+'cm | Motion: '+d.motion+
      ' | Uptime: '+d.uptime_s+'s';
  });
}, 2000);
</script>
</body></html>
)rawhtml";
  server.send(200, "text/html", html);
}

// ── Init Wi-Fi + HTTP server (call from Core 0 task) ─────────────
void initWifi() {
  WiFi.softAP(AP_SSID, AP_PASSWORD);
  Serial.print("[WiFi] AP started: ");
  Serial.println(AP_SSID);
  Serial.print("[WiFi] IP: ");
  Serial.println(WiFi.softAPIP());

  server.on("/",          handleRoot);
  server.on("/status",    handleStatus);
  server.onNotFound([]() {
    // Any /command/* path
    if (server.uri().startsWith("/command/")) handleCommand();
    else server.send(404, "text/plain", "Not found");
  });

  server.begin();
  Serial.println("[WiFi] HTTP server started β€” connect to R2-D2 network");
}

// ── Core 0 task: runs Wi-Fi server continuously ──────────────────
void wifiTask(void* param) {
  initWifi();
  for (;;) {
    server.handleClient();
    vTaskDelay(1); // yield to RTOS β€” must not block Core 0
  }
}

Enter fullscreen mode Exit fullscreen mode

Final r2d2-main.ino

// r2d2-main.ino β€” R2-D2 COMPLETE
// ESP32-S3 N16R8 | All systems operational
// Core 0: Wi-Fi + HTTP server
// Core 1: Sensors, display, NeoPixel, servo, motors, audio

#include <Adafruit_NeoPixel.h>
#include "animations.h"   // NeoPixel dome: GPIO6
#include "sensors.h"      // HC-SR04: GPIO9(TRIG) GPIO10(ECHO); PIR: GPIO2
#include "display.h"      // OLED I2C: SDA=GPIO4 SCL=GPIO5 (CONFLICT RESOLVED)
#include "motion.h"       // Servo: GPIO14; L298N: GPIO25-27, GPIO32-34
#include "voice.h"        // DFPlayer: UART2 GPIO16/17
#include "wifi.h"         // Wi-Fi AP + HTTP server

TaskHandle_t realtimeTask;   // Core 1 handle

// ── Remote command processor ─────────────────────────────────────
void processRemoteCommand(const String& cmd) {
  if      (cmd == "forward")   motorForward();
  else if (cmd == "backward")  motorBackward();
  else if (cmd == "left")      motorTurnLeft();
  else if (cmd == "right")     motorTurnRight();
  else if (cmd == "stop")      motorStop();
  else if (cmd.startsWith("sound/")) {
    int n = cmd.substring(6).toInt();
    if (n >= 1 && n <= 7) playSound((R2Sound)n);
  }
  else if (cmd.startsWith("dome/")) {
    int angle = cmd.substring(5).toInt();
    domeSnap(constrain(angle, 0, 180));
  }
}

unsigned long lastRemoteCmdMs     = 0;
const unsigned long REMOTE_TIMEOUT = 5000; // return to autonomous after 5s

// ── Core 1: real-time loop ───────────────────────────────────────
void realtimeLoop(void* param) {
  // Init all real-time systems on Core 1
  initAnimations();
  initSensors();
  initDisplay();
  initMotion();
  initVoiceSystem();

  for (;;) {
    if (!bootDone) { showBootScreen(); vTaskDelay(10); continue; }

    float dist   = readDistance();
    bool  motion = checkMotion();

    // Share state with Wi-Fi (Core 0 reads these)
    sharedDistance  = dist;
    sharedMotion    = motion;
    sharedDomeAngle = currentDomeAngle;

    // ── Remote command takes priority ──────────────────────────
    bool remoteActive = (millis() - lastRemoteCmdMs < REMOTE_TIMEOUT);

    if (remoteCommandPending) {
      String cmd = remoteCommand;
      remoteCommandPending = false;
      processRemoteCommand(cmd);
      lastRemoteCmdMs = millis();
      remoteActive    = true;
    }

    // ── Autonomous behaviour (when no remote command) ─────────
    if (!remoteActive) {
      if (dist < 30.0f) {
        domeSnap(90);
        motorStop();
      } else if (motion) {
        domeSnap(45);
      } else {
        domeIdleSweep();
      }
    }

    // ── Display + lights + voice (always) ─────────────────────
    if (dist < 15.0f)    showAlertScreen();
    else if (motion)     showMotionScreen();
    else                 showIdleScreen(dist);

    updateAnimationsSensors(dist, motion);
    updateVoice(dist, motion, currentState);

    vTaskDelay(10); // 10ms tick β€” 100Hz loop
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("╔═══════════════════════════════╗");
  Serial.println("β•‘   R2-D2 β€” COMPLETE BUILD      β•‘");
  Serial.println("β•‘   ESP32-S3 N16R8              β•‘");
  Serial.println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•");

  // Start Wi-Fi server on Core 0
  xTaskCreatePinnedToCore(
    wifiTask,       // function
    "WiFiTask",     // name
    8192,           // stack size (bytes)
    NULL,           // parameter
    1,              // priority
    NULL,           // task handle
    0               // core: 0
  );

  // Start real-time loop on Core 1
  xTaskCreatePinnedToCore(
    realtimeLoop,   // function
    "RTLoop",       // name
    8192,           // stack size
    NULL,           // parameter
    2,              // priority (higher than Wi-Fi)
    &realtimeTask,  // task handle
    1               // core: 1
  );

  // Arduino loop() runs on Core 1 by default β€” we've moved everything
  // to FreeRTOS tasks, so loop() is intentionally empty.
}

void loop() {
  vTaskDelay(portMAX_DELAY); // yield forever β€” all work done in tasks
}

Enter fullscreen mode Exit fullscreen mode

The Final Master Wiring Diagram 🧭

C-3PO takes a deep breath and presents the complete diagram with a formality appropriate to the occasion.

C-3PO: "The final wiring diagram. Every component. Every wire. Every protection circuit. Every power rail. Downloadable as PDF. Printable. Frameable. I am... genuinely proud of this."

R2-D2 COMPLETE BUILD β€” Master Wiring Diagram
ESP32-S3 N16R8 | All Systems
════════════════════════════════════════════════════════════════════

POWER RAIL 1 β€” LOGIC (USB 5V regulated to 3.3V by ESP32-S3):
  USB 5V β†’ ESP32-S3 VIN
  ESP32 3V3 output β†’ (nothing external β€” 3.3V stays internal)
  ESP32 GND β†’ Common GND rail

POWER RAIL 2 β€” 5V PERIPHERALS (L298N internal regulator from 9V):
  9V Battery (+) β†’ L298N: VS
  9V Battery (βˆ’) β†’ GND (common with ESP32 GND!)
  L298N 5V out β†’ Servo VCC (red), 74AHCT125 VCC, DFPlayer VCC
  L298N GND β†’ Servo GND, 74AHCT125 GND, DFPlayer GND

I2C BUS (conflict-resolved: SDA=GPIO4, SCL=GPIO5):
  ESP32 GPIO4 (SDA) ──── OLED SDA
  ESP32 GPIO5 (SCL) ──── OLED SCL
  ESP32 3V3         ──── OLED VCC (via 100Β΅F cap to GND)

NEOPIXEL DOME (5V logic via level shifter):
  ESP32 GPIO6 ──── 74AHCT125 A1 ──── NeoPixel DIN
  L298N 5V    ──── NeoPixel 5V + 100Β΅F cap+
  GND         ──── NeoPixel GND + 100Β΅F capβˆ’
  (470Ξ© resistor between 74AHCT125 Y1 output and NeoPixel DIN)

HC-SR04 ULTRASONIC (5V sensor, voltage divider on ECHO):
  ESP32 GPIO9  (TRIG) ──────────────── HC-SR04 TRIG
  HC-SR04 ECHO (5V)   ── 10kΞ© ──┬──── ESP32 GPIO10
                                 └──── 20kΞ© ──── GND

PIR MOTION SENSOR (5V output, voltage divider):
  L298N 5V    ──── PIR HC-SR501 VCC
  GND         ──── PIR HC-SR501 GND
  PIR OUT (5V) ── 10kΞ© ──┬──── ESP32 GPIO2
                          └──── 20kΞ© ──── GND

SERVO β€” DOME ROTATION (5V signal via level shifter):
  ESP32 GPIO14 ──── 74AHCT125 A2 ──── Servo signal (orange)
  L298N 5V     ──── Servo VCC (red)
  GND          ──── Servo GND (brown)

L298N MOTOR DRIVER:
  ESP32 GPIO25 ──── L298N IN1
  ESP32 GPIO26 ──── L298N IN2
  ESP32 GPIO27 ──── L298N ENA (PWM)
  ESP32 GPIO32 ──── L298N IN3
  ESP32 GPIO33 ──── L298N IN4
  ESP32 GPIO34 ──── L298N ENB (PWM)
  L298N OUT1,2 ──── DC Motor Left
  L298N OUT3,4 ──── DC Motor Right

DFPLAYER MINI AUDIO:
  L298N 5V    ──── DFPlayer VCC + 10Β΅F cap+
  GND         ──── DFPlayer GND + 10Β΅F capβˆ’
  ESP32 GPIO17 ── (1kΞ©) ─── DFPlayer RX
  DFPlayer TX ──── ESP32 GPIO16
  DFPlayer SPK1 ── Speaker +
  DFPlayer SPK2 ── Speaker βˆ’

Color code (master):
  RED    = 5V / VIN
  ORANGE = 9V battery rail
  PURPLE = 3.3V logic
  BLACK  = GND (common rail)
  GREEN  = NeoPixel data (level-shifted 5V)
  BLUE   = Servo signal (level-shifted 5V)
  YELLOW = Motor direction pins (IN1-IN4)
  WHITE  = Motor PWM enable pins (ENA, ENB)
  CYAN   = I2C SDA (GPIO4)
  PINK   = I2C SCL (GPIO5)
  BROWN  = HC-SR04 TRIG (GPIO9)
  VIOLET = HC-SR04 ECHO with divider (GPIO10)
  GREY   = PIR output with divider (GPIO2)
  MAROON = DFPlayer UART (TX=GPIO17, RX=GPIO16)

COMPLETE Connection Table:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ From                 β”‚ To                                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ USB 5V               β”‚ ESP32-S3 VIN                             β”‚
β”‚ 9V Battery (+)       β”‚ L298N VS                                 β”‚
β”‚ 9V Battery (βˆ’)       β”‚ L298N GND + ESP32 GND (common)           β”‚
β”‚ L298N 5V             β”‚ Servo VCC, 74AHCT125 VCC, DFPlayer VCC   β”‚
β”‚ ESP32 GPIO4 (SDA)    β”‚ OLED SDA                                 β”‚
β”‚ ESP32 GPIO5 (SCL)    β”‚ OLED SCL                                 β”‚
β”‚ ESP32 3V3            β”‚ OLED VCC (+ 100Β΅F cap to GND)            β”‚
β”‚ ESP32 GPIO6          β”‚ 74AHCT125 A1 β†’ (470Ξ©) β†’ NeoPixel DIN     β”‚
β”‚ ESP32 GPIO9 (TRIG)   β”‚ HC-SR04 TRIG                             β”‚
β”‚ HC-SR04 ECHO (5V)    β”‚ 10k+20k divider β†’ ESP32 GPIO10           β”‚
β”‚ L298N 5V             β”‚ PIR VCC                                  β”‚
β”‚ PIR OUT (5V)         β”‚ 10k+20k divider β†’ ESP32 GPIO2            β”‚
β”‚ ESP32 GPIO14         β”‚ 74AHCT125 A2 β†’ Servo signal (orange)     β”‚
β”‚ ESP32 GPIO25         β”‚ L298N IN1                                β”‚
β”‚ ESP32 GPIO26         β”‚ L298N IN2                                β”‚
β”‚ ESP32 GPIO27 (PWM)   β”‚ L298N ENA                                β”‚
β”‚ ESP32 GPIO32         β”‚ L298N IN3                                β”‚
β”‚ ESP32 GPIO33         β”‚ L298N IN4                                β”‚
β”‚ ESP32 GPIO34 (PWM)   β”‚ L298N ENB                                β”‚
β”‚ L298N OUT1,2         β”‚ DC Motor A                               β”‚
β”‚ L298N OUT3,4         β”‚ DC Motor B                               β”‚
β”‚ L298N 5V             β”‚ DFPlayer VCC + 10Β΅F cap+                 β”‚
β”‚ ESP32 GPIO17 (TX2)   β”‚ 1kΞ© β†’ DFPlayer RX                        β”‚
β”‚ DFPlayer TX          β”‚ ESP32 GPIO16 (RX2)                       β”‚
β”‚ DFPlayer SPK1        β”‚ Speaker terminal 1                       β”‚
β”‚ DFPlayer SPK2        β”‚ Speaker terminal 2                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

⚑ Critical Notes:
  - 9V battery GND MUST connect to ESP32 GND (common ground)
  - 74AHCT125 handles BOTH NeoPixel AND servo level-shift
  - HC-SR04 ECHO and PIR output: voltage dividers MANDATORY on ESP32-S3
  - DFPlayer RX 1kΞ© resistor is mandatory
  - OLED I2C now on GPIO4(SDA)/GPIO5(SCL) β€” conflict with GPIO9 resolved

Enter fullscreen mode Exit fullscreen mode

C-3PO: "Download the PDF. All twenty-three connection rows. Printed. Laminated if possible. This is the complete blueprint of R2-D2."

Final Compile πŸš€

βœ“ Compilation successful
  Board:        ESP32-S3 N16R8
  Files:        r2d2-main.ino + 6 headers
  Binary:       634,512 bytes (9.1% of 16MB Flash)
  RAM:          Used 52,416 bytes (16.0% of 327KB)
  PSRAM:        8.0 MB free
  Wi-Fi:        Access Point mode β€” SSID: "R2-D2"
  Dual-core:    Core 0: Wi-Fi/HTTP | Core 1: Real-time control

Enter fullscreen mode Exit fullscreen mode

Click Upload.

The ESP32-S3 boots. The OLED shows "R2-D2 ONLINE." The startup fanfare plays from the speaker. The dome LEDs breathe. The servo sweeps. The Serial Monitor shows:

╔═══════════════════════════════╗
β•‘   R2-D2 β€” COMPLETE BUILD      β•‘
β•‘   ESP32-S3 N16R8              β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
OLED projector online!
Dome servo centred.
Motor driver online.
DFPlayer Mini online!
R2-D2 says: [sound 5]
[WiFi] AP started: R2-D2
[WiFi] IP: 192.168.4.1
[WiFi] HTTP server started β€” connect to R2-D2 network
All systems ONLINE. R2-D2 is ready.
Distance: 95 cm β€” SAFE

Enter fullscreen mode Exit fullscreen mode

Pick up your phone. Connect to the "R2-D2" Wi-Fi network. Password: beepboop. Open a browser. Navigate to 192.168.4.1.

A simple control page appears. Blue on black. R2-D2's face.

Tap "Forward."

The wheels turn.

Tap "Happy."

R2's happy whistle plays.

Tap "Dome β—„."

The dome snaps left.

The Final Milestone 🚩

Milestone: "R2-D2 COMPLETE β€” Episode 8 Final Build"

Enter fullscreen mode Exit fullscreen mode

Every episode. Every system. Every milestone. All saved. All restorable.

The Complete Droid πŸ€–

The workshop is quiet. The crew stands in a circle around R2-D2 β€” the completed droid. Dome rotating. Lights breathing. OLED projecting. Speaker quiet for now, waiting.

OBI-WAN: "You see what has been built. Not just a robot. Not just a collection of components. The process itself is the lesson β€” the way Codey lowered every barrier between the idea and the creation. You described. It wrote. You compiled. It fixed. You drew. It diagrammed. Each episode, a little more complex. Each episode, possible."

LUKE: "Eight episodes. I went from blinking an LED to a Wi-Fi controlled droid with dome lights, sensors, a display, motors, and audio. And I'm not a programmer."

HAN: "Kid, I've seen the Millennium Falcon held together with less intentionality than this thing. And the Falcon can make the Kessel Run."

R2-D2 beeps. Loudly. Repeatedly. Proudly.

C-3PO: "He says β€” and I am translating precisely β€” 'I told you all it would work.'"

A long pause.

C-3PO: "He also says thank you. Which, from Artoo, is quite remarkable."

Obi-Wan smiles.

OBI-WAN: "The Force will be with you. Always."

R2-D2 plays his startup fanfare one more time. The dome lights sweep blue-white. The OLED displays: "MISSION COMPLETE."

The Series Map β€” What We Built πŸ—ΊοΈ

Episode System Board Codey Feature Spotlight
1 First LED blink Arduino UNO R3 Account setup, first sketch, first wiring diagram
2 Piezo sound Arduino UNO R3 Smart Library Picker, tone(), Serial Monitor
3 NeoPixel dome Arduino UNO R3 Voltage safety check, FastLED, multi-file project
4 Sensor eye Arduino UNO R3 Auto Error Fixing, Vision photo upload
5 OLED projector ESP32-S3 N16R8 Board upgrade, Milestones & Rollback, I2C
6 Motion systems ESP32-S3 N16R8 LEDC servo, L298N motors, two-rail wiring
7 Audio voice ESP32-S3 N16R8 DFPlayer Mini, UART, Live Serial Monitor
8 Complete droid ESP32-S3 N16R8 Deep Think mode, Wi-Fi, dual-core, final diagram

πŸ”— Resources

πŸ€– R2D2 Creation with Codey β€” eight episodes, one complete droid, infinite possibilities. The Force β€” and the cloud compiler β€” were with us. Always.

Top comments (0)