Raspberry Pi Stepper Motor
A stepper motor turns in fixed, precisely countable steps rather than spinning freely, which makes it the right choice whenever a project needs exact, repeatable positioning instead of just speed or a target angle.
Stepper Motors vs. DC and Servo Motors
A DC motor spins continuously for as long as voltage is applied, and its speed roughly tracks the voltage (or PWM duty cycle) you feed it - it has no idea what angle it is sitting at unless you bolt on a separate encoder. A servo motor solves the position problem by hiding a small potentiometer and control loop inside the housing, so it can move to a commanded angle (usually within a 0-180 degree range) and hold it. A stepper motor takes a third approach: one full rotation is broken into a fixed number of equal-sized steps, and the motor rotates by exactly one step every time its driver energizes the coils in the next pattern in the sequence. Because the motor physically cannot move more or less than one step per pulse, the controlling microcontroller always knows the shaft's position simply by counting the pulses it has sent - no feedback sensor required.
What Happens Inside the Motor
A stepper motor is built from several coils of wire (electromagnets) arranged around a toothed rotor. Energizing one coil pulls the rotor's teeth into alignment with that coil's magnetic field. The driver electronics then switch off that coil and energize the next one in sequence, which pulls the rotor forward by one small, fixed angle. Repeating this pattern quickly and continuously in one direction spins the shaft forward; reversing the order of the pattern spins it backward. Because each individual movement corresponds to one specific coil-switching event, the motor never "coasts" past its intended position the way a DC motor can.
Picking a Driver Board
A Raspberry Pi GPIO pin can only supply a few milliamps - nowhere near enough current to energize a motor coil directly, and trying to do so risks damaging the Pi. That is why steppers are always driven through a separate driver chip. Two very common choices show up in beginner projects: the ULN2003, a simple set of Darlington transistor switches usually sold on a small breakout board paired with the inexpensive 28BYJ-48 unipolar stepper (it has a built-in gearbox, so a small physical step from the motor becomes a much smaller step at the output shaft); and the A4988, a dedicated stepper driver chip used with bipolar motors like the NEMA-17, which only needs two logic signals from the Pi - STEP (pulse once per step) and DIR (which way to turn) - because it handles all of the coil-switching internally, including optional microstepping for smoother motion.
Rotate a 28BYJ-48 Stepper With a ULN2003 Driver
from gpiozero import OutputDevice
from time import sleep
# ULN2003 board: IN1..IN4 wired to these GPIO pins
in1 = OutputDevice(17)
in2 = OutputDevice(18)
in3 = OutputDevice(27)
in4 = OutputDevice(22)
coils = (in1, in2, in3, in4)
# Half-step sequence: smoother motion and more torque than full-step
sequence = [
(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),
]
STEPS_PER_REVOLUTION = 4096 # 28BYJ-48 output shaft, half-step mode
def rotate_degrees(angle, delay=0.0015):
steps = int((angle / 360) * STEPS_PER_REVOLUTION)
direction = 1 if steps >= 0 else -1
for _ in range(abs(steps)):
for pattern in sequence[::direction]:
for coil, value in zip(coils, pattern):
coil.value = value
sleep(delay)
for coil in coils:
coil.off()
rotate_degrees(90) # quarter turn clockwise
sleep(1)
rotate_degrees(-90) # back to the starting positionStep a NEMA-17 Stepper With an A4988 Driver
from gpiozero import OutputDevice
from time import sleep
direction = OutputDevice(23) # DIR pin on the A4988
pulse = OutputDevice(24) # STEP pin on the A4988
STEPS_PER_REVOLUTION = 200 # 1.8 degrees per full step, no microstepping
def move(steps, clockwise=True, delay=0.001):
direction.value = clockwise
for _ in range(steps):
pulse.on()
sleep(delay)
pulse.off()
sleep(delay)
# Rotate exactly 45 degrees clockwise
steps_needed = int(STEPS_PER_REVOLUTION * (45 / 360))
move(steps_needed, clockwise=True)- Find the motor's steps-per-revolution rating (for example, 4096 for a 28BYJ-48 in half-step mode, or 200 for a typical 1.8-degree-per-step NEMA-17).
- Divide your target angle by 360 to get the fraction of a full turn you need.
- Multiply that fraction by the steps-per-revolution figure to get the number of pulses to send.
- Round to the nearest whole step, since the motor's shaft physically cannot move a fractional step.
Exercise: Raspberry Pi Motors
Why should a DC motor never be wired directly to a Raspberry Pi GPIO pin?