Raspberry Pi Motor Control System Using L298N and Li-ion Battery Pack

Raspberry Pi Motor Control System Using L298N & Li-ion Battery | MakeMindz
Robotics & Embedded Systems

Raspberry Pi Motor Control
with L298N & Li-ion Battery

📅 February 2026 ⏱ ~2 hrs 🎓 Intermediate

Control two DC motors using Raspberry Pi GPIO, an L298N dual H-bridge driver, and an 18650 Li-ion battery pack — ideal for robotics, autonomous vehicles, and STEM automation.

🔧 6 parts
🐍 Python / C++
⚡ PWM control
🔋 18650 Li-ion

📋 Project Overview

The Raspberry Pi sends I2C commands to an Adafruit PCA9685 PWM breakout, which drives the L298N dual H-bridge. The 18650 battery pack powers the motors directly for portable, high-current operation.

Running full Linux alongside motor control means the Pi can handle camera feeds, network comms, and AI inference — all in one board.

ℹ️
Why Raspberry Pi?Runs Linux, OpenCV, and TensorFlow Lite alongside motor control — making it perfect for AI-powered robots and autonomous vehicles.

🔩 Components Required

🖥️
Raspberry Pi
Any model with GPIO — Pi 3B+, 4, or Zero W.
⚙️
L298N Motor Driver
Dual H-Bridge. Controls direction and speed up to 2A each channel.
🔌
2× DC Motors
Standard TT or N20 gear motors, 3V–12V range.
🔋
18650 Li-ion Pack
High-capacity rechargeable. Supplies stable motor power.
📡
PCA9685 Breakout
Adafruit 16-ch PWM driver, I2C at address 0x40.
🧰
Breadboard + Wires
M-F and M-M jumper wires for GPIO connections.

⚡ How It Works

Raspberry Pi Python Script I2C / GPIO PCA9685 16-ch PWM Breakout (I2C) L298N H-Bridge Driver IN1-4 + ENA/ENB Motor 1 OUT1 / OUT2 Motor 2 OUT3 / OUT4 🔋 Battery 18650 Pack → L298N 12V I2C PWM ① Control ② PWM Gen ③ H-Bridge ④ Motors ⑤ Power

The Pi runs Python → I2C to PCA9685 → PWM signals to L298N → motors spin. The 18650 pack powers only the motors; the Pi takes power separately via USB-C.


🔌 Circuit Diagram

raspberry_pi_motor_control_schematic.svg
Raspberry Pi GPIO + I2C Bus SDA (GPIO 2) SCL (GPIO 3) 5V Power GND PCA9685 PWM Breakout (I2C) CH0CH1 CH2CH3 CH4 ENACH5 ENB SDA SCL VCC GND L298N Dual H-Bridge IN1IN2 IN3IN4 ENAENB OUT1 ● OUT2 ● OUT3 ● OUT4 ● 12V • GND • VSS DC Motor M1 DC Motor M2 🔋 18650 Battery Pack 7.4V Li-ion → L298N Power + GND Wire Key I2C (SDA/SCL) Motor PWM signals Enable (ENA/ENB) Power (+/GND)

🔗 Wiring Guide

FromToOnPurpose
GPIO 2 (SDA)SDAPCA9685I2C Data
GPIO 3 (SCL)SCLPCA9685I2C Clock
Pi 5VVCCPCA9685Logic power
Pi GNDGNDPCA9685 + L298NCommon ground
PCA CH0IN1L298NMotor 1 dir A
PCA CH1IN2L298NMotor 1 dir B
PCA CH2IN3L298NMotor 2 dir A
PCA CH3IN4L298NMotor 2 dir B
PCA CH4ENAL298NMotor 1 speed
PCA CH5ENBL298NMotor 2 speed
Battery +12VL298NMotor power
Battery −GNDL298NCommon GND
OUT1 / OUT2Motor 1 terminalsDC Motor 1Drive
OUT3 / OUT4Motor 2 terminalsDC Motor 2Drive
⚠️
Common ground is critical! L298N GND and Raspberry Pi GND must share a common connection. Never power the Pi directly from the 18650 pack without a 5V step-down regulator.

🪜 Step-by-Step Build

1
Gather all components

Collect your Raspberry Pi, L298N, PCA9685 breakout, two DC motors, 18650 battery pack (fully charged), breadboard, and jumper wires.

2
Enable I2C on Raspberry Pi

Run sudo raspi-config → Interface Options → I2C → Enable. Reboot, then verify with i2cdetect -y 1 — you should see address 0x40.

3
Install Python libraries

pip install adafruit-circuitpython-pca9685 RPi.GPIO

4
Wire PCA9685 to Raspberry Pi

SDA → GPIO 2, SCL → GPIO 3, VCC → 5V, GND → GND. This offloads all PWM generation from the Pi's GPIO pins.

5
Connect PCA9685 to L298N

CH0→IN1, CH1→IN2, CH2→IN3, CH3→IN4, CH4→ENA, CH5→ENB. Double-check polarity before powering on.

6
Connect DC motors

Motor 1 → OUT1/OUT2. Motor 2 → OUT3/OUT4. Swapping wires on a motor reverses its spin direction.

7
Wire the 18650 battery pack

Battery positive (+) → L298N 12V. Battery negative (−) → L298N GND. Connect L298N GND to Pi GND too.

8
Run the code

Copy the Python script below to your Pi. Run python3 motor_control.py. Motor 1 forward, Motor 2 backward for 2 s, then stop.

9
Calibrate speed

Adjust the duty cycle value (0–65535 Python / 0–4095 C++). HALF ≈ 50% speed. Tweak for smooth, consistent motion.


💻 Code

Two versions provided. Both use PCA9685 over I2C to offload PWM from the Pi's GPIO pins for smoother control.

Python 3 (Raspberry Pi native)

Python 3
#!/usr/bin/env python3
"""
Raspberry Pi Motor Control — L298N via PCA9685 (I2C)
MakeMindz.com
"""

import time
import board
import busio
from adafruit_pca9685 import PCA9685

# ── I2C + PCA9685 setup ─────────────────────────────────
i2c = busio.I2C(board.SCL, board.SDA)
pca = PCA9685(i2c)
pca.frequency = 1600        # 1.6 kHz PWM

# Channel map (PCA9685 → L298N)
MOTOR1_IN1 = pca.channels[0]
MOTOR1_IN2 = pca.channels[1]
MOTOR2_IN3 = pca.channels[2]
MOTOR2_IN4 = pca.channels[3]
MOTOR1_ENA = pca.channels[4]  # Motor 1 speed
MOTOR2_ENB = pca.channels[5]  # Motor 2 speed

# PWM duty cycles (16-bit: 0–65535)
FULL = 0xFFFF
HALF = 0x7FFF   # 50% speed
OFF  = 0

def motor1_forward(speed=HALF):
    MOTOR1_ENA.duty_cycle = FULL
    MOTOR1_IN1.duty_cycle = speed
    MOTOR1_IN2.duty_cycle = OFF

def motor2_backward(speed=HALF):
    MOTOR2_ENB.duty_cycle = FULL
    MOTOR2_IN3.duty_cycle = OFF
    MOTOR2_IN4.duty_cycle = speed

def stop_all():
    for ch in [MOTOR1_IN1, MOTOR1_IN2, MOTOR2_IN3, MOTOR2_IN4]:
        ch.duty_cycle = OFF

try:
    while True:
        print("▶ Motor 1 Forward | Motor 2 Backward")
        motor1_forward()
        motor2_backward()
        time.sleep(2)

        print("■ Stop")
        stop_all()
        time.sleep(2)

except KeyboardInterrupt:
    stop_all()
    pca.deinit()
    print("Stopped cleanly.")

Arduino / C++ version

C++ / Arduino
/*
 * Raspberry Pi Motor Control — L298N via PCA9685
 * MakeMindz.com
 */

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); // addr 0x40

#define MOTOR1_IN1  0
#define MOTOR1_IN2  1
#define MOTOR2_IN3  2
#define MOTOR2_IN4  3
#define MOTOR1_ENA  4
#define MOTOR2_ENB  5

#define PWM_FULL  4096  // 100% (always ON)
#define PWM_HALF  2048  // 50% speed
#define PWM_OFF   0

void setup() {
  Wire.begin();
  pwm.begin();
  pwm.setPWMFreq(1600);
  pwm.setPWM(MOTOR1_ENA, 0, PWM_FULL);
  pwm.setPWM(MOTOR2_ENB, 0, PWM_FULL);
}

void loop() {
  // Motor 1 Forward, Motor 2 Backward
  pwm.setPWM(MOTOR1_IN1, 0, PWM_HALF);
  pwm.setPWM(MOTOR1_IN2, 0, PWM_OFF);
  pwm.setPWM(MOTOR2_IN3, 0, PWM_OFF);
  pwm.setPWM(MOTOR2_IN4, 0, PWM_HALF);
  delay(2000);

  // Stop all
  pwm.setPWM(MOTOR1_IN1, 0, PWM_OFF);
  pwm.setPWM(MOTOR1_IN2, 0, PWM_OFF);
  pwm.setPWM(MOTOR2_IN3, 0, PWM_OFF);
  pwm.setPWM(MOTOR2_IN4, 0, PWM_OFF);
  delay(2000);
}
L298N direction truth table Forward: IN1=HIGH, IN2=LOW  |  Backward: IN1=LOW, IN2=HIGH  |  Brake: both HIGH  |  Coast: both LOW

🖥️ Simulate It

Test virtually before building. All three platforms support the L298N and motor control logic.


🚀 Applications

🤖
AI Robots
Run OpenCV or TensorFlow Lite on the Pi for vision-guided navigation.
🚗
Autonomous Vehicles
Add ultrasonic or LiDAR sensors for self-driving robot cars.
🏭
Industrial Automation
Conveyor belts, sorting machines, automated assembly.
📡
IoT Mobility
Control robots over WiFi or MQTT from any browser.
🎓
STEM Projects
Solid, documented base for engineering final-year projects.
📷
Vision Robotics
Add Pi Camera for line-following or object detection bots.


🏷️ Tags

Raspberry Pi L298N DC Motor PWM PCA9685 I2C 18650 Battery Robotics Python GPIO Embedded Systems STEM MakeMindz

© 2026 MakeMindz — Embedded Electronics & Robotics Tutorials

Comments

try for free