The TB6612FNG is the small, efficient alternative to the L298N for robot cars: its MOSFET outputs lose only about half a volt, and it stays cool without a heatsink.
With an ESP32 DevKit V1 the logic side runs from 3V3, the motors from a separate battery on VM, and the speed inputs get 20 kHz PWM from the ESP32’s LEDC peripheral — silent, unlike the audible whine of an Uno’s ~1 kHz PWM. All seven control pins (D18, D19, D25–D27, D32, D33) are chosen so nothing twitches while the ESP32 boots. The sketch ramps both motors up, brakes, spins in place and goes to standby.
Wiring
| ESP32 DevKit V1 | TB6612FNG dual DC motor driver | Wire | Notes |
|---|---|---|---|
| — | A01 | — | To Motor A (+) (not drawn) |
| — | A02 | — | To Motor A (−) (not drawn) |
| — | B01 | — | To Motor B (+) (not drawn) |
| — | B02 | — | To Motor B (−) (not drawn) |
| 2× 18650 pack (7.4 V) or 4× AA · + | VM | red | Motor supply, 4.5–13.5 V. 10–100 µF capacitor across VM and GND if the board has none. |
| 2× 18650 pack (7.4 V) or 4× AA · - | GND | black | |
| 3V3 | VCC | red | Logic supply. With VCC at 3.3 V the inputs match the ESP32’s logic exactly. |
| GND | GND | black | Common ground with the battery — required. |
| D25 | PWMA | orange | |
| D26 | AIN1 | yellow | |
| D27 | AIN2 | green | |
| D32 | PWMB | purple | |
| D18 | BIN1 | blue | |
| D19 | BIN2 | cyan | |
| D33 | STBY | white | HIGH = run, LOW = standby (outputs off). |
Motor 4.5–13.5 V on VM; logic 2.7–5.5 V on VCC part, 3.3 V board. Logic follows VCC. Power VCC from 3.3 V on ESP32 and Pico, 5 V on Uno, and every control pin connects directly. The motor supply on VM is separate.
Board quirks
- VCC goes to 3V3, not VIN. The TB6612’s logic follows its VCC, so at 3.3 V every control pin connects directly. (On an Uno you would use 5 V.) VM, the motor supply, is separate and comes from the battery.
ledcAttach(pin, 20000, 8)runs the PWM at 20 kHz, above hearing, so the motors don’t whine. The TB6612 switches up to 100 kHz; an Uno is stuck near 1 kHz on these pins.- All seven control pins avoid the boot-time troublemakers: no D2, D5, D12 or D15 (strapping), and no D14 (it outputs PWM during boot, which would twitch a motor). The driver’s inputs have internal pull-downs, so the motors stay off while the ESP32 starts.
- Don’t power the ESP32 from the motor pack directly. Run it from USB while testing; for a robot, feed VIN from a small 5 V buck converter on the pack, with all grounds joined.
- Limits: 1.2 A per channel continuous (3.2 A peak) and 13.5 V on VM. BO and N20 gear motors are fine; big 12 V motors that stall above 3 A need a bigger driver.
- Both inputs HIGH is a short brake; both LOW lets the motor coast. Labels may read
A01/A02orAO1/AO2— same outputs.
Code
// TB6612FNG dual motor driver on an ESP32 DevKit V1 — 20 kHz PWM (silent) with the LEDC peripheral.
// PWMA → D25, AIN1 → D26, AIN2 → D27 (motor A); PWMB → D32, BIN1 → D18, BIN2 → D19 (motor B); STBY → D33.
// VCC → 3V3 (logic), GND → GND, VM → battery + (4.5–13.5 V), battery − → GND. ESP32 runs from USB.
constexpr uint8_t PWMA = 25, AIN1 = 26, AIN2 = 27;
constexpr uint8_t PWMB = 32, BIN1 = 18, BIN2 = 19;
constexpr uint8_t STBY = 33;
constexpr uint32_t PWM_HZ = 20000; // above hearing: no motor whine (an Uno is stuck near 1 kHz)
constexpr uint8_t PWM_BITS = 8; // duty 0–255
// speed: -255 (reverse) .. 0 (coast) .. 255 (forward)
void motor(uint8_t pwm, uint8_t in1, uint8_t in2, int speed) {
digitalWrite(in1, speed > 0);
digitalWrite(in2, speed < 0);
ledcWrite(pwm, abs(speed));
}
void brake() { // both inputs HIGH + PWM HIGH = short brake
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, HIGH);
digitalWrite(BIN1, HIGH);
digitalWrite(BIN2, HIGH);
ledcWrite(PWMA, 255);
ledcWrite(PWMB, 255);
}
void setup() {
for (uint8_t p : {AIN1, AIN2, BIN1, BIN2, STBY}) {
pinMode(p, OUTPUT);
digitalWrite(p, LOW);
}
ledcAttach(PWMA, PWM_HZ, PWM_BITS); // core 3.x API: the pin is the channel handle
ledcAttach(PWMB, PWM_HZ, PWM_BITS);
digitalWrite(STBY, HIGH); // LOW = standby (outputs off)
Serial.begin(115200);
Serial.println("TB6612FNG test, PWM 20 kHz");
}
void loop() {
Serial.println("Forward, ramping up");
for (int s = 0; s <= 255; s += 5) {
motor(PWMA, AIN1, AIN2, s);
motor(PWMB, BIN1, BIN2, s);
delay(20);
}
delay(1000);
Serial.println("Brake");
brake();
delay(500);
Serial.println("Spin in place (A forward, B reverse)");
motor(PWMA, AIN1, AIN2, 180);
motor(PWMB, BIN1, BIN2, -180);
delay(1500);
Serial.println("Standby");
digitalWrite(STBY, LOW);
delay(2000);
digitalWrite(STBY, HIGH);
}// TB6612FNG dual motor driver on an ESP32 DevKit V1 — 20 kHz PWM (silent) with the LEDC peripheral.
// PWMA → D25, AIN1 → D26, AIN2 → D27 (motor A); PWMB → D32, BIN1 → D18, BIN2 → D19 (motor B); STBY → D33.
// VCC → 3V3 (logic), GND → GND, VM → battery + (4.5–13.5 V), battery − → GND. ESP32 runs from USB.
constexpr uint8_t PWMA = 25, AIN1 = 26, AIN2 = 27;
constexpr uint8_t PWMB = 32, BIN1 = 18, BIN2 = 19;
constexpr uint8_t STBY = 33;
constexpr uint32_t PWM_HZ = 20000; // above hearing: no motor whine (an Uno is stuck near 1 kHz)
constexpr uint8_t PWM_BITS = 8; // duty 0–255
// speed: -255 (reverse) .. 0 (coast) .. 255 (forward)
void motor(uint8_t pwm, uint8_t in1, uint8_t in2, int speed) {
digitalWrite(in1, speed > 0);
digitalWrite(in2, speed < 0);
ledcWrite(pwm, abs(speed));
}
void brake() { // both inputs HIGH + PWM HIGH = short brake
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, HIGH);
digitalWrite(BIN1, HIGH);
digitalWrite(BIN2, HIGH);
ledcWrite(PWMA, 255);
ledcWrite(PWMB, 255);
}
void setup() {
for (uint8_t p : {AIN1, AIN2, BIN1, BIN2, STBY}) {
pinMode(p, OUTPUT);
digitalWrite(p, LOW);
}
ledcAttach(PWMA, PWM_HZ, PWM_BITS); // core 3.x API: the pin is the channel handle
ledcAttach(PWMB, PWM_HZ, PWM_BITS);
digitalWrite(STBY, HIGH); // LOW = standby (outputs off)
Serial.begin(115200);
Serial.println("TB6612FNG test, PWM 20 kHz");
}
void loop() {
Serial.println("Forward, ramping up");
for (int s = 0; s <= 255; s += 5) {
motor(PWMA, AIN1, AIN2, s);
motor(PWMB, BIN1, BIN2, s);
delay(20);
}
delay(1000);
Serial.println("Brake");
brake();
delay(500);
Serial.println("Spin in place (A forward, B reverse)");
motor(PWMA, AIN1, AIN2, 180);
motor(PWMB, BIN1, BIN2, -180);
delay(1500);
Serial.println("Standby");
digitalWrite(STBY, LOW);
delay(2000);
digitalWrite(STBY, HIGH);
}Expected serial output
TB6612FNG test, PWM 20 kHz
Forward, ramping up
Brake
Spin in place (A forward, B reverse)
StandbyTB6612FNG test, PWM 20 kHz Forward, ramping up Brake Spin in place (A forward, B reverse) Standby
Common errors
Motors don’t move but the sketch runs
STBY is LOW or unconnected, VM has no battery, or the battery GND isn’t joined to the ESP32 GND.
ESP32 resets when the motors start
The motor current is pulling the shared supply down. Power the motors only from the battery on VM, add 100 µF across VM–GND, and keep the ESP32 on USB or its own 5 V supply.
One motor runs the wrong way
Swap that motor’s two wires on A01/A02 (or B01/B02), or swap its IN1/IN2 pins in the sketch.