Raspberry Pi Pico: Control a Stepper Motor with MicroPython

In this guide, you’ll learn how to control a stepper motor with the Raspberry Pi Pico. We’ll use the 28BYJ-48 unipolar stepper motor with the ULN2003 motor driver. You’ll learn how to wire it to the Raspberry Pi Pico and how to control it using a MicroPython module.

Raspberry Pi Pico Control a Stepper Motor MicroPython 28BYJ-48 unipolar ULN2003 motor driver

New to the Raspberry Pi Pico? Read the following guide: Getting Started with Raspberry Pi Pico (and Pico W).

Table of Contents:

Throughout this tutorial, we’ll cover the following contents:

Our Raspberry Pi Pico eBookLearn Raspberry Pi Pico/Pico W with MicroPython

Prerequisites

Before continuing, make sure you follow the next prerequisites.

MicroPython Firmware

To follow this tutorial you need MicroPython firmware installed in your Raspberry Pi Pico board. You also need an IDE to write and upload the code to your board.

micorpython logo

The recommended MicroPython IDE for the Raspberry Pi Pico is Thonny IDE. Follow the next tutorial to learn how to install Thonny IDE, flash MicroPython firmware, and upload code to the board.

Alternatively, if you like programming using VS Code, you can start with the following tutorial:

Parts Required

You’ll also need the following parts:

Raspberry Pi Pico Stepper Motor ULN2003

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Introducing Stepper Motors

A stepper motor is a brushless DC electric motor that divides a full rotation into a number of steps. It moves one step at a time, and each step is the same size. This allows us to rotate the motor at a precise angle to a precise position. The stepper motor can rotate clockwise or counterclockwise.

The following picture shows two 28BYJ-48 stepper motors.

28BYJ-48 Stepper Motors

Stepper motors are made of internal coils that make the motor shaft move in steps in one direction or the other when current is applied to the coils in a specific way. There are two types of stepper motors: unipolar and bipolar stepper motors. We won’t go into detail how the stepper motors are made or how they work internally.

To learn in more detail how they work and the differences between each type of stepper motor, we recommend reading this article by the DroneBotWorkshop blog.

28BYJ-48 Stepper Motor

There are several stepper motors with different specifications. We’ll use the used 28BYJ-48 unipolar stepper motor with the ULN2003 motor driver, which is a combination widely used by DIY and electronics hobbyists.

28BYJ-48 connected to ULN2003 Motor Driver 01 module

28BYJ-48 Stepper Motor Features

Features of the stepper motor (for more details, consult the datasheet):

  • Rated voltage: 5V DC
  • Number of phases: 4
  • Speed variation ratio: 1/64
  • Stride angle: 5.625º/64
  • Frequency: 100Hz

The 28BYJ-48 stepper motor has a total of four coils. One end of the coils is connected to 5V, which corresponds to the motor’s red wire. The other end of the coils corresponds to the wires with blue, pink, yellow, and orange colors. Energizing the coils in a logical sequence makes the motor move one step in one direction or the other.

28BYJ-48 stepper motor coils

The 28BYJ-48 Stepper Motor has a stride angle of 5.625°/64 in half-step mode. This means that the motor has a step angle of 5.625º—so it needs 360º/5.625º = 64 steps in half-step mode. In full-step mode: 64/2 = 32 steps to complete one rotation.

However, the output shaft is driven via a 64:1 gear ratio. This means that the shaft (visible outside the motor) will complete a rotation if the motor inside rotates 64 times. This means that the motor will have to move 32×64 = 2048 steps for the shaft to complete one full rotation. This means that you’ll have a precision of 360º/2048 steps = 0.18º/step.

So, in summary:

  • Total steps per revolution = 2048 steps
  • Step angle = 0.18º/step

If you’re using a different stepper motor, please consult the datasheet.

Don’t worry if this seems confusing. There are libraries that make programming easier without having to worry about those numbers.

ULN2003 Motor Driver

To interface the stepper motor with the Raspberry Pi Pico, we’ll use the ULN2003 motor driver, as shown in the figure below. The 28BYJ-48 stepper motor is many times sold together with the ULN2003 motor driver as a bundle.

ULN2003 Motor Driver 01 module board

The module comes with a connector that makes it easy and simple to connect the motor to the module. It has four input pins to control the coils that make the stepper motor move. The four LEDs provide a visual interface of the coils’ state.

ULN2003 Motor Driver Labels

ULN2003 Motor Driver Pinout

The following table shows the module pinout:

IN1Control the motor: connect to a Pico digital pin
IN2Control the motor: connect to a Pico digital pin
IN3Control the motor: connect to a Pico digital pin
IN4Control the motor: connect to a Pico digital pin
VCCPowers the motor
GNDCommon GND
Motor connectorConnect the motor connector wire

Wiring the Stepper Motor to the Raspberry Pi Pico

Let’s connect the stepper motor to the Raspberry Pi Pico via the ULN2003 motor driver. We’ll connect IN1, IN2, IN3, and IN4 to GPIOs 28, 27, 26, and 22, respectively. You can use any other digital pins as long as you indicate the pins you’re using on the code.

Recommended reading: Raspberry Pi Pico and Pico W Pinout Guide: GPIOs Explained.

You can follow the next schematic diagram. Or use the table below as a reference.

Wiring the Stepper Motor to the Raspberry Pi Pico

Note: you should power the motor driver using an external power supply between 5 to 12V. We’re using 6.5V provided by a battery pack.

The GND pin of the battery pack should also be connected to one GND pin of the Raspberry Pi Pico.

Motor Driver ULN2003Raspberry Pi Pico
IN1GPIO 28
IN2GPIO 27
IN3GPIO 26
IN4GPIO 22

MicroPython Library for the Stepper Motor

There are several ways to control a stepper motor using a library. We’ll use one that was done specifically for the 28BYJ-48 stepper motor controlled with the ULN2003 motor driver.

Follow the next steps to upload the library to the Raspberry Pi Pico.

  1. Create a new file in Thonny IDE and copy the library code. The library code can be found below (or you can click on this link).
  2. Go to File > Save as and select Raspberry Pi Pico.
  3. Name the file stepper.py and click OK to save the file on the Raspberry Pi Pico.
# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-stepper-motor-micropython/
# Forked from: https://github.com/larsks/micropython-stepper-motor/blob/master/motor.py

import machine
import time

class Motor:
    stepms = 10

    # Do be defined by subclasses
    maxpos = 0
    states = []

    def __init__(self, p1, p2, p3, p4, stepms=None):
        self.pins = [p1, p2, p3, p4]

        if stepms is not None:
            self.stepms = stepms

        self._state = 0
        self._pos = 0

    def __repr__(self):
        return '<{} @ {}>'.format(
            self.__class__.__name__,
            self.pos,
        )

    @property
    def pos(self):
        return self._pos

    @classmethod
    def frompins(cls, *pins, **kwargs):
        return cls(*[machine.Pin(pin, machine.Pin.OUT) for pin in pins],
                   **kwargs)

    def reset(self):
        self._pos = 0

    def _step(self, dir):
        state = self.states[self._state]

        for i, val in enumerate(state):
            self.pins[i].value(val)

        self._state = (self._state + dir) % len(self.states)
        self._pos = (self._pos + dir) % self.maxpos

    def step(self, steps):
        dir = 1 if steps >= 0 else -1
        steps = abs(steps)

        for _ in range(steps):
            t_start = time.ticks_ms()

            self._step(dir)

            t_end = time.ticks_ms()
            t_delta = time.ticks_diff(t_end, t_start)
            time.sleep_ms(self.stepms - t_delta)

    def step_until(self, target, dir=None):
        if target < 0 or target > self.maxpos:
            raise ValueError(target)

        if dir is None:
            dir = 1 if target > self._pos else -1
            if abs(target - self._pos) > self.maxpos/2:
                dir = -dir

        while True:
            if self._pos == target:
                break
            self.step(dir)

    def step_until_angle(self, angle, dir=None):
        if angle < 0 or angle > 360:
            raise ValueError(angle)

        target = int(angle / 360 * self.maxpos)
        self.step_until(target, dir=dir)
        
    def step_degrees(self, degrees):
        if degrees < 0 or degrees > 360:
            raise ValueError("Degrees should be between 0 and 360")

        steps_to_take = int(degrees / 360 * self.maxpos)

        self.zero()  # Ignore the current position, start from zero
        self.step(steps_to_take)

class FullStepMotor(Motor):
    stepms = 5
    maxpos = 2048
    states = [
        [1, 1, 0, 0],
        [0, 1, 1, 0],
        [0, 0, 1, 1],
        [1, 0, 0, 1],
    ]


class HalfStepMotor(Motor):
    stepms = 3
    maxpos = 4096
    states = [
        [1, 0, 0, 0],
        [1, 1, 0, 0],
        [0, 1, 0, 0],
        [0, 1, 1, 0],
        [0, 0, 1, 0],
        [0, 0, 1, 1],
        [0, 0, 0, 1],
        [1, 0, 0, 1],
    ]

View raw code

And that’s it. The library was uploaded to your board. Now, you can use the library functionalities in your code by importing the library.

Controlling the Stepper Motor – MicroPython Code

The library we’re using is very intuitive to use. The following code illustrates the different available methods.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-stepper-motor-micropython/

import stepper
from time import sleep

# Define the stepper motor pins
IN1 = 28
IN2 = 27
IN3 = 26
IN4 = 22

# Initialize the stepper motor
stepper_motor = stepper.HalfStepMotor.frompins(IN1, IN2, IN3, IN4)

# Set the current position as position 0
stepper_motor.reset()

try:
    while True:
        #Move 500 steps in clockwise direction
        stepper_motor.step(500)
        sleep(0.5) # stop for a while
        
        # Move 500 steps in counterclockwise direction
        stepper_motor.step(-500)
        sleep(0.5) # stop for a while
        
        # Go to a specific position (in steps)
        stepper_motor.step_until(2000)
        sleep(0.5) # stop for a while
        
        # Force a direction using the dir paramter
        stepper_motor.step_until(2000, dir=-1)
        sleep(0.5) # stop for a while        
        
        # Go to a specific position (angle, maximum is 359, otherwise it will spin indefinetely)
        stepper_motor.step_until_angle(359)
        sleep(0.5) # stop for a while
    
except KeyboardInterrupt:
    print('Keyboard interrupt')

View raw code

How the Code Works

Let’s take a quick look at how the code works to see the different methods provided by the library to control stepper motors.

First, you need the stepper library you’ve just uploaded to the Raspberry Pi Pico.

import stepper

Then, define the pins you’re using to control the stepper motor via the ULN2003 motor driver. If you’re using different pins, modify the following lines:

# Define the stepper motor pins
IN1 = 28
IN2 = 27
IN3 = 26
IN4 = 22

Then, initialize the stepper motor as follows (we’re using a half-step motor, as explained previously).

# Initialize the stepper motor
stepper_motor = stepper.HalfStepMotor.frompins(IN1, IN2, IN3, IN4)

Before start moving the motor, we use the reset() method to tell the code that the stepper’s current position is position 0.

# Set the current position as position 0
stepper_motor.reset()

Then, we have a while loop where we move the motor using different methods.

try:
    while True:

The step() method moves the motor by a number of steps in a clockwise direction. Pass as an argument the number of steps you want to move.

#Move 500 steps in clockwise direction
stepper_motor.step(500)

You can also pass a negative number to move counterclockwise.

# Move 500 steps in counterclockwise direction
stepper_motor.step(-500)

If you want the motor to go to a specific position (counting from the position defined as zero), you can use the step_until() method and pass as an argument the step number. This method will find the shortest way to move the motor to a specific position.

# Go to a specific position (in steps)
stepper_motor.step_until(2000)

At any time during your code, you can call the reset() method to define a new reference point (position 0).

You can pass an extra parameter to the step_until() method, the dir parameter. It can be 1 to force the motor to move in a clockwise direction, or -1 to force the motor to go in a counterclockwise direction.

# Force a direction using the dir parameter
stepper_motor.step_until(2000, dir=-1)
sleep(0.5) # stop for a while

Similarly, there’s a method that does the same thing, but you can pass the position in degrees. Don’t pass 360 as an argument, otherwise, the motor won’t stop moving.

stepper_motor.step_until_angle(359)

Testing the Code

Upload the previous code to your Raspberry Pi Pico and see the motor moving to different positions every half a second. Experiment with the different methods and pass different arguments to see how the motor behaves.

Wrapping Up

In this tutorial, you’ve learned how to control a stepper motor with the Raspberry Pi Pico programmed with MicroPython. One of the most popular motors to use in DIY hobbyists’ projects is the 28BYJ-48 unipolar stepper motor with the ULN2003 motor driver.

If you want to interface other motors with the Raspberry Pi Pico, check the tutorials below:

Would you like to learn more about the Raspberry Pi Pico? Make sure you take a look at our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.