R2D2 Creation with Codey π€ Ep.7
Source: Dev.to
Episode 7: The Voice of the Galaxy
βArtoo, I Wish We Didnβt Have to Listen to You Sometimesβ π
C-3PO enters the workshop clutching a small speaker and a DFPlayer Mini module.
C-3PO: βI want to state, for the record, that I have mixed feelings about this episode. On one hand, R2-D2βs communication through audio files will be considerably clearer than whatever he has been doing with that piezo buzzer. On the other hand, once we enable full audio playback, he will never. Stop. Talking.β
R2-D2 beeps at considerable volume.
C-3PO: βSee? Heβs already celebrating. This is going to be exhausting.β
Han Solo leans against the doorframe.
HAN: βWhat he said. Letβs wire it up anyway.β
ποΈ SIPOC β The Voice System
Suppliers Inputs Process Outputs Customers
You (the maker) βAdd DFPlayer Mini on UART2, microSD with R2-D2 sounds, trigger by sensor eventsβ Codey picks DFRobotDFPlayerMini library, writes Serial2 init code, maps sounds to events Complete voice.h β sound triggers for every R2-D2 state ESP32-S3 UART2 β DFPlayer Mini β Speaker
DFPlayer Mini 5V power, RX/TX from ESP32-S3 UART2 Reads MP3/WAV files from microSD by number, decodes audio Amplified audio signal to speaker Speaker β which produces actual R2-D2 sounds
microSD card (β€32GB FAT32) MP3 or WAV files named 0001.mp3, 0002.mp3β¦ DFPlayer reads by file number Audio playback Speaker
Live Serial Monitor ESP32-S3 Serial.print() output at 115200 baud Shows state transitions, sound triggers, distance readings in real time A running log of what R2-D2 is doing and thinking You β debugging without a logic analyser
Codey Smart Library Picker βDFPlayer Miniβ mentioned in prompt Automatically includes DFRobotDFPlayerMini.h and the correct library Code that compiles first time with correct API calls You β no library hunting required
The Components π§
Yoda examines the DFPlayer Mini with one ear raised.
YODA: βA small board. Powerful it is. Speaks the language of MP3, it does. Talks to the ESP32 through UART, it does. Patient with the timing, you must be β 200 milliseconds after power-on, it needs.β
Component Quantity Notes
ESP32-S3 N16R8 1 Our brain
DFPlayer Mini 1 MP3/WAV player module, 3V3β5V
Speaker (4Ξ© or 8Ξ©, 0.5Wβ3W) 1 Connect directly to DFPlayer SPK1/SPK2
microSD card (β€32GB) 1 Format FAT32, files named 0001.mp3 etc.
1kΞ© resistor 1 On DFPlayer RX line β prevents voltage noise
10Β΅F capacitor (electrolytic) 1 Between DFPlayer VCC and GND β power filtering
Jumper wires 5
Preparing the microSD Card
Format your microSD as FAT32. Create a folder called mp3 at the root. Name your files:
/mp3/
0001.mp3 β R2-D2 "happy" whistle sequence
0002.mp3 β R2-D2 "alert" rapid beeps
0003.mp3 β R2-D2 "sad" descending moan
0004.mp3 β R2-D2 "question" rising whistle
0005.mp3 β R2-D2 "startup" fanfare
0006.mp3 β R2-D2 "motion detected" warble
0007.mp3 β R2-D2 "danger close" urgent alarm
Enter fullscreen mode
Exit fullscreen mode
C-3PO: βI would suggest downloading these from any reputable Star Wars sound effects collection. The DFPlayer cares only about the filenames β it does not evaluate artistic merit.β
R2-D2 beeps indignantly.
C-3PO: βI am sure your sounds are excellent, Artoo.β
The Codey Prompt ποΈ
Luke sits at the keyboard.
LUKE: βI love how this works now. I just describe what I want and Codey handles the library, the setup, all of it.β
In Agent mode:
We're continuing R2-D2 on ESP32-S3 N16R8.
Add a DFPlayer Mini MP3 module for R2-D2 voice responses:
- DFPlayer connected to ESP32-S3 UART2
- TX2 = GPIO17 β DFPlayer RX (through 1kΞ© resistor)
- RX2 = GPIO16 β DFPlayer TX
- DFPlayer powered from L298N 5V rail
- 10Β΅F capacitor across DFPlayer VCC/GND
- Speaker connected to DFPlayer SPK1 and SPK2 (no extra amp needed)
Sound file mapping on microSD (/mp3/ folder):
1 = R2 happy whistle (play when idle, randomly every 30-60 seconds)
2 = R2 alert beeps (play when object
And use Serial2 on the ESP32-S3 for the DFPlayer communication.
Serial2 on ESP32-S3: RX=GPIO16, TX=GPIO17 by default.
Generating voice.h now...
Enter fullscreen mode
Exit fullscreen mode
Generated voice.h
// voice.h β R2-D2 Voice System
// DFPlayer Mini on ESP32-S3 UART2 (TX=GPIO17, RX=GPIO16)
// microSD: /mp3/0001.mp3 through 0007.mp3
#pragma once
#include
// ββ DFPlayer setup βββββββββββββββββββββββββββββββββββββββββββββββ
DFRobotDFPlayerMini dfPlayer;
bool dfPlayerReady = false;
// ββ Sound IDs (match filenames on microSD) βββββββββββββββββββββββ
enum R2Sound {
SOUND_HAPPY = 1,
SOUND_ALERT = 2,
SOUND_SAD = 3,
SOUND_QUESTION = 4,
SOUND_STARTUP = 5,
SOUND_MOTION = 6,
SOUND_DANGER = 7,
};
// ββ Random idle chatter timing βββββββββββββββββββββββββββββββββββ
unsigned long nextIdleChatterMs = 0;
const unsigned long CHATTER_MIN = 30000UL; // 30 seconds
const unsigned long CHATTER_MAX = 60000UL; // 60 seconds
void scheduleNextChatter() {
nextIdleChatterMs = millis() + CHATTER_MIN
+ (unsigned long)(random(CHATTER_MAX - CHATTER_MIN));
}
// ββ Initialise DFPlayer ββββββββββββββββββββββββββββββββββββββββββ
bool initVoice() {
// ESP32-S3 UART2: RX=GPIO16, TX=GPIO17
Serial2.begin(9600, SERIAL_8N1, 16, 17);
// DFPlayer needs 200ms to settle after Serial begin
delay(200);
if (!dfPlayer.begin(Serial2, /*isACK=*/true, /*doReset=*/true)) {
Serial.println("DFPlayer Mini not detected β check wiring!");
Serial.println(" β’ Is the microSD inserted?");
Serial.println(" β’ Is the 1kΞ© resistor on the DFPlayer RX line?");
Serial.println(" β’ Is VCC on the 5V rail?");
return false;
}
dfPlayer.volume(25); // 0β30 (25 = ~83% volume β plenty for a droid)
dfPlayerReady = true;
Serial.println("DFPlayer Mini online! R2-D2 voice system ready.");
// Play startup fanfare
playSound(SOUND_STARTUP);
scheduleNextChatter();
return true;
}
// ββ Play a sound βββββββββββββββββββββββββββββββββββββββββββββββββ
void playSound(R2Sound sound) {
if (!dfPlayerReady) return;
dfPlayer.play(sound);
Serial.print("R2-D2 says: [sound ");
Serial.print(sound);
Serial.println("]");
}
// ββ Stop current playback ββββββββββββββββββββββββββββββββββββββββ
void stopSound() {
if (!dfPlayerReady) return;
dfPlayer.stop();
}
// ββ Update: called every loop β handles idle chatter ββββββββββββ
void updateVoice(float distance, bool motion, DomeState domeState) {
if (!dfPlayerReady) return;
unsigned long now = millis();
// DFPlayer print errors to Serial β check occasionally
if (dfPlayer.available()) {
uint8_t type = dfPlayer.readType();
int value = dfPlayer.read();
if (type == DFPlayerError) {
Serial.print("DFPlayer error: ");
Serial.println(value);
}
}
// Danger: object very close
if (distance 4000) {
playSound(SOUND_ALERT);
lastAlertMs = now;
}
return;
}
// Motion: play question on first detection
if (motion) {
static bool motionSoundPlayed = false;
if (!motionSoundPlayed) {
playSound(SOUND_QUESTION);
motionSoundPlayed = true;
}
return;
} else {
static bool motionSoundPlayed = false;
motionSoundPlayed = false;
}
// Idle chatter: random happy whistle
if (now >= nextIdleChatterMs) {
playSound(SOUND_HAPPY);
scheduleNextChatter();
}
}
void initVoiceSystem() {
randomSeed(esp_random()); // True random seed from ESP32 hardware RNG
initVoice();
}
Enter fullscreen mode
Exit fullscreen mode
Updated r2d2-main.ino
#include
#include "sounds.h" // LEDC buzzer (backup only, mostly replaced)
#include "animations.h" // NeoPixel dome
#include "sensors.h" // HC-SR04 + PIR
#include "display.h" // SSD1306 OLED
#include "motion.h" // Servo + motors
#include "voice.h" // DFPlayer Mini
void setup() {
Serial.begin(115200);
Serial.println("βββββββββββββββββββββββββββββββ");
Serial.println(" R2-D2 ESP32-S3 Boot Sequence");
Serial.println("βββββββββββββββββββββββββββββββ");
initSounds();
initAnimations();
initSensors();
initDisplay();
initMotion();
initVoiceSystem(); // β new
Serial.println("All systems ONLINE. R2-D2 is ready.");
}
void loop() {
if (!bootDone) { showBootScreen(); return; }
float dist = readDistance();
bool motion = checkMotion();
// ββ Motion (dome + wheels) ββββββββββββββββββββββββββββββββββ
if (dist < 30.0f) {
domeSnap(90);
motorStop();
} else if (motion) {
domeSnap(45);
} else {
domeIdleSweep();
}
// ββ Display βββββββββββββββββββββββββββββββββββββββββββββββββ
if (dist < 15.0f) showAlertScreen();
else if (motion) showMotionScreen();
else showIdleScreen(dist);
// ββ Dome lights βββββββββββββββββββββββββββββββββββββββββββββ
updateAnimationsSensors(dist, motion);
// ββ Voice ββββββββββββββββββββββββββββββββββββββββββββββββββββ
updateVoice(dist, motion, currentState);
}
Enter fullscreen mode
Exit fullscreen mode
Wiring Diagram β Adding the Voice Module π§
C-3PO presents the updated diagram with a satisfied nod.
C-3PO: βThe DFPlayer Mini connects to UART2 on the ESP32-S3. Note the 1kΞ© resistor on the RX line of the DFPlayer β this prevents communication noise that can cause the module to behave erratically. I am noting this specifically because I have personally witnessed the consequences of omitting it, and it is not pleasant.β
R2-D2 Voice System Addition β ESP32-S3 N16R8
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
DFPLAYER MINI CONNECTIONS:
L298N 5V ββββ DFPlayer VCC
L298N 5V ββββ 10Β΅F cap + (filter)
GND ββββ DFPlayer GND + 10Β΅F cap β
ESP32 GPIO17 ββββ (1kΞ©) ββββ DFPlayer RX
DFPlayer TX ββββ ESP32 GPIO16
DFPlayer SPK1 ββββ Speaker terminal 1
DFPlayer SPK2 ββββ Speaker terminal 2
MicroSD Card:
Inserted directly into DFPlayer Mini SD slot
Formatted FAT32, files in /mp3/ folder
Color code (additions):
PINK = DFPlayer TX (audio data from module)
CYAN = DFPlayer RX (commands to module)
GREY = Through 1kΞ© resistor on DFPlayer RX
Connection Table (additions):
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β From β To β
ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ€
β L298N 5V β DFPlayer VCC β
β L298N 5V β 10Β΅F cap: + (long leg) β
β GND (common) β DFPlayer GND + 10Β΅F cap: β β
β ESP32 GPIO17 (TX2) β 1kΞ© resistor β DFPlayer RX β
β DFPlayer TX β ESP32 GPIO16 (RX2) β
β DFPlayer SPK1 β Speaker terminal 1 β
β DFPlayer SPK2 β Speaker terminal 2 β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββ
β‘ Notes:
- DFPlayer powered from 5V β DO NOT use ESP32 3V3 (insufficient current)
- 1kΞ© resistor on DFPlayer RX is mandatory β prevents communication errors
- Speaker: 4Ξ© or 8Ξ©, 0.5W minimum β DFPlayer can drive it directly
- MicroSD must be FAT32, files named 0001.mp3β0007.mp3 in /mp3/ folder
Enter fullscreen mode
Exit fullscreen mode
The Live Serial Monitor β Watching R2 Think π
After uploading, open Codeyβs Live Serial Monitor at 115200 baud.
As R2 runs:
Serial Monitor β 115200 baud
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ
R2-D2 ESP32-S3 Boot Sequence
βββββββββββββββββββββββββββββββ
OLED projector online!
Dome servo centred.
Motor driver online.
DFPlayer Mini online! R2-D2 voice system ready.
R2-D2 says: [sound 5] β Startup fanfare!
All systems ONLINE. R2-D2 is ready.
Distance: 124 cm β SAFE
Distance: 119 cm β SAFE
R2-D2 says: [sound 1] β Idle chatter (random)
Motion detected β scan mode!
R2-D2 says: [sound 4] β Question whistle
State β SCAN
Distance: 28 cm β CAUTION
R2-D2 says: [sound 2] β Alert!
Distance: 12 cm β DANGER
R2-D2 says: [sound 7] β Danger alarm!
State β ALERT
Distance: 85 cm β SAFE β Object moved away
Enter fullscreen mode
Exit fullscreen mode
HAN: βLook at that. Every event logged. Every sound triggered. You can see exactly what heβs thinking. Thatβs not just a droid β thatβs a droid with a diary.β
R2-D2 beeps something that sounds like βI would prefer more privacy, actually.β
Compile and Upload π
β Compilation successful
Board: ESP32-S3 N16R8
Sketch: r2d2-main.ino + 6 headers
Binary: 554,768 bytes (8.0% of 16MB Flash)
RAM: Used 41,240 bytes (12.6% of 327KB)
PSRAM: 7.9 MB free β plenty for Episode 8 additions
Enter fullscreen mode
Exit fullscreen mode
The ESP32-S3 boots. The OLED displays βR2-D2 ONLINE.β The dome LEDs breathe blue-white. The dome servo sweeps gently.
Then β from the speaker β a clear, unmistakable R2-D2 startup whistle.
C-3PO: quietly ββ¦Oh my.β
R2-D2 β the real one β goes completely silent. Then a single, emotional beep.
LUKE: βHe recognises it. He recognises his own voice.β
Save Milestone π©
Milestone: "R2-D2 Voice System β Episode 7 Complete"
Enter fullscreen mode
Exit fullscreen mode
Six systems. All running. All talking to each other. R2-D2 has lights, sound, a display, sensors, motion, and a voice. There is one episode left.
Whatβs Next: The Complete Droid π€
The entire Star Wars crew assembles in the workshop. Han. Luke. Obi-Wan. C-3PO. And there, in the centre β R2-D2, all systems operational, blue dome glowing, OLED projecting status, dome rotating in a gentle scan, audio clips playing softly.
Obi-Wan speaks.
OBI-WAN: βIn the final episode, we bring every system together into a single, cohesive, complete R2-D2. We explore the ESP32-S3βs Wi-Fi capabilities. We use Codeyβs Deep Think mode for the most complex orchestration code. We draw the final complete wiring diagram β every component, every wire, every protection circuit. And we save the last milestone. The droid is nearly complete.β
R2-D2 plays sound 1: his happy whistle.
Everyone smiles.
π Resources
DFRobotDFPlayerMini library: github.com/DFRobot/DFRobotDFPlayerMini
ESP32-S3 UART: docs.espressif.com/esp32-s3/uart
Codey Live Serial Monitor: 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.