The 28BYJ-48 with its ULN2003 board is the cheapest stepper motor around — ₹120 for both — and it’s geared, so it is slow but strong. It suits blinds, a rotating display or a dial pointer.
The ULN2003 takes four logic inputs from D8–D11. Instead of the basic Stepper library this guide uses AccelStepper in half-step mode, which runs smoother and quieter and ramps the speed up and down.
Wiring
| Arduino Nano | 28BYJ-48 stepper motor + ULN2003 driver | Wire | Notes |
|---|---|---|---|
| D8 | IN1 | blue | |
| D9 | IN2 | purple | |
| D10 | IN3 | yellow | |
| D11 | IN4 | orange | |
| 5V | + | red | About 240 mA while stepping — OK from USB for one motor. Two motors: separate 5 V supply, common GND. |
| GND | - | black |
5 V for the 5 V version of the motor (about 200–250 mA while stepping) part, 5 V board. The ULN2003 inputs accept 3.3 V or 5 V logic, so ESP32 and Pico drive IN1–IN4 directly. Power the motor from 5 V — not from a 3.3 V pin — and share GND with the board.
Board quirks
- AccelStepper needs the coil order IN1, IN3, IN2, IN4 for this motor — that is why the constructor reads (8, 10, 9, 11). With 8, 9, 10, 11 the motor just buzzes.
- The gearbox makes one output turn ≈ 4096 half-steps (the ratio is about 63.7:1, so it is a hair off). Above ~1000 half-steps/s the motor stalls.
- The sketch switches the coils off between moves. Holding position would keep 240 mA flowing and make the motor hot.
- The four LEDs on the ULN2003 board show the coil pattern — a quick way to check each wire.
Code
// 28BYJ-48 stepper + ULN2003 board on an Arduino Nano with AccelStepper (half-step, smooth ramps).
// IN1 → D8, IN2 → D9, IN3 → D10, IN4 → D11, + → 5V, − → GND.
#include <AccelStepper.h>
const long STEPS_PER_REV = 4096; // half-steps per output-shaft turn (gear ratio ~64:1)
// AccelStepper wants the coil order IN1, IN3, IN2, IN4 for this motor.
AccelStepper stepper(AccelStepper::HALF4WIRE, 8, 10, 9, 11);
void setup() {
Serial.begin(115200);
stepper.setMaxSpeed(800); // half-steps/s; above ~1000 the 28BYJ-48 stalls
stepper.setAcceleration(400);
stepper.moveTo(STEPS_PER_REV);
Serial.println(F("One turn clockwise..."));
}
void loop() {
if (stepper.distanceToGo() == 0) {
Serial.print(F("Reached "));
Serial.println(stepper.currentPosition());
stepper.disableOutputs(); // coils off between moves: the motor stays cool
delay(1000);
stepper.enableOutputs();
stepper.moveTo(stepper.currentPosition() == 0 ? STEPS_PER_REV : 0);
}
stepper.run();
}// 28BYJ-48 stepper + ULN2003 board on an Arduino Nano with AccelStepper (half-step, smooth ramps).
// IN1 → D8, IN2 → D9, IN3 → D10, IN4 → D11, + → 5V, − → GND.
#include <AccelStepper.h>
const long STEPS_PER_REV = 4096; // half-steps per output-shaft turn (gear ratio ~64:1)
// AccelStepper wants the coil order IN1, IN3, IN2, IN4 for this motor.
AccelStepper stepper(AccelStepper::HALF4WIRE, 8, 10, 9, 11);
void setup() {
Serial.begin(115200);
stepper.setMaxSpeed(800); // half-steps/s; above ~1000 the 28BYJ-48 stalls
stepper.setAcceleration(400);
stepper.moveTo(STEPS_PER_REV);
Serial.println(F("One turn clockwise..."));
}
void loop() {
if (stepper.distanceToGo() == 0) {
Serial.print(F("Reached "));
Serial.println(stepper.currentPosition());
stepper.disableOutputs(); // coils off between moves: the motor stays cool
delay(1000);
stepper.enableOutputs();
stepper.moveTo(stepper.currentPosition() == 0 ? STEPS_PER_REV : 0);
}
stepper.run();
}Library: Stepper (Arduino) — compiled with AccelStepper 1.64.
Expected serial output
One turn clockwise...
Reached 4096
Reached 0One turn clockwise... Reached 4096 Reached 0
Common errors
Motor vibrates but does not turn
Wrong pin order in the constructor, or running too fast. Use (8, 10, 9, 11) and lower setMaxSpeed.
Nano resets when the motor starts
The USB port can’t supply the motor current. Power the ULN2003 board from a separate 5 V supply, common GND.