Episode 5: The Projector of Hope
"Help Me, Codey Online โ You're My Only Hope" ๐ฝ๏ธ
The workshop is quiet. R2-D2 sits in the corner, dome LEDs softly breathing their blue-white rhythm. Then, suddenly โ a holographic projection flickers from somewhere inside his chassis. Princess Leia. Eighteen centimetres tall. Looping silently.
Luke stares.
LUKE: "That's what I want. That's exactly what I want. An OLED screen mounted in his dome, showing status messages, projecting text like a mission briefing. Something that makes him feel... alive."
R2-D2 beeps with the energy of someone who has been waiting patiently for this episode.
LUKE: "But Obi-Wan said something about upgrading the brain first. Moving to a bigger board."
Obi-Wan's voice arrives like dawn.
OBI-WAN: "The Arduino UNO served us well through four episodes, young one. But what lies ahead โ the motion systems, the voice responses, the wireless capabilities โ these require a more powerful foundation. It is time to meet the ESP32-S3 N16R8."
๐๏ธ SIPOC โ The Great Upgrade
| Suppliers | Inputs | Process | Outputs | Customers |
|---|---|---|---|---|
| You (the maker) | "Migrate to ESP32-S3 N16R8 and add SSD1306 OLED via I2C" | Codey recognises the new board, updates pin mappings, rewrites I2C addresses, flags 3.3V implications | Correct ESP32-S3 firmware with Adafruit_SSD1306, new I2C pins | ESP32-S3 N16R8 โ which drives the OLED and all previous systems |
| Codey Voltage Safety Check | NeoPixel (5V logic), OLED (3.3V or 5V), HC-SR04 (5V) vs ESP32-S3 (3.3V GPIO) | Cross-references component voltage requirements with ESP32-S3 GPIO levels | Specific warnings per component + recommended level shifter or workarounds | You โ who avoid smoke on this far more expensive board |
| SSD1306 OLED (128ร64) | 3.3V power, I2C (SDA/SCL) | Receives display commands over I2C protocol | A crisp 128ร64 pixel display showing R2 status and messages | R2-D2's dome โ the holographic projection system |
| Milestones & Rollback | Previous working code at Episode 4 milestone | One-click restore of code + chat history | Known-good state if the board migration causes issues | You โ who can always go back |
Meet the New Brain: ESP32-S3 N16R8 ๐ง
C-3PO emerges carrying a padded case like it contains the Holy Grail.
C-3PO: "The ESP32-S3 N16R8. I have computed its specifications forty-seven times. Allow me to translate from technical to comprehensible:"
| Feature | ESP32-S3 N16R8 | Arduino UNO R3 |
|---|---|---|
| Processor | Dual-core Xtensa LX7 @ 240 MHz | Single-core ATmega328P @ 16 MHz |
| Flash storage | 16 MB | 32 KB |
| PSRAM | 8 MB | 0 |
| GPIO pins | 45 | 14 digital |
| Logic voltage | 3.3V | 5V |
| Wi-Fi | Yes (802.11b/g/n) | No |
| Bluetooth | Yes (BLE 5.0 + Classic) | No |
| USB | Native USB-CDC (two ports!) | USB via CH340/FTDI chip |
| PWM | LEDC โ 8 channels, 14-bit resolution | 6 PWM pins, 8-bit |
| DAC | 2ร 8-bit DAC outputs | None |
C-3PO: "Considerably more capable. However โ and I cannot stress this strongly enough โ the GPIO outputs are 3.3V, not 5V. Several of our existing components expect a 5V signal. This is a matter of the gravest concern and I intend to address each one individually."
R2-D2 beeps impatiently.
C-3PO: "Artoo, voltage compatibility is NOT a detail. It is the difference between a functional droid and a very expensive doorstop."
The 3.3V Migration: C-3PO's Component Review โก
Darth Vader's voice rumbles through the ventilation system.
VADER: "Your lack of a level shifter... disturbs me."
C-3PO whirls around, then remembers where he is.
C-3PO: "Quite. Let us review each component's voltage situation with the ESP32-S3:"
Component Voltage Compatibility Table
| Component | VCC Needed | Signal Level Needed | ESP32-S3 GPIO (3.3V) | Action Required |
|---|---|---|---|---|
| SSD1306 OLED | 3.3V or 5V | 3.3V I2C | โ Compatible | None โ OLED runs perfectly on 3.3V |
| Piezo Buzzer | 3.3Vโ5V | 3.3V PWM | โ Compatible | None โ works fine at 3.3V |
| NeoPixel WS2812B | 5V | 5V data signal | โ ๏ธ Marginal | Add 74AHCT125 level shifter OR use 470ฮฉ + 3V3-to-5V shifter |
| HC-SR04 | 5V | 5V ECHO output | โ ๏ธ Risky | The ECHO pin outputs 5V โ use voltage divider on ECHO |
| PIR HC-SR501 | 5V | 5V output | โ ๏ธ Risky | Use voltage divider (10k + 20k) on output pin |
C-3PO: "Codey will generate the level shifter and voltage divider circuits in the wiring diagram. This is precisely the scenario for which Codey's voltage safety checks were designed."
Codey, upon seeing the ESP32-S3 selected, automatically adds voltage warnings for each incompatible component in the project.
Selecting the New Board in Codey ๐
Han Solo demonstrates with the casual confidence of someone who has hot-swapped ship components mid-jump.
HAN: "Simple. In your Codey project, click the board selector at the top. Change it from Arduino UNO R3 to ESP32-S3 N16R8. Done. Codey figures out the rest."
- Click the board name dropdown at the top of the Codey interface
- Select ESP32-S3 N16R8
- Codey immediately shows:
โ ๏ธ Board changed: Arduino UNO R3 โ ESP32-S3 N16R8
This board uses 3.3V GPIO logic.
I've detected components in your project that may be affected:
โข NeoPixel WS2812B (pin 6): Needs level shifter โ see updated wiring diagram
โข HC-SR04 (pins 9/10): ECHO pin outputs 5V โ needs voltage divider
โข PIR HC-SR501 (pin 2): Output is 5V โ needs voltage divider
Recommended: Add 74AHCT125 (quad level shifter) for WS2812B data line.
Piezo buzzer (pin 8): โ Compatible โ no change needed.
Shall I update the code and wiring diagram for the ESP32-S3?
R2-D2 beeps enthusiastically: "YES."
HAN: "See? It flags everything. No surprises."
Before Moving On: Rolling Back If Needed ๐ฉ
Obi-Wan raises a steady hand.
OBI-WAN: "Before this migration, let us invoke the wisdom of Milestones. Our Episode 4 code runs perfectly on the Arduino UNO. If the ESP32-S3 migration encounters difficulty, we must be able to return to that known-good state."
In Codey, click Milestones โ find "R2-D2 Sensor Eye โ Episode 4 Complete" โ click Restore.
Both the code AND the chat history are restored to exactly how they were after Episode 4.
Now make a new milestone before starting the migration:
Milestone: "Pre-ESP32 Migration Checkpoint"
OBI-WAN: "The Force gives you wisdom to try. The Milestone gives you the courage to try boldly."
R2-D2 beeps something along the lines of "can we please just look at the OLED already."
Prompting Codey for the ESP32-S3 Migration + OLED ๐ฌ
In Plan mode first:
We're upgrading R2-D2's brain from Arduino UNO R3 to ESP32-S3 N16R8.
Existing systems to migrate:
- Piezo buzzer on pin 8 โ keep same pin, use LEDC channel instead of tone()
- NeoPixel ring (12 LEDs) on pin 6 โ keep pin, add note about 74AHCT125
- HC-SR04 on pins 9/10 โ keep pins, add voltage divider on ECHO
- PIR sensor on pin 2 โ keep pin, add voltage divider on output
New addition:
- SSD1306 OLED 128x64 display via I2C
- ESP32-S3 I2C default: SDA=GPIO8, SCL=GPIO9
- Show these screens:
1. Startup: "R2-D2 ONLINE" with a spinning animation
2. Idle: distance reading + "SAFE" or "ALERT"
3. Motion: "MOTION DETECTED" with exclamation marks
4. Very close: "TOO CLOSE" with a blinking border
Show me the plan before writing any code.
Codey Plan Mode Response
R2-D2 ESP32-S3 Migration + Holographic Projector โ Plan
FILES:
r2d2-main.ino โ setup() and loop(), board init
animations.h โ NeoPixel dome animations (migrated to ESP32)
sensors.h โ HC-SR04 + PIR (voltage divider notes)
display.h โ SSD1306 OLED functions (new)
sounds.h โ LEDC-based tone functions (replaces tone() API)
KEY CHANGES:
1. tone() โ ledc functions (ESP32 uses LEDC peripheral for PWM)
ledcSetup(0, 2637, 8) + ledcWrite(0, 128) replaces tone(pin, freq)
2. I2C on ESP32-S3: Wire.begin(8, 9) for SDA=GPIO8, SCL=GPIO9
(Adafruit_SSD1306 uses Wire automatically)
3. NeoPixel: Same FastLED/NeoPixel API โ ESP32-S3 natively supports
RMT peripheral for WS2812B. Add 74AHCT125 recommendation in wiring.
4. HC-SR04 ECHO: Add voltage divider (10kฮฉ + 20kฮฉ) to reduce 5Vโ3.3V
TRIG output from ESP32-S3 (3.3V) is sufficient to trigger HC-SR04.
5. PIR output: Add voltage divider (10kฮฉ + 20kฮฉ)
DISPLAY STATES:
โข BOOT โ "R2-D2 ONLINE", spinner, 2 seconds
โข IDLE โ Distance + status, updates every 500ms
โข SCAN โ "MOTION DETECTED !!", large text
โข ALERT โ "TOO CLOSE" with animated blinking border
Proceed?
Click Proceed. Codey writes all five files.
Generated display.h
// display.h โ R2-D2 Holographic Projector
// SSD1306 OLED 128x64 via I2C
// ESP32-S3: SDA=GPIO8, SCL=GPIO9
#pragma once
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C // Default I2C address for SSD1306
// OLED_RESET = -1 means share the Arduino reset pin (or no reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
bool displayReady = false;
// โโ Boot animation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const char* spinFrames[] = {"|", "/", "-", "\\"};
int spinIdx = 0;
unsigned long lastSpinMs = 0;
unsigned long bootStartMs = 0;
bool bootDone = false;
void initDisplay() {
// ESP32-S3 I2C: SDA=GPIO8, SCL=GPIO9
Wire.begin(8, 9);
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("SSD1306 not found โ check wiring!");
return;
}
displayReady = true;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
bootStartMs = millis();
Serial.println("OLED projector online!");
}
void showBootScreen() {
if (!displayReady || bootDone) return;
unsigned long now = millis();
if (now - lastSpinMs > 150) {
lastSpinMs = now;
spinIdx = (spinIdx + 1) % 4;
display.clearDisplay();
// Title โ large text centred
display.setTextSize(2);
display.setCursor(4, 8);
display.println("R2-D2");
display.setCursor(4, 28);
display.println("ONLINE");
// Spinner bottom right
display.setTextSize(2);
display.setCursor(112, 48);
display.print(spinFrames[spinIdx]);
display.display();
}
if (now - bootStartMs > 2500) {
bootDone = true;
}
}
// โโ Idle screen: distance + status โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
unsigned long lastIdleRefreshMs = 0;
void showIdleScreen(float distance) {
if (!displayReady) return;
unsigned long now = millis();
if (now - lastIdleRefreshMs < 500) return;
lastIdleRefreshMs = now;
display.clearDisplay();
// Distance reading
display.setTextSize(1);
display.setCursor(0, 0);
display.print("DISTANCE:");
display.setTextSize(2);
display.setCursor(0, 12);
if (distance < 500.0f) {
display.print((int)distance);
display.print(" cm");
} else {
display.print("--- cm");
}
// Status label
display.setTextSize(1);
display.setCursor(0, 48);
if (distance < 15.0f) {
display.print("!! TOO CLOSE !!");
} else if (distance < 30.0f) {
display.print("CAUTION");
} else {
display.print("SAFE");
}
display.display();
}
// โโ Motion detected screen โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
void showMotionScreen() {
if (!displayReady) return;
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 8);
display.println("MOTION");
display.setCursor(0, 28);
display.println("DETECTED");
display.setTextSize(3);
display.setCursor(96, 28);
display.print("!!");
display.display();
}
// โโ Alert: blinking border + TOO CLOSE โโโโโโโโโโโโโโโโโโโโโโโโโโ
bool borderVisible = true;
unsigned long lastBorderFlipMs = 0;
void showAlertScreen() {
if (!displayReady) return;
unsigned long now = millis();
if (now - lastBorderFlipMs > 300) {
lastBorderFlipMs = now;
borderVisible = !borderVisible;
}
display.clearDisplay();
if (borderVisible) {
display.drawRect(0, 0, 128, 64, SSD1306_WHITE);
display.drawRect(2, 2, 124, 60, SSD1306_WHITE);
}
display.setTextSize(2);
display.setCursor(8, 16);
display.println("TOO");
display.setCursor(8, 36);
display.println("CLOSE!");
display.display();
}
Generated sounds.h (ESP32 LEDC version)
// sounds.h โ R2-D2 Sound System (ESP32-S3 LEDC)
// Replaces Arduino tone() with ESP32 LEDC peripheral
#pragma once
#define BUZZER_PIN 8 // GPIO8 on ESP32-S3
#define LEDC_CHANNEL 0
#define LEDC_TIMER 8 // 8-bit resolution
bool ledcReady = false;
void initSounds() {
// Configure LEDC channel for buzzer
ledcSetup(LEDC_CHANNEL, 1000, LEDC_TIMER);
ledcAttachPin(BUZZER_PIN, LEDC_CHANNEL);
ledcWrite(LEDC_CHANNEL, 0); // silent
ledcReady = true;
}
void playTone(int frequency) {
if (!ledcReady || frequency <= 0) {
ledcWrite(LEDC_CHANNEL, 0);
return;
}
ledcWriteTone(LEDC_CHANNEL, frequency);
ledcWrite(LEDC_CHANNEL, 128); // 50% duty cycle
}
void stopTone() {
ledcWrite(LEDC_CHANNEL, 0);
}
Generated r2d2-main.ino (ESP32-S3 version)
// r2d2-main.ino โ R2-D2 ESP32-S3 N16R8
// Unifying all systems: sounds, dome lights, sensors, display
#include <Adafruit_NeoPixel.h>
#include "sounds.h"
#include "animations.h"
#include "sensors.h"
#include "display.h"
void setup() {
Serial.begin(115200);
Serial.println("R2-D2 ESP32-S3 boot sequence...");
initSounds();
initAnimations(); // NeoPixel dome
initSensors(); // HC-SR04 + PIR
initDisplay(); // SSD1306 OLED
Serial.println("All systems nominal. R2-D2 is ONLINE.");
}
void loop() {
// Boot animation runs first
if (!bootDone) {
showBootScreen();
return;
}
// Read sensors
float dist = readDistance();
bool motion = checkMotion();
// Update display based on state
if (dist < 15.0f) {
showAlertScreen();
} else if (motion) {
showMotionScreen();
} else {
showIdleScreen(dist);
}
// Update dome animations (non-blocking)
updateAnimationsSensors(dist, motion);
}
The Wiring Diagram โ ESP32-S3 Edition ๐งญ
C-3PO straightens to his full height, relieved.
C-3PO: "Codey has drawn the wiring diagram. I have checked it. It includes the voltage dividers. It includes the level shifter recommendation. It is correct. I am, for the first time this episode, not worried."
R2-D2 Holographic System โ ESP32-S3 N16R8 Wiring
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[ESP32-S3 N16R8]
POWER RAIL (3.3V):
3V3 โโโโฌโโ OLED VCC (3.3V input)
โโโ (100ยตF cap +)
GND โโโโฌโโ OLED GND
โโโ (100ยตF cap โ)
POWER RAIL (5V via USB):
VIN โโโโฌโโ NeoPixel Ring: 5V
โโโ HC-SR04: VCC
โโโ PIR HC-SR501: VCC
GND โโโโฌโโ NeoPixel Ring: GND
โโโ HC-SR04: GND
โโโ PIR HC-SR501: GND
I2C BUS (3.3V โ OLED compatible):
GPIO8 (SDA) โโโโ OLED SDA
GPIO9 (SCL) โโโโ OLED SCL
SIGNALS with PROTECTION:
GPIO6 โโโโ [74AHCT125 level shifter] โโโโ NeoPixel DIN (5V)
GPIO8 โโโโ (100ฮฉ) โโโโโโโโโโโโโโโโโโโโโโโโ Piezo Buzzer +
GPIO9 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ HC-SR04 TRIG
GPIO10 โโโโ [10kฮฉ / 20kฮฉ divider] โโโโโโโโโโโ HC-SR04 ECHO
GPIO2 โโโโ [10kฮฉ / 20kฮฉ divider] โโโโโโโโโโโ PIR OUT
Voltage Dividers for 5V โ 3.3V:
GPIO_pin โโโ (junction) โโโ 20kฮฉ โโโ GND
โโโโโโโโโโโ 10kฮฉ โโโ 5V signal in
Connection Table:
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ From โ To โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ESP32-S3 3V3 โ OLED VCC โ
โ ESP32-S3 GND โ OLED GND โ
โ ESP32-S3 GPIO8 (SDA) โ OLED SDA โ
โ ESP32-S3 GPIO9 (SCL) โ OLED SCL โ
โ ESP32-S3 VIN (5V) โ NeoPixel 5V, HC-SR04 VCC, PIR VCCโ
โ ESP32-S3 GPIO6 โ Level shifter โ NeoPixel DIN โ
โ ESP32-S3 GPIO8 โ 100ฮฉ โ Piezo Buzzer + โ
โ ESP32-S3 GPIO9 โ HC-SR04 TRIG โ
โ HC-SR04 ECHO (5V) โ Voltage divider โ ESP32 GPIO10 โ
โ PIR OUT (5V) โ Voltage divider โ ESP32 GPIO2 โ
โโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โก Critical Notes:
- OLED: 3.3V compatible โ no level shifting needed
- NeoPixel: 5V logic required โ 74AHCT125 MANDATORY
- HC-SR04 ECHO: 5V output โ voltage divider protects ESP32-S3
- ESP32-S3 has two USB ports โ Codey auto-detects the correct one
LUKE: "Download the PDF. Print two copies."
Compile and Upload to ESP32-S3 ๐
The ESP32-S3 N16R8 has two USB ports. Codey knows which one to use:
โ๏ธ Board: ESP32-S3 N16R8
Note: This board has two USB connectors:
โข USB (UART) โ for serial communication / upload via UART bridge
โข USB-OTG (native USB-CDC) โ direct USB communication
Codey will attempt to detect the correct port automatically.
If upload fails, try the other USB connector.
Click Compile:
โ Compilation successful
Board: ESP32-S3 N16R8
Sketch: r2d2-main.ino + 4 headers
Binary: 412,288 bytes (5.9% of 16MB Flash)
RAM: Used 28,492 bytes (8.7% of 327KB)
PSRAM: 8MB available for future expansion
Click Upload โ Select the correct USB port โ Flash completes.
The OLED lights up: "R2-D2" and "ONLINE" with a spinning cursor.
R2-D2 beeps.
Then the idle screen appears: "DISTANCE: 87 cm โ SAFE."
Luke waves his hand in front of the HC-SR04. The screen immediately switches to "TOO CLOSE!" with a blinking border.
LUKE: "It works. He sees me. He's telling me I'm too close."
R2-D2 beeps something that clearly means "yes, please back up."
Save the Milestone ๐ฉ
Milestone: "R2-D2 Holographic Projector โ Episode 5 Complete"
Four systems running. ESP32-S3 humming. The OLED projecting. The dome glowing. The sensors watching.
What's Next: The Motion Systems Activate โ๏ธ
Han Solo slaps the workbench.
HAN: "Okay. He blinks, he beeps, he shows you a screen. But you know what R2-D2 actually does that nobody else can? He moves. He rolls. His dome spins. He navigates. Episode 6 โ we give this droid wheels and a rotating dome. Servo for the dome, DC motors for the base. The wiring diagram is going to get interesting."
R2-D2 beeps the equivalent of "I have been waiting for this my entire existence."
๐ Resources
- ESP32-S3 datasheet: espressif.com/esp32-s3
- SSD1306 library: github.com/adafruit/Adafruit_SSD1306
- 74AHCT125 level shifter: Search "74AHCT125 NeoPixel level shifter"
- Codey Online: codey.online
๐ค R2D2 Creation with Codey โ building the galaxy's greatest droid, one episode at a time. May the Force โ and the cloud compiler โ be with you.
Top comments (0)