Part 3: Motor Control Programming - Raspberry Pi Pico W Robotics Course

Part 3: Motor Control Programming

Welcome to Part 3 of the Raspberry Pi Pico W Robotics Course! In this section, we’ll write Python code to control the robot’s motors using the L298N motor driver. By the end, your robot will be able to move forward, backward, left, and right.

What You’ll Do in Part 3

In this part, we’ll focus on programming the robot’s movement. Here’s what you’ll accomplish:

  • Write Python code to control the motors using the L298N motor driver.
  • Test basic movements: forward, backward, left, and right.
  • Use Pulse Width Modulation (PWM) to control motor speed.

Let’s get started!

Step 1: Understanding the L298N Motor Driver

The L298N motor driver allows you to control two DC motors independently. It uses four input pins (IN1, IN2, IN3, IN4) to control the direction of the motors and enable pins (ENA, ENB) to control their speed using PWM.

Here’s how the motor driver works:

  • IN1 and IN2: Control Motor A (e.g., forward or backward).
  • IN3 and IN4: Control Motor B (e.g., forward or backward).
  • ENA and ENB: Enable pins for Motor A and Motor B, respectively. These pins accept PWM signals to control speed.

Refer to the motor driver’s datasheet or pinout diagram for more details.

Step 2: Writing the Motor Control Code

Now, let’s write the Python code to control the motors. Open Thonny and create a new Python file. Use the following code as a starting point:


"""
This program controls two DC motors using PWM on a Raspberry Pi Pico. 
It allows the user to set a speed (0-100%) and moves the motors forward, backward, 
left, and right in a loop. If the motors move in the wrong direction, 
reverse their polarity by swapping the connections on the respective DC motor.
"""


from machine import Pin, PWM
from time import sleep

# ───── Helper function to convert 0–100% into duty_u16 value (0–65535) ─────
def percent_to_duty(percentage):
    # Clamp the percentage between 0 and 100
    if percentage < 0:
        percentage = 0
    elif percentage > 100:
        percentage = 100
    return int((65535 * percentage) / 100)

# ───── MOTOR B (Existing) ─────
in3 = Pin(2, Pin.OUT)   # GPIO2
in4 = Pin(3, Pin.OUT)   # GPIO3
enb = PWM(Pin(9))       # GPIO9 for PWM
enb.freq(1000)          # Set PWM frequency to 1 kHz (reduces audible noise, balances efficiency)

def motorB_forward(duty):
    in3.high()
    in4.low()
    enb.duty_u16(duty)

def motorB_backward(duty):
    in3.low()
    in4.high()
    enb.duty_u16(duty)

def motorB_stop():
    in3.low()
    in4.low()
    enb.duty_u16(0)

# ───── MOTOR A (New) ─────
in1 = Pin(0, Pin.OUT)   # GPIO0
in2 = Pin(1, Pin.OUT)   # GPIO1
ena = PWM(Pin(8))       # GPIO8 for PWM
ena.freq(1000)          # Set PWM frequency to 1 kHz

def motorA_forward(duty):
    in1.high()
    in2.low()
    ena.duty_u16(duty)

def motorA_backward(duty):
    in1.low()
    in2.high()
    ena.duty_u16(duty)

def motorA_stop():
    in1.low()
    in2.low()
    ena.duty_u16(0)

# ───── MAIN LOOP ─────
try:
    while True:
        # Ask the user for a speed percentage
        user_input = input("Enter speed (0–100) for both motors (Ctrl-C to quit): ")
        if not user_input.isdigit():
            print("Please enter a valid integer between 0 and 100!")
            continue

        speed_percent = int(user_input)
        # Convert percentage to duty cycle (0–65535)
        duty_value = percent_to_duty(speed_percent)

        # 1. Move FORWARD at user-specified speed
        print(f"Moving both motors FORWARD at {speed_percent}%")
        motorA_forward(duty_value)
        motorB_forward(duty_value)
        sleep(2)

        # Stop motors
        print("Stopping motors...")
        motorA_stop()
        motorB_stop()
        sleep(1)

        # 2. Move BACKWARD at user-specified speed
        print(f"Moving both motors BACKWARD at {speed_percent}%")
        motorA_backward(duty_value)
        motorB_backward(duty_value)
        sleep(2)

        # Stop motors
        print("Stopping motors...")
        motorA_stop()
        motorB_stop()
        sleep(1)

        # 3. Turn LEFT
        #    (Motor A backward, Motor B forward)
        print(f"Turning LEFT at {speed_percent}%")
        motorA_backward(duty_value)
        motorB_forward(duty_value)
        sleep(2)

        # Stop motors
        print("Stopping motors...")
        motorA_stop()
        motorB_stop()
        sleep(1)

        # 4. Turn RIGHT
        #    (Motor A forward, Motor B backward)
        print(f"Turning RIGHT at {speed_percent}%")
        motorA_forward(duty_value)
        motorB_backward(duty_value)
        sleep(2)

        # Stop motors
        print("Stopping motors...")
        motorA_stop()
        motorB_stop()
        sleep(1)

except KeyboardInterrupt:
    motorA_stop()
    motorB_stop()
    print("\nProgram stopped by user.")
            

This code defines functions to move the robot forward, backward, left, and right. It also includes a stop() function to halt the motors. The ENA and ENB pins control the speed of the motors using PWM.

Step 3: Testing Motor Responsiveness

Upload the code to your Raspberry Pi Pico W and run it. Observe the robot’s movement:

  • Does it move forward and backward as expected?
  • Does it turn left and right smoothly?
  • Adjust the PWM values in the move_forward(), turn_left(), and other functions to fine-tune the speed.

If the robot doesn’t move as expected, double-check your wiring and GPIO pin assignments.

What’s Next?

Great job! Your robot can now move in all directions. In Part 4: Adding Obstacle Detection, we’ll integrate the HC-SR04 ultrasonic sensor to detect obstacles and make the robot autonomous. Stay tuned!

Create a free account to access full content.

All access to code and resources on ShillehTek.

Signup Now

Already a member? Sign In

Explore More on Our Blog

Build a Real-Time Azure IoT Sensor Pipeline with Raspberry Pi Pico W + BME280

Build a Real-Time Azure IoT Sensor Pipeline with Raspberry Pi Pico W + BME280

How to send live environmental sensor data to Azure IoT Hub and visualize it in Azure Data Explorer.

How to Connect an Arduino Sensor to ROS 2 on a Raspberry Pi

How to Connect an Arduino Sensor to ROS 2 on a Raspberry Pi

Learn how to connect a BME280 sensor to an Arduino and publish real-time sensor data into ROS...

How to Install ROS 2 Humble on Raspberry Pi (Ubuntu 22.04.5 LTS)

How to Install ROS 2 Humble on Raspberry Pi (Ubuntu 22.04.5 LTS)

Learn how to install ROS 2 Humble on a Raspberry Pi 4B running Ubuntu 22.04.5 LTS. This step-by-step guide walks...

Part 5: Building a Wi-Fi Control Interface for Remote Control Robot

Part 5: Building a Wi-Fi Control Interface for Remote Control Robot

Part 4: Adding Obstacle Detection - Raspberry Pi Pico W Robotics Course

Part 4: Adding Obstacle Detection - Raspberry Pi Pico W Robotics Course

Part 3: Motor Control Programming - Raspberry Pi Pico W Robotics Course

Part 3: Motor Control Programming - Raspberry Pi Pico W Robotics Course

Part 2: Robot Assembly - Raspberry Pi Pico W Robotics Course

Part 2: Robot Assembly - Raspberry Pi Pico W Robotics Course

Build a Wi-Fi-Controlled Obstacle-Avoidance Robot with Raspberry Pi Pico W

Build a Wi-Fi-Controlled Obstacle-Avoidance Robot with Raspberry Pi Pico W

Learn how to build a remote-control robot using the Raspberry Pi Pico W and a few simple hardware components. This...

Simple Guide: Build a Reverse Geolocator with Raspberry Pi Pico W and GPS Module

Simple Guide: Build a Reverse Geolocator with Raspberry Pi Pico W and GPS Module

Reverse Geolocator Tutorial with Raspberry Pi Pico W and GPS Module

Learn how to create a reverse...

LoRa and Raspberry Pi Pico W: Building a Sender-Receiver Communication System

LoRa and Raspberry Pi Pico W: Building a Sender-Receiver Communication System

function toggleAuthorBio() { var bio = document.getElementById('authorBio'); var button = document.querySelector('.toggle-button'); if (bio.style.display === 'none') { bio.style.display = 'block'; button.textContent = '▼ About this Author'; // Using Unicode character directly button.classList.add('expanded'); } else { bio.style.display = 'none'; button.textContent = '▶ About this Author'; // Using Unicode character directly button.classList.remove('expanded'); } }