Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

TB6612FNG with ESP32 DevKit V1 — 3.3 V logic and silent 20 kHz PWM

Drive two DC motors from an ESP32 DevKit V1 with a TB6612FNG: logic on 3V3, a separate motor battery, 20 kHz LEDC PWM on D25/D32 and boot-safe direction pins.

Arduino C++Verified — core esp32 3.3.12, 3 Nov 2026Compile-only

Community draft — report a mistake

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 V1Only the pins used are shown · 3.3 V logic2× 18650 pack (7.4 V) or 4× AATB6612FNG dual DC motor driver+-D32D33D25D26D27GNDD19D183V3VMVCCGNDA01 → Motor A (+)A02 → Motor A (−)B02 → Motor B (−)B01 → Motor B (+)PWMAAIN2AIN1STBYBIN1BIN2PWMB
Every connection
ESP32 DevKit V1TB6612FNG dual DC motor driverWireNotes
—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 · +VMredMotor 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 · -GNDblack
3V3VCCredLogic supply. With VCC at 3.3 V the inputs match the ESP32’s logic exactly.
GNDGNDblackCommon ground with the battery — required.
D25PWMAorange
D26AIN1yellow
D27AIN2green
D32PWMBpurple
D18BIN1blue
D19BIN2cyan
D33STBYwhiteHIGH = 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/A02 or AO1/AO2 — same outputs.

Code

tb6612-esp32.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// 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

Serial Monitor · 115200Output
TB6612FNG 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.

↑↓ to move · Enter to open