A stepper motor moves in precise, discrete steps rather than spinning freely like a DC motor. This makes it ideal for applications where you need exact positioning -- think 3D printers, CNC machines, camera sliders, and automated blinds. The 28BYJ-48 is an inexpensive, widely available stepper motor that comes paired with the ULN2003 driver board. It is the go-to starter stepper for learning the fundamentals.
This guide uses the ESP32-WROOM-32 DevKit. Pin labels and GPIO numbers may differ on your board -- always check your board's pinout diagram.
🔗Key Specs
| Parameter | Value |
|---|---|
| Operating voltage | $5\,\text{V}$ DC |
| Phase | 4-phase unipolar |
| Step angle | $5.625°$ per step (half-step mode) |
| Steps per revolution | $2048$ (half-step), $4096$ with gear reduction in some references |
| Gear ratio | $1:64$ |
| Current draw | $\approx 240\,\text{mA}$ |
| Holding torque | $\approx 300\,\text{g}\cdot\text{cm}$ |
Note: There is some confusion online about the exact step count. The 28BYJ-48 is commonly reported as 2048 steps per revolution in half-step mode. You may need to calibrate by running a full revolution and observing the result.
Which motor type? Steppers move in precise, discrete steps. Need continuous rotation with speed control? See DC motors. Need to hold a specific angle? See servo motors. Our motor comparison guide explains the differences.
🔗What You'll Need
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| 28BYJ-48 stepper motor | 1 | AliExpress | Amazon.de .co.uk .com | |
| ULN2003 stepper driver board | 1 | Usually sold together with the motor | AliExpress | Amazon.de .co.uk .com |
| 5V power supply | 1 | External, do not power from ESP32 | Amazon.de .co.uk .com |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | 6 | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
🔗Wiring
The ULN2003 driver board acts as an interface between the ESP32 and the motor. It contains Darlington transistor arrays that can handle the motor's current.
🔗ULN2003 Driver to ESP32
| ULN2003 Pin | ESP32 Pin | Notes |
|---|---|---|
| IN1 | GPIO 25 | Coil A control |
| IN2 | GPIO 26 | Coil B control |
| IN3 | GPIO 27 | Coil C control |
| IN4 | GPIO 14 | Coil D control |
🔗Power
| ULN2003 Pin | Connection |
|---|---|
| + (VCC) | External 5V supply (not ESP32 3.3V) |
| - (GND) | External supply GND and ESP32 GND |
The motor connects directly to the ULN2003 board via its white 5-pin connector -- just plug it in.
Warning: Do not power the stepper motor from the ESP32's 3.3V or 5V pin. The motor draws around $240\,\text{mA}$, which can overload the ESP32's voltage regulator. Use a separate 5V supply and connect the grounds.
🔗Required Libraries
The Stepper library comes built into the Arduino IDE -- no installation necessary.
🔗Code Example: Basic Rotation
This sketch rotates the stepper motor one full revolution in each direction.
#include <Stepper.h>
#define STEPS_PER_REV 2048 // 28BYJ-48 in half-step mode
// Note: the pin order matters for correct stepping sequence
// ULN2003 IN1, IN3, IN2, IN4 mapping for the 28BYJ-48
Stepper stepper(STEPS_PER_REV, 25, 27, 26, 14);
void setup() {
Serial.begin(115200);
stepper.setSpeed(10); // RPM (keep low for 28BYJ-48, max ~15 RPM)
Serial.println("Stepper motor ready.");
}
void loop() {
Serial.println("Rotating clockwise...");
stepper.step(STEPS_PER_REV); // One full revolution CW
delay(1000);
Serial.println("Rotating counter-clockwise...");
stepper.step(-STEPS_PER_REV); // One full revolution CCW
delay(1000);
}Important: Notice the pin order in the
Stepper()constructor:25, 27, 26, 14(IN1, IN3, IN2, IN4). The 28BYJ-48's internal coil arrangement requires this non-sequential order. If you use25, 26, 27, 14, the motor will vibrate but not spin properly.
🔗Code Example: Rotate by Degrees
This helper function lets you command the motor in degrees rather than steps.
#include <Stepper.h>
#define STEPS_PER_REV 2048
Stepper stepper(STEPS_PER_REV, 25, 27, 26, 14);
void rotateDegrees(float degrees) {
int steps = (int)(degrees / 360.0 * STEPS_PER_REV);
stepper.step(steps);
}
void setup() {
Serial.begin(115200);
stepper.setSpeed(10);
}
void loop() {
Serial.println("Rotate 90 degrees CW");
rotateDegrees(90);
delay(1000);
Serial.println("Rotate 90 degrees CCW");
rotateDegrees(-90);
delay(1000);
Serial.println("Rotate 180 degrees CW");
rotateDegrees(180);
delay(2000);
}🔗How It Works
🔗Stepping Sequence
A stepper motor has multiple coils arranged around a rotor with permanent magnets. By energizing the coils in a specific sequence, you create a rotating magnetic field that pulls the rotor from one position to the next. Each switch in the sequence moves the rotor by one "step."
The 28BYJ-48 is a 4-phase unipolar motor. In half-step mode (which the Stepper library uses), it energizes the coils in an 8-step sequence:
| Step | Coil A | Coil B | Coil C | Coil D |
|---|---|---|---|---|
| 1 | HIGH | LOW | LOW | LOW |
| 2 | HIGH | HIGH | LOW | LOW |
| 3 | LOW | HIGH | LOW | LOW |
| 4 | LOW | HIGH | HIGH | LOW |
| 5 | LOW | LOW | HIGH | LOW |
| 6 | LOW | LOW | HIGH | HIGH |
| 7 | LOW | LOW | LOW | HIGH |
| 8 | HIGH | LOW | LOW | HIGH |
The internal gear train multiplies the number of steps needed for one output shaft revolution, which is what gives the motor its fine positioning ability.
🔗Speed Limits
The 28BYJ-48 is a geared motor designed for precision, not speed. Setting setSpeed() above approximately $15\,\text{RPM}$ will cause it to skip steps or stall. For reliable operation, keep the speed at $10$--$12\,\text{RPM}$.
🔗Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| Motor vibrates but does not rotate | Wrong pin order in constructor | Use Stepper(STEPS, 25, 27, 26, 14) -- note the non-sequential order |
| Motor gets hot | Coils energized while idle | The Stepper library keeps coils energized after step(). Write all pins LOW when not moving to reduce heat. |
| Motor stalls or skips steps | Speed set too high | Reduce speed to $10\,\text{RPM}$ or below |
| Motor moves less than a full revolution | Wrong steps-per-revolution value | Try $2048$ or adjust based on observation |
| Motor does not move at all | No external power to ULN2003 | Provide 5V from an external supply, not the ESP32 |
| ESP32 resets when motor runs | Power draw too high or no shared GND | Use separate 5V supply with shared ground |
Tip: To prevent the motor from overheating when idle, set all motor pins to LOW after completing a movement. Add this function and call it when the motor should be at rest:
void disableMotor() { digitalWrite(25, LOW); digitalWrite(26, LOW); digitalWrite(27, LOW); digitalWrite(14, LOW); }Note that this releases the holding torque, so the shaft can be turned manually.
🔗Next Steps
- Build a motorized camera slider or turntable
- Create an automated window blind controller
- Use a stepper for precise fluid dispensing (peristaltic pump)
- Explore the AccelStepper library for acceleration/deceleration profiles and non-blocking movement