micropython-control-motores-dc-l298n-tb6612

DC Motor Control with MicroPython: L298N and TB6612

  • 6 min

A DC motor is an actuator that rotates when direct current is applied.

Servomotors are great for moving an arm or rotating a camera, but if we want to create a vehicle with wheels, a fan, or a conveyor belt, we need direct current motors.

The problem with DC motors is that they are “dumb” and “hungry”.

  1. Dumb: If you supply power, they spin. If you remove power, they stop (by inertia). They don’t know their position.
  2. Hungry: They consume much more current than a microcontroller pin can provide.

Never connect a motor directly to a GPIO pin An ESP32 or Pico pin delivers at most 20mA-40mA. A small toy motor consumes 200mA unloaded and can reach 1A when stalled. If you connect them directly, you will instantly burn the pin or the processor.

To solve this, we need an intermediary: the Motor Driver.

The H-bridge concept

To control a motor, we need two things:

  1. Speed: Which we will control via PWM (rapidly turning the power on and off).
  2. Direction: To reverse rotation, we need to reverse the polarity of the wires.

Since we can’t physically swap the wires while the robot is moving, we use a transistor circuit called a H-Bridge.

Imagine four switches. Depending on which ones we close, current flows from left to right (clockwise rotation) or from right to left (counter-clockwise rotation).

The most common drivers

In the maker world, there are two undisputed kings, both managed very similarly from the code.

L298N: The classic veteran

This is the large red module with an aluminum heatsink.

  • Pros: Cheap, robust, handles high voltages (up to 35V).
  • Cons: Old technology (BJT). It is very inefficient. It “eats” about 2V from the battery just to run and gets very hot.

TB6612FNG / DRV8833: The modern option

These are small modules.

  • Pros: MOSFET technology. Very efficient, barely heat up, and utilize the battery better.
  • Cons: They are more sensitive to maximum voltages (generally up to 10V-12V).

If you are powering your robot with small batteries or packs (LiPo 1S or 2S), always use a TB6612 or DRV8833. The L298N wastes too much power.

Electrical connection

Let’s assume a standard configuration that works for both drivers. We need 3 microcontroller pins per motor (although we’ll see how to optimize it to 2 later).

  • IN1: Controls one side of the bridge.
  • IN2: Controls the other side.
  • ENA (Enable): Activates or deactivates the motor (sometimes used for PWM).

The ESSENTIAL wiring diagram:

  1. Driver GND connected to Microcontroller GND. (Without this, it won’t work).
  2. Motor power supply (VM) to the external batteries.
  3. Logic power supply (VCC) to the board’s 3.3V or 5V.

Basic control in MicroPython

The logic to move the motor follows this truth table:

IN1IN2Result
LowLowStopped (Coast)
HighLowRotation Direction A
LowHighRotation Direction B
HighHighBrake (Short Brake)

We will implement it by controlling the speed via PWM directly on pins IN1 and IN2, which allows us to save the ENA pin (which we can physically connect to 5V/3.3V to keep it always active).

MotorDC Class

As always, at luisllamas.es we like clean, reusable code. Let’s create a class to govern our motors.

from machine import Pin, PWM

class MotorDC:
    def __init__(self, pin_in1, pin_in2, freq=1000):
        """
        :param pin_in1: GPIO pin connected to IN1 of the driver
        :param pin_in2: GPIO pin connected to IN2 of the driver
        :param freq: PWM frequency (1kHz usually works well to avoid humming)
        """
        self.pwm1 = PWM(Pin(pin_in1))
        self.pwm2 = PWM(Pin(pin_in2))

        self.pwm1.freq(freq)
        self.pwm2.freq(freq)

        # Initialize as stopped
        self.stop()

    def forward(self, speed):
        """
        Moves the motor forward.
        :param speed: Speed in percentage (0 to 100)
        """
        duty = self._percentage_to_duty(speed)
        self.pwm1.duty_u16(duty)
        self.pwm2.duty_u16(0)

    def backward(self, speed):
        """
        Moves the motor backward.
        :param speed: Speed in percentage (0 to 100)
        """
        duty = self._percentage_to_duty(speed)
        self.pwm1.duty_u16(0)
        self.pwm2.duty_u16(duty)

    def stop(self):
        """Stops the motor by coasting (Coast)."""
        self.pwm1.duty_u16(0)
        self.pwm2.duty_u16(0)

    def brake(self):
        """Stops the motor abruptly (Short brake)."""
        self.pwm1.duty_u16(65535)
        self.pwm2.duty_u16(65535)

    def _percentage_to_duty(self, percentage):
        """Converts 0-100 to 0-65535"""
        # Limit between 0 and 100
        percentage = max(0, min(percentage, 100))
        return int((percentage / 100) * 65535)
Copied!

Usage example

Now, let’s test our driver by smoothly accelerating, braking, and reversing the motor.

import time
# from motor_dc import MotorDC  <-- If you saved the class in another file

# Example pins for ESP32: IN1=GPIO26, IN2=GPIO27
motor_left = MotorDC(26, 27)

print("Accelerating forward...")
for vel in range(0, 101, 5): # From 0 to 100% in steps of 5
    motor_left.forward(vel)
    time.sleep(0.1)

print("Braking...")
motor_left.brake()
time.sleep(1)

print("Reverse at half power...")
motor_left.backward(50)
time.sleep(2)

print("Coasting to stop...")
motor_left.stop()
Copied!

Common problems and solutions

When working with motors, physics plays tricks on us that don’t occur when simply turning on an LED.

The microcontroller resets due to brownout

When starting up, motors draw a lot of current suddenly. This can cause a momentary voltage drop that resets your ESP32 or Pico.

  • Solution: Use separate power supplies (one for motors, another for the micro) sharing GND.
  • Solution: Place large electrolytic capacitors (e.g., 470uF) at the power input of the driver.

The motor makes a high-pitched noise and doesn’t spin

If you send a very low speed (e.g., 10%), there may not be enough power to overcome initial friction, but the PWM frequency vibrates the coils.

  • Solution: Increase the minimum start speed (dead zone). Rarely does a cheap DC motor start well below 30% PWM.

Electrical noise

Motors generate sparks and electromagnetic noise that can drive your electronics crazy.

  • Solution: Solder small ceramic capacitors (100nF) between the motor terminals and its casing.

Mastering DC motors is the first step towards building your own rover. Now that we have movement and direction, the next step is learning to use sensors so our robot doesn’t crash into walls.