This project turns a robot chassis into something that actually drives under your own code, combining a controller, a motor driver, and two DC motors into one coordinated machine. It's the first project in the robotics track because it introduces motor control specifically -- an output type that's fundamentally different from switching a light or driving a display.
What the Controller Does
The controller decides where the robot should go and translates that decision into signals the motor driver understands -- a direction for each wheel and a speed for each wheel, updated continuously as the program runs. An ESP32-S3 is used here rather than a smaller board like the ESP32-C3 specifically for its extra GPIO headroom: a motor driver alone needs several pins, and leaving room for a future sensor upgrade (like the line-following project's sensor array) matters from the start.
What the Motor Driver Does
The TB6612FNG sits between the controller and the motors and does the actual work of driving them. It's a MOSFET-based driver, which is genuinely more power-efficient than an older linear driver design like the L298N -- less energy wasted as heat, which matters directly on a battery-powered chassis. The controller never drives the motors' higher-current supply directly; it only ever sends the driver low-power logic signals.
What the Chassis and Motors Do
The chassis holds the two DC gear motors, wheels, and battery box in a fixed physical layout, and translates each motor's rotation into the robot's actual movement -- turning by running each wheel at a different speed or in different directions rather than through any separate steering mechanism.
Motor Driver Wiring Basics
Each motor channel on the TB6612FNG needs two things from the controller: a direction signal (which way should this wheel turn) and a PWM speed signal (how fast). Just as important is what's wired separately: the motor supply -- the 18650 battery pack -- powers the motors themselves, while the controller's own logic power comes from its USB connection. Keeping motor power and logic power on separate rails is standard practice; it protects the controller's more sensitive logic pins from the electrical noise a motor's current draw can create.
Software and Library Concepts
Most motor driver libraries expose a simple function per motor: set a direction and a speed value, and the library handles the underlying PWM and direction-pin logic for you. Start by getting each motor spinning independently and confirm both directions work before writing any higher-level driving logic (forward, turn, stop) on top.
Testing and Troubleshooting
Motors don't turn at all -- check the battery and driver wiring before assuming it's a code bug; a driver with no motor power will accept commands silently and do nothing.
One motor spins the wrong direction -- either swap that motor's two leads at the driver, or flip the direction logic for that motor in code; both fixes are equally valid.
Movement is erratic or weak -- check the battery's charge level and voltage; insufficient power is a very common cause that gets mistaken for a code problem.
Robot drives in a curve instead of straight -- confirm both motors are actually receiving the same speed value, since even small differences between two DC motors can pull a two-wheel chassis off a straight line.
Optional Future Upgrade: Line-Following Robot
This exact chassis and TB6612FNG driver are reused directly in the Line-Following Robot project, which adds a line-sensor array on top of the same drive base -- once you're comfortable with basic motor control here, that's a natural next step rather than a restart.
Wiring
TB6612FNG VM → 18650 battery holder (+) — through the holder's power switch — motor supply, kept separate from logic power
TB6612FNG GND → battery holder (−) AND ESP32-S3 GND — common ground is required across battery, driver, and controller
TB6612FNG VCC → ESP32-S3 3V3 — logic-level supply for the driver chip
TB6612FNG STBY → ESP32-S3 GPIO7 — must be driven HIGH to take the driver out of standby
TB6612FNG AIN1 → ESP32-S3 GPIO4 — motor A direction
TB6612FNG AIN2 → ESP32-S3 GPIO5 — motor A direction
TB6612FNG PWMA → ESP32-S3 GPIO6 — motor A speed/enable
TB6612FNG BIN1 → ESP32-S3 GPIO15 — motor B direction
TB6612FNG BIN2 → ESP32-S3 GPIO16 — motor B direction
TB6612FNG PWMB → ESP32-S3 GPIO17 — motor B speed/enable
TB6612FNG A01/A02 → left DC gear motor (chassis kit) — two wires, either polarity (swap if it spins the wrong way)
TB6612FNG B01/B02 → right DC gear motor (chassis kit)
ESP32-S3 Dev Board → USB-C cable → computer (programming only; run untethered from the battery holder once flashed)
GPIO numbers above are generic safe choices — ESP32-S3 dev boards vary by vendor, so confirm each pin against your specific board's pinout diagram and avoid any pin marked as reserved for flash/PSRAM (typically GPIO26-32 on boards with octal PSRAM) or USB (GPIO19/20 on many S3 boards). This starter code drives PWMA/PWMB pins fully HIGH for constant full-speed motion rather than variable-speed PWM, keeping the example simple and avoiding ambiguity between different ESP32 Arduino core PWM APIs — swap in `ledcAttach()`/`ledcWrite()` speed control once you're ready to go beyond on/off. STBY must be HIGH before the motors will respond at all, and it's worth wiring/testing that pin first when bringing up the driver. Adding a remote input (BLE or Wi-Fi) or a line-sensor add-on (see the line-following-robot project) are natural next steps once this pattern runs reliably.
Wiring Diagram
Starter Code
// Two-Wheel Robot Car — ESP32-S3 + TB6612FNG
// Basic starter: runs a fixed forward/stop/turn test pattern on loop.
// --- Pins — adjust to match your specific ESP32-S3 dev board's pinout ---
#define STBY 7
#define AIN1 4
#define AIN2 5
#define PWMA 6
#define BIN1 15
#define BIN2 16
#define PWMB 17
void motorA(bool forward, bool run) {
digitalWrite(AIN1, run ? (forward ? HIGH : LOW) : LOW);
digitalWrite(AIN2, run ? (forward ? LOW : HIGH) : LOW);
digitalWrite(PWMA, run ? HIGH : LOW); // full speed when running
}
void motorB(bool forward, bool run) {
digitalWrite(BIN1, run ? (forward ? HIGH : LOW) : LOW);
digitalWrite(BIN2, run ? (forward ? LOW : HIGH) : LOW);
digitalWrite(PWMB, run ? HIGH : LOW);
}
void stopMotors() {
motorA(true, false);
motorB(true, false);
}
void driveForward() {
motorA(true, true);
motorB(true, true);
}
void turnLeft() {
// Left motor reverse/stopped, right motor forward = pivot left
motorA(false, true);
motorB(true, true);
}
void turnRight() {
motorA(true, true);
motorB(false, true);
}
void setup() {
pinMode(STBY, OUTPUT);
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(PWMA, OUTPUT);
pinMode(BIN1, OUTPUT);
pinMode(BIN2, OUTPUT);
pinMode(PWMB, OUTPUT);
digitalWrite(STBY, HIGH); // take the driver out of standby
stopMotors();
}
void loop() {
driveForward();
delay(1500);
stopMotors();
delay(500);
turnRight();
delay(600);
stopMotors();
delay(500);
turnLeft();
delay(600);
stopMotors();
delay(1000);
}