R2D2 Creation with Codey π€ Ep.6
Source: Dev.to
Episode 6: Let the Wookiee Win β R2 Moves!
βWould Somebody Get This Big Walking Carpet Out of My Way?β π¦Ύ
Han Solo enters the workshop with the energy of someone who has been waiting since Episode 1 for this moment.
HAN: βOkay, look. Iβve put up with the blinking LED. The beeps were cute. The screen β fine, useful. But I need this droid to actually MOVE. Because right now heβs sitting there like a very opinionated lamp and I am not impressed.β
R2-D2 beeps at high volume and at length.
HAN: βYeah yeah, I know, youβre very capable. Prove it. Episode 6. Letβs go.β
Somewhere, a Wookiee roars enthusiastically.
HAN: βChewie agrees. Let the droid move.β
ποΈ SIPOC β The Motion System
Suppliers Inputs Process Outputs Customers
You (the maker) βAdd servo for dome rotation on GPIO14, L298N motor driver for wheels on GPIO pins 25-27β Codey writes ESP32-S3 LEDC servo code + L298N motor control, generates motion.h Dome that sweeps left and right, wheels that drive forward/back/turn R2-D2 β who finally lives up to his reputation
ESP32-S3 LEDC PWM 50Hz PWM signal, 3.3V Drives servo signal line (with level shifter) Dome servo at 0Β°, 45Β°, 90Β°, 135Β°, 180Β° SG90 servo β which rotates R2βs dome
L298N Motor Driver 5Vβ12V external power, direction + PWM signals from ESP32-S3 H-bridge controls current direction to each DC motor Forward / backward / left turn / right turn Two DC motors β which drive R2βs wheels
Codey Wiring Diagram Full component list including motor power requirements Draws color-coded diagram with separate power rail for motors Two-rail wiring diagram: logic (3.3V) + motor power You β who correctly separate the motor power from the logic power
The Components π§
Yoda examines the motor driver with a long, thoughtful look.
YODA: βMoving parts, added today we are. Careful, one must be. Motors, hungry for current they are. Directly from the ESP32-S3, power them you must not β destroyed, your microcontroller would be.β
Component Quantity Notes
ESP32-S3 N16R8 1 Our brain from Episode 5
SG90 micro servo 1 For dome rotation β 5V, 3-wire
L298N dual H-bridge motor driver 1 Drives two DC motors, needs separate 5Vβ12V
DC gear motors with wheels 2 3Vβ6V, ~200mA stall current each
74AHCT125 level shifter 1 Servo signal: 3.3V β 5V
9V battery or 2S LiPo 1 Motor power supply β separate from logic
Jumper wires 12 Several gauge sizes
USB cable 1
YODA: βTwo power rails, we shall have. Logic power: USB 5V β 3.3V for ESP32-S3. Motor power: separate battery for L298N and motors. Cross them, you must not.β
The ESP32-S3 and Servo Control: LEDC at 50Hz βοΈ
C-3PO raises a cautionary finger.
C-3PO: βI must explain the servo PWM situation. The Arduino Servo library used the Timer1 hardware. On the ESP32-S3, we use the LEDC peripheral β the same one we used for the buzzer tone in Episode 5, but configured at 50Hz for servo control. A standard hobby servo expects a pulse between 0.5ms and 2.5ms at a 50Hz frequency. Codey handles this calculation automatically.β
HAN: βThree-PO. The servo. Just the servo.β
C-3PO: βYes. Moving on.β
In Agent mode:
We're continuing R2-D2 on ESP32-S3 N16R8.
Add motion systems:
1. SG90 servo for dome rotation:
- Signal on GPIO14 (through 74AHCT125 level shifter)
- 5V power from VIN rail
- Create these movements:
a. idle_scan: slow sweep 60Β°β120Β° and back, 3 second period
b. alert_snap: snap to face sensor direction
c. full_sweep: 0Β° to 180Β° and back when "happy"
2. L298N motor driver for two DC wheels:
- Motor A: IN1=GPIO25, IN2=GPIO26, ENA=GPIO27 (PWM)
- Motor B: IN3=GPIO32, IN4=GPIO33, ENB=GPIO34 (PWM)
- Create: forward(), backward(), turnLeft(), turnRight(), stop()
- Speed: 0-255 mapped to LEDC duty cycle
3. Behavior: when distance = 120) domeSweepDir = -1;
if (currentDomeAngle = 180) { fullSweepDir = -1; }
if (fullSweepAngle <= 0) { fullSweepDone = true; }
setDomeAngle(fullSweepAngle);
}
// ββ L298N MOTOR DRIVER ββββββββββββββββββββββββββββββββββββββββββββ
#define MOTOR_A_IN1 25
#define MOTOR_A_IN2 26
#define MOTOR_A_EN 27 // PWM channel 2
#define MOTOR_B_IN3 32
#define MOTOR_B_IN4 33
#define MOTOR_B_EN 34 // PWM channel 3
#define MOTOR_CHANNEL_A 2
#define MOTOR_CHANNEL_B 3
#define MOTOR_FREQ 5000 // 5kHz PWM for motors
#define MOTOR_RES 8 // 8-bit β 0β255
void initMotors() {
// Direction pins
pinMode(MOTOR_A_IN1, OUTPUT);
pinMode(MOTOR_A_IN2, OUTPUT);
pinMode(MOTOR_B_IN3, OUTPUT);
pinMode(MOTOR_B_IN4, OUTPUT);
// PWM channels for enable pins
ledcSetup(MOTOR_CHANNEL_A, MOTOR_FREQ, MOTOR_RES);
ledcSetup(MOTOR_CHANNEL_B, MOTOR_FREQ, MOTOR_RES);
ledcAttachPin(MOTOR_A_EN, MOTOR_CHANNEL_A);
ledcAttachPin(MOTOR_B_EN, MOTOR_CHANNEL_B);
// Start stopped
ledcWrite(MOTOR_CHANNEL_A, 0);
ledcWrite(MOTOR_CHANNEL_B, 0);
Serial.println("Motor driver online.");
}
void motorForward(uint8_t speed = 180) {
digitalWrite(MOTOR_A_IN1, HIGH); digitalWrite(MOTOR_A_IN2, LOW);
digitalWrite(MOTOR_B_IN3, HIGH); digitalWrite(MOTOR_B_IN4, LOW);
ledcWrite(MOTOR_CHANNEL_A, speed);
ledcWrite(MOTOR_CHANNEL_B, speed);
}
void motorBackward(uint8_t speed = 180) {
digitalWrite(MOTOR_A_IN1, LOW); digitalWrite(MOTOR_A_IN2, HIGH);
digitalWrite(MOTOR_B_IN3, LOW); digitalWrite(MOTOR_B_IN4, HIGH);
ledcWrite(MOTOR_CHANNEL_A, speed);
ledcWrite(MOTOR_CHANNEL_B, speed);
}
void motorTurnLeft(uint8_t speed = 150) {
// Left motor backward, right motor forward = left turn
digitalWrite(MOTOR_A_IN1, LOW); digitalWrite(MOTOR_A_IN2, HIGH);
digitalWrite(MOTOR_B_IN3, HIGH); digitalWrite(MOTOR_B_IN4, LOW);
ledcWrite(MOTOR_CHANNEL_A, speed);
ledcWrite(MOTOR_CHANNEL_B, speed);
}
void motorTurnRight(uint8_t speed = 150) {
// Left motor forward, right motor backward = right turn
digitalWrite(MOTOR_A_IN1, HIGH); digitalWrite(MOTOR_A_IN2, LOW);
digitalWrite(MOTOR_B_IN3, LOW); digitalWrite(MOTOR_B_IN4, HIGH);
ledcWrite(MOTOR_CHANNEL_A, speed);
ledcWrite(MOTOR_CHANNEL_B, speed);
}
void motorStop() {
ledcWrite(MOTOR_CHANNEL_A, 0);
ledcWrite(MOTOR_CHANNEL_B, 0);
digitalWrite(MOTOR_A_IN1, LOW); digitalWrite(MOTOR_A_IN2, LOW);
digitalWrite(MOTOR_B_IN3, LOW); digitalWrite(MOTOR_B_IN4, LOW);
}
void initMotion() {
initServo();
initMotors();
}
Enter fullscreen mode
Exit fullscreen mode
Updated r2d2-main.ino loop
// Loop addition β reactive motion
void loop() {
if (!bootDone) { showBootScreen(); return; }
float dist = readDistance();
bool motion = checkMotion();
// ββ Dome behaviour βββββββββββββββββββββββββββββββββββββββββ
if (dist < 30.0f) {
domeSnap(90); // face forward, something is ahead
motorStop(); // stop wheels β obstacle!
} else if (motion) {
domeSnap(45); // snap left β motion detected
} else {
domeIdleSweep(); // gentle idle scan
}
// ββ Display βββββββββββββββββββββββββββββββββββββββββββββββββ
if (dist < 15.0f) showAlertScreen();
else if (motion) showMotionScreen();
else showIdleScreen(dist);
// ββ Dome lights βββββββββββββββββββββββββββββββββββββββββββββ
updateAnimationsSensors(dist, motion);
}
Enter fullscreen mode
Exit fullscreen mode
The Wiring Diagram β Two Power Rails π§
C-3PO studies the diagram, then exhales slowly.
C-3PO: βCodey has correctly separated the power rails. The logic rail β 3.3V from the ESP32-S3 regulator β powers only the microcontroller and the OLED. The motor rail β the external 9V battery through the L298Nβs onboard 5V regulator β powers the motors and the servo. This is exactly correct. Mixing them would cause voltage drops that would reset the ESP32-S3 every time a motor starts.β
R2-D2 Motion System β Wiring Diagram (ESP32-S3 N16R8)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
POWER RAIL 1 β LOGIC (USB 5V β ESP32-S3 regulator):
USB 5V ββββ ESP32-S3 VIN
ESP32 3V3 ββ OLED VCC
ESP32 GND ββ OLED GND, logic GND rail
POWER RAIL 2 β MOTOR POWER (9V battery):
9V Bat (+) ββ L298N: VS (motor supply)
9V Bat (β) ββ L298N: GND (common ground with ESP32 GND)
L298N: 5V ββ Servo VCC (5V regulated output)
L298N: 5V ββ 74AHCT125: VCC
L298N: GND ββ Servo GND, 74AHCT125 GND
β οΈ IMPORTANT: ESP32 GND and battery GND must be connected together!
SERVO (dome rotation):
ESP32 GPIO14 βββ 74AHCT125 A1 input (3.3V signal in)
74AHCT125 Y1 βββ Servo Signal wire (orange/yellow) β 5V signal out
L298N 5V βββ Servo VCC (red wire)
L298N GND βββ Servo GND (brown/black wire)
MOTORS via L298N:
ESP32 GPIO25 ββββ L298N IN1 (Motor A direction)
ESP32 GPIO26 ββββ L298N IN2 (Motor A direction)
ESP32 GPIO27 ββββ L298N ENA (Motor A PWM speed)
ESP32 GPIO32 ββββ L298N IN3 (Motor B direction)
ESP32 GPIO33 ββββ L298N IN4 (Motor B direction)
ESP32 GPIO34 ββββ L298N ENB (Motor B PWM speed)
L298N OUT1 ββββ DC Motor A: terminal 1
L298N OUT2 ββββ DC Motor A: terminal 2
L298N OUT3 ββββ DC Motor B: terminal 1
L298N OUT4 ββββ DC Motor B: terminal 2
NeoPixel (from Episode 5):
ESP32 GPIO6 ββ 74AHCT125 A2 ββ NeoPixel DIN
Color code:
RED = 5V / VIN power
ORANGE = 9V battery supply
BLACK = GND
PURPLE = 3.3V logic
GREEN = NeoPixel data (level-shifted)
BLUE = Servo signal (level-shifted)
YELLOW = Motor direction signals
WHITE = Motor PWM (speed) signals
Connection Table:
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β From β To β
ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β 9V Battery (+) β L298N: VS β
β 9V Battery (β) β L298N: GND + ESP32 GND (common) β
β L298N: 5V out β Servo VCC (red), 74AHCT125 VCC β
β L298N: GND β Servo GND, 74AHCT125 GND β
β ESP32 GPIO14 β 74AHCT125 A1 β servo signal (5V) β
β ESP32 GPIO6 β 74AHCT125 A2 β NeoPixel DIN (5V) β
β 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,OUT2 β DC Motor A β
β L298N OUT3,OUT4 β DC Motor B β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
Enter fullscreen mode
Exit fullscreen mode
HAN: βTwo power rails. Thatβs important. Iβve seen people blow a microcontroller trying to power motors directly off the GPIO pins. The L298Nβs internal regulator gives you the 5V for the servo and the level shifter. Clean.β
R2-D2 beeps approvingly.
HAN: βYeah, even I have to admit thatβs well done.β
Compile and Watch the Dome Spin π
β Compilation successful
Board: ESP32-S3 N16R8
Sketch: r2d2-main.ino + 5 headers
Binary: 498,244 bytes (7.2% of 16MB Flash)
RAM: Used 34,128 bytes (10.4% of 327KB)
Enter fullscreen mode
Exit fullscreen mode
The dome servo sweeps slowly between 60Β° and 120Β°. R2-D2 scans the room.
Wave your hand in front of the HC-SR04. The dome snaps to 90Β°. The wheels stop. The OLED shows βTOO CLOSE!β
Han Solo watches the dome turn. A long pause.
HAN: ββ¦Okay. Thatβs actually pretty good.β
R2-D2 beeps.
HAN: βI said it was pretty good. Donβt push it.β
Save Milestone π©
Milestone: "R2-D2 Motion Systems β Episode 6 Complete"
Enter fullscreen mode
Exit fullscreen mode
Five systems running. Dome rotating. Wheels ready. The droid is almost complete.
Whatβs Next: R2 Gets His Voice Back π
Obi-Wan speaks with warmth.
OBI-WAN: βThe dome spins. The lights glow. The screen projects. The wheels wait. But something is still missing β the true voice. Not just beeps from a buzzer, but the full audio character of the galaxyβs most beloved droid. In Episode 7, we add the DFPlayer Mini and a speaker. R2-D2 will play actual audio files. And the Live Serial Monitor will show us exactly what is happening.β
R2-D2 emits a long, heartfelt whistle that says everything.
π Resources
ESP32 LEDC (servo): randomnerdtutorials.com/esp32-servo-motor-web-server
L298N motor driver: Search βL298N Arduino ESP32 tutorialβ
74AHCT125 level shifter: Search β74AHCT125 3.3V 5V logic level converterβ
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.