Overview
The 28BYJ-48 is the most-used stepper motor in the maker world: a 5V unipolar stepper with a built-in gearbox that trades speed for torque and precision. The motor itself steps 32 times per revolution, but an internal ~1:64 gear train multiplies that to roughly 2048 steps per output-shaft turn (4096 half-steps) — fine enough to point, index, and position mechanisms with no encoder at all.
Five colored wires leave the motor: red is the +5V center tap shared by both windings, and blue, pink, yellow, and orange are the four coil ends. Energize the coils in sequence and the shaft walks around; reverse the sequence and it walks back. Because each coil needs ~90 mA — more than a GPIO pin can source — the motor is always driven through a transistor array, almost universally the ULN2003 driver board it ships with in most kits (ShillehTek's motor+driver bundle includes it).
It's happiest in light-duty jobs: camera sliders and pan mounts, clock and gauge mechanisms, blinds and vent flaps, dispensers, and desktop automatons. It is quiet, cheap, runs straight off 5V, and — unlike a servo — rotates continuously with repeatable steps in both directions.
At a Glance
Specifications
| Parameter | Value |
| Motor Type | Unipolar permanent-magnet gear stepper |
| Rated Voltage | 5V DC |
| Phases | 4 (common center tap on red wire) |
| Coil Resistance | ~50 ohm per phase (~90 mA at 5V) |
| Stride Angle | 5.625° / 64 per half-step (motor: 11.25°) |
| Steps per Output Revolution | ~2048 full-step / ~4096 half-step |
| Gear Reduction | ~1:63.68 (commonly quoted 1:64) |
| Holding Torque | >34 mN·m (~300 g·cm) |
| Practical Speed | ~10-15 RPM continuous |
| Wire Colors | Red = +5V, Orange/Yellow/Pink/Blue = coils 1-4 |
| Shaft | 5 mm flatted output shaft |
Pinout Diagram
The red wire is the shared +5V center tap; each of the other four wires grounds one coil through the ULN2003's darlington outputs. The connector keys into the driver board one way, so with the standard kit you can't miswire it. The coil order the driver sees is IN1 = blue, IN2 = pink, IN3 = yellow, IN4 = orange — the sequences in the code below assume exactly that.
Wiring Guide
All wiring below goes through the ULN2003 driver board: motor connector into the keyed socket, then four logic inputs plus power.
Arduino Wiring
| ULN2003 Pin | Arduino Pin | Details |
|---|---|---|
| IN1 | D8 | |
| IN2 | D9 | |
| IN3 | D10 | |
| IN4 | D11 | |
| + / - | 5V / GND | Motor power |
ESP32 Wiring
The ULN2003 inputs switch fine from 3.3V logic; motor power still comes from 5V (VIN/USB).
| ULN2003 Pin | ESP32 Pin | Details |
|---|---|---|
| IN1 / IN2 / IN3 / IN4 | GPIO 14 / 27 / 26 / 25 | 3.3V logic OK |
| + | VIN (5V) | Motor supply |
| - | GND | Common ground |
Raspberry Pi Wiring
| ULN2003 Pin | Pi Pin | Details |
|---|---|---|
| IN1 | GPIO 17 (pin 11) | |
| IN2 | GPIO 18 (pin 12) | |
| IN3 | GPIO 27 (pin 13) | |
| IN4 | GPIO 22 (pin 15) | |
| + / - | Pin 2 (5V) / Pin 6 (GND) | Motor power |
Raspberry Pi Pico Wiring
| ULN2003 Pin | Pico Pin | Details |
|---|---|---|
| IN1 / IN2 / IN3 / IN4 | GP2 / GP3 / GP4 / GP5 | |
| + | VBUS (pin 40, 5V) | USB 5V |
| - | GND (pin 38) |
Code Examples
Each example turns the shaft one full revolution clockwise, pauses, then returns counter-clockwise — the standard smoke test that verifies coil order, direction, and step count in one run.
Arduino
// 28BYJ-48 + ULN2003 - Arduino Example (built-in Stepper library)
// IN1->8, IN2->9, IN3->10, IN4->11
#include <Stepper.h>
const int STEPS_PER_REV = 2048; // full-step mode
// Note the pin order: IN1, IN3, IN2, IN4 for correct sequencing
Stepper stepper(STEPS_PER_REV, 8, 10, 9, 11);
void setup() {
Serial.begin(9600);
stepper.setSpeed(12); // RPM (10-15 is reliable)
}
void loop() {
Serial.println("One revolution clockwise...");
stepper.step(STEPS_PER_REV);
delay(1000);
Serial.println("One revolution counter-clockwise...");
stepper.step(-STEPS_PER_REV);
delay(1000);
}
ESP32 (MicroPython)
# 28BYJ-48 + ULN2003 - ESP32 MicroPython Example (half-step)
# IN1->GPIO 14, IN2->GPIO 27, IN3->GPIO 26, IN4->GPIO 25
from machine import Pin
import time
pins = [Pin(p, Pin.OUT) for p in (14, 27, 26, 25)]
HALF_STEP = [
(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_REV = 4096 # half-steps per output rev
def move(steps, delay_us=1200):
seq = HALF_STEP if steps > 0 else HALF_STEP[::-1]
for i in range(abs(steps)):
for pin, v in zip(pins, seq[i % 8]):
pin.value(v)
time.sleep_us(delay_us)
for pin in pins: # release coils (no heat)
pin.value(0)
while True:
print("One revolution clockwise...")
move(STEPS_PER_REV)
time.sleep(1)
print("One revolution counter-clockwise...")
move(-STEPS_PER_REV)
time.sleep(1)
Raspberry Pi (Python)
#!/usr/bin/env python3
# 28BYJ-48 + ULN2003 - Raspberry Pi Example (half-step)
# IN1->GPIO17, IN2->GPIO18, IN3->GPIO27, IN4->GPIO22
import RPi.GPIO as GPIO
import time
PINS = [17, 18, 27, 22]
HALF_STEP = [
(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_REV = 4096
GPIO.setmode(GPIO.BCM)
for p in PINS:
GPIO.setup(p, GPIO.OUT, initial=0)
def move(steps, delay=0.0012):
seq = HALF_STEP if steps > 0 else HALF_STEP[::-1]
for i in range(abs(steps)):
for pin, v in zip(PINS, seq[i % 8]):
GPIO.output(pin, v)
time.sleep(delay)
for p in PINS:
GPIO.output(p, 0) # release coils
try:
while True:
print("One revolution clockwise...")
move(STEPS_PER_REV)
time.sleep(1)
print("One revolution counter-clockwise...")
move(-STEPS_PER_REV)
time.sleep(1)
except KeyboardInterrupt:
GPIO.cleanup()
print("Stopped by user")
Raspberry Pi Pico (MicroPython)
# 28BYJ-48 + ULN2003 - Pico MicroPython Example (half-step)
# IN1->GP2, IN2->GP3, IN3->GP4, IN4->GP5, +->VBUS, -->GND
from machine import Pin
import time
pins = [Pin(p, Pin.OUT) for p in (2, 3, 4, 5)]
HALF_STEP = [
(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_REV = 4096
def move(steps, delay_us=1200):
seq = HALF_STEP if steps > 0 else HALF_STEP[::-1]
for i in range(abs(steps)):
for pin, v in zip(pins, seq[i % 8]):
pin.value(v)
time.sleep_us(delay_us)
for pin in pins:
pin.value(0)
while True:
print("One revolution clockwise...")
move(STEPS_PER_REV)
time.sleep(1)
print("One revolution counter-clockwise...")
move(-STEPS_PER_REV)
time.sleep(1)