DEV Community

Cover image for ESP32 MicroPython DRV8833 DC Motor Driver
Arduino ESP32 Raspberry Pi Tutorials
Arduino ESP32 Raspberry Pi Tutorials

Posted on Originally published at newbiely.com

ESP32 MicroPython DRV8833 DC Motor Driver

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

Wiring

ESP32 MicroPython DRV8833 DC Motor Driver wiring diagram

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)
Enter fullscreen mode Exit fullscreen mode

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)