To build a responsive remote control car with Raspberry Pi, use the Raspberry Pi Zero 2 W paired with a TB6612FNG dual H-bridge motor driver and a 2S LiPo battery. This specific combination eliminates the massive 1.5V voltage drop inherent to older L298N drivers, keeps the chassis light enough for standard TT gear motors to actually move, and provides enough processing headroom for low-latency UDP control.

This guide provides the exact hardware spec sheet, pin mapping, and a complete, compilable Python UDP server script using the gpiozero library. We will also cover the power architecture required to prevent brownouts and debug the three most common failure modes you will encounter on the bench.

Decision Tree: Choosing Your Motor Driver

The biggest mistake in Pi-based RC builds is picking a motor driver based on price rather than voltage drop and switching frequency. Here is the decision matrix for standard 6V TT motors.

Driver IC Topology Voltage Drop Continuous Current Verdict
L298N Bipolar BJT ~1.5V to 2.0V 2.0A per channel Reject. Wastes battery as heat; starves 6V motors.
DRV8833 MOSFET ~0.2V 1.5A per channel Acceptable, but max input voltage is 10.8V (risky for fully charged 2S LiPo at 8.4V + spikes).
TB6612FNG MOSFET ~0.5V 1.2A (3.2A peak) Default Pick. High efficiency, handles 15V max, perfect match for TT motor stall currents.
Bench Note: TT motors typically stall around 0.8A at 6V. The TB6612FNG's 1.2A continuous rating gives you a 50% safety margin, while its MOSFET design ensures your motors actually see the battery voltage instead of losing it to silicon heat.

Hardware Spec Sheet & Pin Mapping

This build targets the Raspberry Pi Zero 2 W running Raspberry Pi OS Bookworm Lite (64-bit). The Zero 2 W is chosen over the Pi 4 because its lower idle current (~120mA vs ~600mA) drastically extends runtime on a small LiPo pack, and its physical footprint fits standard 2WD acrylic chassis kits without overhanging.

Parts List (2026 Pricing)

  • Compute: Raspberry Pi Zero 2 W (~$15) with soldered 2x20 male header.
  • Motor Driver: TB6612FNG Dual Motor Driver Breakout (Pololu or SparkFun variant, ~$8).
  • Chassis & Motors: Generic 2WD Acrylic Kit with 130-size TT motors and 65mm wheels (~$15).
  • Power: Zeee 2S 7.4V 1300mAh LiPo with XT60 connector (~$18).
  • Regulation: LM2596 Step-Down Buck Converter module (~$3).
  • Wiring: 18 AWG silicone wire for power, 24 AWG stranded for logic.

GPIO Pin Mapping (BCM Numbering)

The TB6612FNG requires a standby (STBY) pin to be pulled HIGH to enable the H-bridges. We handle this via a dedicated GPIO pin rather than tying it to VCC, allowing the Pi to hard-disable the motors during software crashes.

TB6612FNG Pin Pi Zero 2 W GPIO (BCM) Function
VCC3.3V (Pin 1)Logic level reference
GNDGND (Pin 6)Common ground
STBYGPIO 24Enable/Disable driver
PWMAGPIO 12Left motor PWM speed
AIN1GPIO 17Left motor direction 1
AIN2GPIO 27Left motor direction 2
PWMBGPIO 13Right motor PWM speed
BIN1GPIO 22Right motor direction 1
BIN2GPIO 23Right motor direction 2

Power Architecture: Preventing Pi Brownouts

Never power the Pi Zero 2 W directly from the motor driver's 5V output, and never share a raw LiPo feed without regulation. TT motors generate massive inductive voltage spikes when reversing direction, which will corrupt the Pi's SD card or trigger the onboard brownout detector (lightning bolt icon).

  1. Primary Source: Connect the 2S LiPo (8.4V full, 6.0V empty) to the TB6612FNG VMOT pin and the LM2596 buck converter input.
  2. Regulation: Use a multimeter to adjust the LM2596 potentiometer until the output reads exactly 5.1V. Do this before connecting the Pi.
  3. Decoupling: Solder a 470µF electrolytic capacitor across the buck converter's output terminals. This acts as a local energy reservoir to absorb motor-induced voltage sags.
  4. Connection: Feed the 5.1V output directly to the Pi's 5V and GND GPIO pins (bypassing the USB micro port's polyfuse to eliminate an unnecessary voltage drop point).
Lithium Safety: Never use a raw 2S LiPo without a dedicated balance charger. Ensure your LiPo has a built-in BMS or use an external low-voltage alarm set to 6.4V to prevent permanent cell damage from over-discharge.

The Control Code: UDP Server with gpiozero

We use raw UDP sockets instead of TCP or HTTP/WebSockets for the control link. UDP eliminates the handshake overhead and TCP retransmission delays that cause 'laggy' steering. The Pi listens for JSON payloads containing left and right motor speeds (-1.0 to 1.0).

Target Board: Raspberry Pi Zero 2 W | OS: Bookworm Lite | Library: gpiozero (pre-installed)

import socket
import json
from gpiozero import Motor, PWMOutputDevice
from signal import pause

# --- PIN DEFINITIONS (BCM) ---
# Left Motor (A)
LEFT_FWD = 17
LEFT_BWD = 27
LEFT_PWM = 12

# Right Motor (B)
RIGHT_FWD = 22
RIGHT_BWD = 23
RIGHT_PWM = 13

# Driver Enable
STBY_PIN = 24

# Initialize Motors (gpiozero handles PWM on the enable pins)
left_motor = Motor(forward=LEFT_FWD, backward=LEFT_BWD, pwm=True)
right_motor = Motor(forward=RIGHT_FWD, backward=RIGHT_BWD, pwm=True)

# TB6612FNG requires STBY HIGH to operate
stby = PWMOutputDevice(STBY_PIN)
stby.on()

UDP_IP = '0.0.0.0'
UDP_PORT = 5005

def setup_socket():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # SO_REUSEADDR prevents Errno 98 on quick script restarts
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((UDP_IP, UDP_PORT))
    return sock

def drive(left_speed, right_speed):
    # Clamp values to valid gpiozero range
    left_speed = max(-1.0, min(1.0, left_speed))
    right_speed = max(-1.0, min(1.0, right_speed))

    if left_speed >= 0:
        left_motor.forward(left_speed)
    else:
        left_motor.backward(abs(left_speed))

    if right_speed >= 0:
        right_motor.forward(right_speed)
    else:
        right_motor.backward(abs(right_speed))

if __name__ == '__main__':
    try:
        sock = setup_socket()
        print(f'Listening on UDP {UDP_IP}:{UDP_PORT}')
        while True:
            data, addr = sock.recvfrom(1024)
            try:
                cmd = json.loads(data.decode('utf-8'))
                drive(cmd.get('L', 0), cmd.get('R', 0))
            except json.JSONDecodeError:
                print('Invalid JSON payload received')
    except OSError as e:
        print(f'Socket Error: {e}')
    except KeyboardInterrupt:
        print('\nStopping motors and exiting.')
    finally:
        # Safe shutdown sequence
        left_motor.stop()
        right_motor.stop()
        stby.off()  # Hard disable the H-bridges
        print('Motors disabled. STBY pulled LOW.')

Debugging: When the Motors Don't Spin

If you send UDP packets but the chassis sits dead, do not start rewriting code. Hardware and permission faults account for 95% of Pi RC failures.

The First Three Things to Check

  1. STBY Pin State: Measure the voltage on the TB6612FNG STBY pin with a multimeter. It must read ~3.3V. If it reads 0V, your H-bridges are internally disabled. Check your GPIO 24 wiring.
  2. VMOT vs VCC: Ensure the LiPo power is connected to VMOT (motor power), not VCC (logic power). VCC must connect to the Pi's 3.3V pin. Swapping these will instantly fry the Pi's 3.3V regulator.
  3. UFW Firewall: If the Python script runs but the Pi ignores your PC's UDP packets, the Pi's firewall is blocking port 5005. Run sudo ufw allow 5005/udp.

Exact Error Strings & Ranked Causes

Error 1: OSError: [Errno 98] Address already in use

  • Cause A (Most Likely): A ghost instance of your Python script is still running in the background from a previous SSH session, holding port 5005. Fix: Run sudo lsof -i :5005 and kill the PID.
  • Cause B: Missing SO_REUSEADDR socket option (already included in the provided code to prevent this).

Error 2: gpiozero.exc.GPIOPinInUse: pin 17 is already in use

  • Cause A: Another library (like RPi.GPIO) or a background service (like pigpiod) has claimed the pin. Fix: Stop conflicting services via sudo systemctl stop pigpiod.
  • Cause B: You wired a peripheral to the physical pin but mapped the wrong BCM number in the code. Verify BCM vs Physical pinout.

Extending or Simplifying the Build

Depending on your end goal, you can strip this build down to its bare essentials or scale it up into an FPV rover.

How to Simplify (The 'Weekend Cruiser' Route)

If you want to eliminate the buck converter and LiPo safety overhead, swap the 2S LiPo and LM2596 for a standard 5V 2A USB power bank. Feed the power bank's USB cable directly into the Pi Zero 2 W's micro-USB port. Trade-off: You must wire the TB6612FNG VMOT to a separate 4x AA battery pack (6V). You cannot run the motors and the Pi from the same 5V USB power bank; the motor spikes will reboot the Pi instantly.

How to Extend (The 'FPV Rover' Route)

To add camera-based First Person View (FPV) steering:

  1. Upgrade to a Raspberry Pi 4 (2GB) to handle H.264 video encoding without dropping UDP control packets.
  2. Add a Pi Camera Module V2 and use the libcamera-vid pipeline to stream video over TCP port 8554.
  3. Swap the fixed TT motors for continuous-rotation servos controlled via a PCA9685 I2C PWM board. This frees up the Pi's hardware PWM pins and provides much finer low-speed control than DC gear motors.

For authoritative reference on motor class implementations, consult the gpiozero Motor documentation. For detailed electrical characteristics and truth tables for the driver IC, review the Pololu TB6612FNG breakout datasheet. Always verify your power supply tolerances against the official Raspberry Pi power supply guidelines before applying voltage to the GPIO header.