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.
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:
- Introducing Stepper Motors
- ULN2003 Motor Driver
- Wiring the Stepper Motor to the Raspberry Pi Pico
- MicroPython Library for the Stepper Motor
- Controlling the Stepper Motor – MicroPython Code
Our Raspberry Pi Pico eBook: Learn 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.
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
- 28BYJ-48 Stepper Motor + ULN2003 Motor Driver
- Jumper wires
- 4xAA battery pack or any other power source (between 5V to 12V)
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.
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 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.
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.
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 Pinout
The following table shows the module pinout:
IN1 | Control the motor: connect to a Pico digital pin |
IN2 | Control the motor: connect to a Pico digital pin |
IN3 | Control the motor: connect to a Pico digital pin |
IN4 | Control the motor: connect to a Pico digital pin |
VCC | Powers the motor |
GND | Common GND |
Motor connector | Connect 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.
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 ULN2003 | Raspberry Pi Pico |
IN1 | GPIO 28 |
IN2 | GPIO 27 |
IN3 | GPIO 26 |
IN4 | GPIO 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.
- 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).
- Go to File > Save as and select Raspberry Pi Pico.
- 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],
]
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')
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:
- Raspberry Pi Pico: Control DC Motor with L298N Motor Driver (MicroPython)
- Raspberry Pi Pico: Control a Servo Motor (MicroPython)
Would you like to learn more about the Raspberry Pi Pico? Make sure you take a look at our resources:
Thanks for reading.
Brilliant! Thank you so, so, much. This has been really helpful. Clearly written and easy to follow.
Love your guides, will it be possible to control pico w from node-red as with esp32 – esp8266.
Hi.
Yes.
You can do it via http or MQTT.
We have this getting started guide for MQTT with the Pico: https://randomnerdtutorials.com/raspberry-pi-pico-w-mqtt-micropython/
Regards,
Sara
Thanks.