Stepper Motor with Raspberry PI Pico: 28BYJ-48 and ULN2003 wiring and MicroPython code


Stepper motors can trasform your code logic into move in robotic projects. The 28BYJ-48 stepper motor (with ULN2003 motor driver) is the cheapest model and works with a simple code on Rapsberry PI Pico

In this tutorial, I’m going to show you how to wire and use the 28BYJ-48 and ULN2003 with Raspberry PI Pico, with MicroPython code.

Please note that, if you need the code for Raspberry PI computer boards, you can refer to my Controlling a stepper motor with Raspberry Pi Zero W tutorial.

28BYJ-48 stepper motor and ULN2003

A stepper motor is a brushless DC electric motor. Their hardware makes it possible to divide a full rotation into a number of equal steps. Compared to common DC motors, they are far more precise but they have less rotation speed. They usually fit well into applications to control cameras, robotic arms, or whenever you need to perform precise rotations. When choosing the stepper motor to use for your project, please check carefully the required torque force and speed (usually available from the producer datasheet), as it may impact your final result.

As stepper motors are current hungry devices, a motor driver is usually included to manage the power supply and keep it isolated from microcontroller circuits.

The 28BYJ-48 stepper motor works by powering a specific sequence into the ULN2003 driver Pins. The states for each sequence step are the following:


To move the stepper you have to produce sequence 1 first at the pins, then sequence 2, then sequence 3, and so on. Moving the stepper in opposite direction can be achieved just by working in the opposite sequence (sequence 0, sequence 7, sequence 6, and so on).

In this tutorial, I’m going to use the 28BYJ-48 stepper motor and the ULN2003 motor driver, which are usually sold together.

What We Need

As usual, I suggest adding from now to your favourite e-commerce shopping cart all needed hardware, so that at the end you will be able to evaluate overall costs and decide if continue with the project or remove them from the shopping cart. So, hardware will be only:

Check hardware prices with the following links:

amazon raspberry pi pico box
Amazon stepper motor box
Amazon Dupont Wiring box

Step-by-Step Procedure

Prepare Wiring

Please arrange cabling according to the following diagram, according to Raspberry PI Pico pinout:

raspberry pi pico stepper motor wiring diagram

Please find below some detail pictures from my lab:

raspberry pi pico stepper motor details 01
raspberry pi pico stepper motor details 02
raspberry pi pico stepper motor details 03
raspberry pi pico stepper motor details 04

Get and Understand my Code

Now download the following file to use your 28BYJ-48 stepper motor with ULN2003:

You can both load it in your Raspberry PI Pico storage or run it from your computer.

I’ll explain all code lines in the following paragraphs.

First of all, required modules are imported:

from machine import Pin
from time import sleep

Then we define some variables. The first one identifies what are the GP number from Raspberry PI Pico connected with ULN2003, in order with IN1, IN2, IN3, and IN4. According to my cabling, I use GP0, GP1, GP2, and GP3, so:

motor_GP = [0,1,2,3]

Then I use a sequence pointer. This is going to be a global variable so that the function which moves the stepper motor is able to update it at each step:


I also use a “stepper_obj” array. This will include the 4 Pin objects of Raspberry PI Pico, in order to get the following code simplified:

stepper_obj = []

The last variable lists all the sequence steps as an array of array. Please note that arrSeq[0] is equal to Seq0 from the previous table:

arrSeq = [[0,0,0,1],\

Before going into the main function/loop, I initialize all the Raspberry PI Pico pins to output and append those objects (the Pin() function creates an object) to my stepper_obj. As you can see, the array structure allows setting all the 4 Raspberry PI Pico Pins with a single-line loop. The user is warned that Pins are going to be initialized with a shell message:

print("Setup pins...")
for gp in motor_GP: stepper_obj.append(Pin(gp, Pin.OUT))

The stepper_move() function is where all the stuff is managed. It requires an input variable: direction. It must be “+1” or “-1” in order to have forward or backward rotation.

The “global” statement makes this function able to edit the seq_pointer variable:

def stepper_move(direction): # direction must be +1 or -1
    global seq_pointer

When the code enters this function, we rotate the sequance pointer by 1 step.

At the start of this script, the seq_pointer equals to [0,1,2,3,4,5,6,7]. Executing “seq_pointer[1:]+seq_pointer[:1]” we’ll get a new value for seq_pointer equal to [1,2,3,4,5,6,7,0]. The following execution will implement the same rotation.

On the other side, from seq_pointer = [0,1,2,3,4,5,6,7], executing “seq_pointer[-1:]+seq_pointer[:-1]” we’ll get a new value for seq_pointer equal to [7,0,1,2,3,4,5,6].

At each iteration the first value of seq_pointer array will be the real pointer to be used to select how to power GP pins according to arrSeq map:


So, seq_pointer[0] will be our pointer at every step. Used as index for arraySeq, we’ll get the ordered list of values to set into our Pins. For example:

  • when seq_pointer = [7,0,1,2,3,4,5,6]
  • -> seq_pointer[0] = 7
  • -> arrSeq[seq_pointer[0]] = arrSeq[7] = [1,0,0,1]

So, the following row calculates the power to set at each Raspberry PI Pico Pin and assign that value within a single for loop:

    for a in range(4): stepper_obj[a].value(arrSeq[seq_pointer[0]][a])

Before closing the stepper_move() function, we add a delay because a too fast execution will result in no move…:


That done, moving the 28BYJ-48 stepper forward will be achieved by calling the stepper_move() function with direction parameter set to 1, as in the following loop:

while True:

If you need to move backward, you just need to change “1” into “-1”:

while True:

Running the Script

Run this script in your Thonny IDE (F5) and your stepper motor will start rotating.


How useful was this post?

Click on a star to rate it anonymously!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?