A GPIO pin on the ESP32 can't drive a DC motor directly - it only gives a few milliamps at 3.3 volts, and a motor needs hundreds of milliamps plus the ability to reverse current. The DRV8833 driver solves both problems with two built-in H-bridges, letting you run one or two brushed DC motors from MicroPython using nothing but machine.PWM.
This walks through wiring one motor, then two, so you end up with basic forward, reverse and turn control for something like a small robot car.
The full tutorial, with every detail and the line-by-line code explanation, is on our site: ESP32 MicroPython DRV8833 DC Motor Driver
What you need
- 38-pin ESP32 ESP-WROOM-32 Dev Module - Narrow
- 38-pin ESP32 ESP-WROOM-32 Dev Module - Wide
- 30-pin ESP32 ESP-WROOM-32 Dev Module - Wide
- ESP32 Uno-form board
- ESP32 S3 Uno-form board
- USB Cable Type-A to Type-C (for USB-A PC)
- USB Cable Type-C to Type-C (for USB-C PC)
- 5V DC Motor
- 5V Power Adapter
- DC Power Jack
- Breadboard
- Jumper Wires
- Screw Terminal Expansion Board for ESP32
- Breakout Expansion Board for ESP32
- Power Splitter for ESP32
- DIYables ESP32 Starter Kit (ESP32 included)
- DIYables ESP32 S3 Starter Kit (ESP32 S3 included)
- DIYables Sensor Kit (18 sensors/displays)
Wiring
IN1 and IN2 go to two PWM-capable GPIOs on the ESP32 and drive motor A; IN3 and IN4 do the same for motor B. OUT1/OUT2 and OUT3/OUT4 connect to the motor wires. VCC takes motor power from an external supply (2.7V to 10.8V), such as a 5V adapter through a DC power jack, never from the ESP32's 3.3V or 5V pin.
GND from the supply, the DRV8833 and the ESP32 all need to tie together, otherwise the IN signals have no reference and the motor behaves strangely. Tie EEP (nSLEEP) to the ESP32's 3.3V so the driver stays awake. If a motor spins the wrong way, swap its two OUT wires - no code change needed.
How it works
The DRV8833 has no separate enable pin like the L298N - you send PWM straight into the IN pins to set speed. For channel A, driving IN1 with PWM while IN2 stays LOW spins the motor forward; swapping which pin gets PWM reverses it. Both IN pins LOW lets the motor coast to a stop, and both HIGH brakes it quickly.
In MicroPython, duty_u16() takes 0 to 65535, so 0 means LOW, 65535 means HIGH, and anything in between sets the speed. The chip's output MOSFETs have low resistance, so little power turns into heat and the motor gets nearly the full battery voltage.
Code
The one-motor script sets up GPIO27 and GPIO26 as 1 kHz PWM outputs with machine.PWM and wraps the logic in a motor_a(speed) function that takes -100 to 100, converts it to a duty value, and picks which IN pin gets PWM based on the sign. motor_a_brake() drives both pins HIGH. The main loop ramps the motor up in 20% steps, holds full speed, coasts, reverses at 60%, then brakes, printing each step to the Shell.
The two-motor version adds GPIO33 and GPIO32 for channel B, generalizes the logic into set_channel(), and layers on forward(), backward(), turn_left(), turn_right() and stop() helpers built around one DRIVE_SPEED value.
esp32-micropython-drv8833-dc-motor-driver.py
"""
This ESP32 MicroPython code was developed by newbiely.com
This ESP32 MicroPython code is made available for public use without any restriction
For comprehensive instructions and wiring diagrams, please visit:
https://newbiely.com/tutorials/esp32-micropython/esp32-micropython-drv8833-dc-motor-driver
"""
from machine import Pin, PWM
import time
# ESP32 pins connected to channel A of the DRV8833 module
IN1_PIN = 27 # ESP32 GPIO27 connected to the IN1 pin of the DRV8833
IN2_PIN = 26 # ESP32 GPIO26 connected to the IN2 pin of the DRV8833
PWM_FREQ = 1000 # 1 kHz PWM, the DRV8833 accepts up to 50 kHz
# Both IN pins use PWM, because the DRV8833 has no separate speed (enable) pin
in1 = PWM(Pin(IN1_PIN), freq=PWM_FREQ)
in2 = PWM(Pin(IN2_PIN), freq=PWM_FREQ)
in1.duty_u16(0) # start with the motor stopped (coast)
in2.duty_u16(0)
FULL_DUTY = 65535 # duty_u16() range is 0 to 65535
def percent_to_duty(percent):
return percent * FULL_DUTY // 100 # convert 0-100% to 0-65535
def motor_a(speed):
# speed: -100 to 100 (%). Positive = forward, negative = reverse, 0 = coast
speed = max(-100, min(100, speed))
if speed > 0:
in1.duty_u16(percent_to_duty(speed)) # PWM on IN1
in2.duty_u16(0) # IN2 LOW
elif speed < 0:
in1.duty_u16(0) # IN1 LOW
in2.duty_u16(percent_to_duty(-speed)) # PWM on IN2
else:
in1.duty_u16(0) # IN1 LOW + IN2 LOW = coast (motor rolls to a stop)
in2.duty_u16(0)
def motor_a_brake():
in1.duty_u16(FULL_DUTY) # IN1 HIGH + IN2 HIGH = brake (fast stop)
in2.duty_u16(FULL_DUTY)
while True:
# forward: raise the speed step by step
print("Forward: speeding up")
for speed in range(20, 101, 20):
motor_a(speed)
print(" speed = {}%".format(speed))
time.sleep(0.5)
print("Forward: full speed for 2 seconds")
time.sleep(2)
print("Coast: motor rolls to a stop")
motor_a(0)
time.sleep(2)
print("Reverse: 60% speed for 2 seconds")
motor_a(-60)
time.sleep(2)
print("Brake: fast stop")
motor_a_brake()
time.sleep(1)
motor_a(0) # release the brake before the next loop
print("----")
time.sleep(1)
Things that go wrong
Both IN pins for a channel must sit on PWM-capable GPIOs - almost all ESP32 output pins qualify, but check this first if a motor ignores commands. If nothing moves at all, check EEP: it has to be HIGH or the chip sleeps and ignores every input.
A motor spinning the wrong direction just needs its two OUT wires swapped. Two motors draw more current than one, so size the 5V adapter at 2A or more. Test with the wheels free to spin, since a stalled motor can pull far more current than a spinning one.
Full tutorial
The complete tutorial - wiring diagram, code explanation and troubleshooting - is here: https://newbiely.com/tutorials/esp32-micropython/esp32-micropython-drv8833-dc-motor-driver

Top comments (0)