Learning Goals 5 min
Yesterday you wrote the four-pattern sequence by hand. Today you delete it — Arduino's built-in Stepper library does the same job in three method calls. The hardware is identical; the sketch is half the length. By the end of this lesson you will be able to:
- Include the built-in
Stepperlibrary, create aStepperobject with the correct pin order for a 28BYJ-48 + ULN2003, and command rotations in degrees rather than raw steps. - Set rotational speed in RPM with
setSpeed()and explain why values above ~15 RPM cause the 28BYJ-48 to skip steps. - Reverse direction by passing a negative number to
step()— and explain why this approach is cleaner than the "run the sequence backwards" manual code from L03-12.
Warm-Up 10 min
Keep yesterday's wiring intact: 28BYJ-48 plugged into the ULN2003 board, control wires on Arduino D8–D11, separate 5 V supply for the motor, common ground. Yesterday's sketch should still be in your IDE if you saved it as stepper-manual.ino.
Recall your homework prediction
You guessed how many lines today's library sketch would be vs yesterday's ~25-line manual one. Hold that number in your head — we'll compare at the end.
The big idea before we open the library
What you wrote yesterday: 4 lines for the sequence table, a writePhase() helper, a stepOnce() helper, and a loop counting steps. That code is generic — it would work for any 4-coil unipolar stepper, not just the 28BYJ-48. The Arduino team noticed the same thing in ~2008, wrote it once, and shipped it as the Stepper library. Including it means you never write that code again.
New Concept · The three method calls 25 min
Step 1 · Include the library
#include <Stepper.h>Built in — no install needed. Same family as Servo.h.
Step 2 · Create a Stepper object
The constructor takes the steps-per-revolution and four pin numbers:
const int STEPS_PER_REV = 2048; // 28BYJ-48 with internal gearbox
Stepper motor(STEPS_PER_REV, 8, 10, 9, 11);Note the pin order: 8, 10, 9, 11 — not 8, 9, 10, 11. This is the classic gotcha. The library expects the pins for the two coils of a bipolar stepper, in the order they fire. For the 28BYJ-48 + ULN2003, that comes out as IN1, IN3, IN2, IN4 on the driver. Get this wrong and the motor will buzz and vibrate but not turn smoothly — or it will turn at a quarter of the expected speed. Try both orders if your motor refuses to rotate properly.
Step 3 · setSpeed(rpm)
Sets the rotation speed in RPM (revolutions per minute):
motor.setSpeed(12); // 12 RPM — safely inside the 28BYJ-48's reliable rangeThe library will time its step delays to hit that RPM, given the steps-per-revolution you specified. Set 60 RPM on the 28BYJ-48 and the motor will not move — you're asking for 4× the reliable maximum, and the rotor can't physically keep up. Stay at or below 15 RPM.
Step 4 · step(numSteps)
Steps the motor by the given number of steps (not degrees). Positive = one direction, negative = the other. Blocks until done.
motor.step(1024); // 1024 steps = half revolution on a 28BYJ-48
motor.step(-512); // 512 steps backward = quarter revThe full minimum-viable sketch
Here's yesterday's "one full rotation" rewritten with the library:
#include <Stepper.h>
const int STEPS_PER_REV = 2048;
Stepper motor(STEPS_PER_REV, 8, 10, 9, 11);
void setup() {
motor.setSpeed(12);
motor.step(STEPS_PER_REV); // one full revolution forward
motor.step(-STEPS_PER_REV); // one full revolution backward
}
void loop() { }Total: 10 lines vs yesterday's ~25. The library hides the sequence table, the phase counter, and the per-step delay — you specify what you want, not how to do it.
A degrees-not-steps helper
You usually think in degrees, not raw steps. Wrap the conversion:
int degToSteps(float deg) {
return (int)(deg * STEPS_PER_REV / 360.0);
}
// usage:
motor.step(degToSteps(90)); // quarter turn forward
motor.step(degToSteps(-45)); // 1/8 turn backFrom here on, code reads as physical motion: "step 90 degrees" rather than "step 512".
The big drawback — step() blocks
The library's step() doesn't return until the motion is finished. Commanding step(2048) at 12 RPM takes 5 seconds — and during those 5 seconds your sketch can do nothing else. No sensor reads, no button checks, nothing.
If you need non-blocking stepper motion, the third-party AccelStepper library is the answer. We'll mention it in §5 but stick with the built-in Stepper for L03 — the simplicity is worth the blocking on most builds.
Worked Example · A "quarter-turn-on-command" rig 25 min
You'll command quarter turns and reversals from the Serial Monitor — exactly the kind of thing the rotating display platform in L03-14 will dispatch to the motor.
Step 1 — verify wiring
If you've unplugged from yesterday, re-wire: 28BYJ-48 plug → ULN2003 socket, ULN2003 '+ −' → 4 × AA pack, common ground to Arduino GND, IN1–IN4 to D8–D11. No new components today.
Step 2 — the sketch
// L03-13 · Stepper library — quarter-turn-on-command
#include <Stepper.h>
const int STEPS_PER_REV = 2048;
Stepper motor(STEPS_PER_REV, 8, 10, 9, 11);
const int COIL_PINS[4] = {8, 9, 10, 11};
int degToSteps(float deg) {
return (int)(deg * STEPS_PER_REV / 360.0);
}
void releaseCoils() {
for (int i = 0; i < 4; i++) digitalWrite(COIL_PINS[i], LOW);
}
void setup() {
Serial.begin(9600);
motor.setSpeed(12);
releaseCoils();
Serial.println("# Stepper ready. r=+90 deg l=-90 deg h=+180 deg x=release coils");
}
void loop() {
if (Serial.available() == 0) return;
char cmd = Serial.read();
switch (cmd) {
case 'r': case 'R':
Serial.println(">> +90 deg");
motor.step(degToSteps(90));
releaseCoils();
break;
case 'l': case 'L':
Serial.println(">> -90 deg");
motor.step(degToSteps(-90));
releaseCoils();
break;
case 'h': case 'H':
Serial.println(">> +180 deg");
motor.step(degToSteps(180));
releaseCoils();
break;
case 'x': case 'X':
Serial.println(">> coils off");
releaseCoils();
break;
default:
return;
}
}Step 3 — upload and play
Open Serial Monitor at 9600 baud. Type:
r+ Enter → motor advances 90° clockwise, coils release.rthree more times → back to start.l→ 90° anti-clockwise.h→ 180° clockwise.
After each move the coils release (current off) — the motor freewheels until the next command. Hold the shaft between moves; it has no holding torque while released. This is the "low-heat" pattern: only energise when commanding motion.
Step 4 — measure how long each move takes
Add a timer around the step call:
unsigned long t0 = millis();
motor.step(degToSteps(90));
unsigned long elapsed = millis() - t0;
Serial.print(">> +90 deg took "); Serial.print(elapsed); Serial.println(" ms");You should see ~1250 ms for a 90° move at 12 RPM (one quarter rev = 5 s ÷ 4 = 1.25 s). This is the headline reason step() blocks — it can't return earlier because the motor physically isn't there yet.
Step 5 — speed sweep test
In a small standalone sketch, call setSpeed with values 5, 10, 15, 20, 25 RPM, then command one full revolution at each. You'll feel:
- 5 RPM: smooth and slow, no skipped steps.
- 10 RPM: smooth and faster.
- 15 RPM: about as fast as it'll reliably go. Slight whine.
- 20 RPM: motor buzzes but may skip steps — ends at a different position than expected.
- 25 RPM: buzz, no rotation. Far above the rotor's ability to keep up.
Confirms yesterday's "~15 RPM is the limit" finding.
Step 6 — preview AccelStepper (no install today)
The third-party AccelStepper library (available via Sketch → Include Library → Manage Libraries) supports acceleration profiles, non-blocking motion, and synchronised multi-motor moves. Same wiring, smarter library. For most school builds the built-in Stepper is enough; for advanced robotics (Level 4's self-balancer needs encoders, not steppers, but the same non-blocking pattern), AccelStepper is the upgrade.
Try It Yourself 15 min
Goal: Add a fifth command f that does one full revolution forward, releases the coils. Then add F (capital) that does one full revolution reverse.
Hint
case 'f':
motor.step(STEPS_PER_REV);
releaseCoils();
break;
case 'F':
motor.step(-STEPS_PER_REV);
releaseCoils();
break;Note we're using upper vs lower case as the "direction modifier" — a common keyboard-only UX trick.
Goal: Read a number typed into the Serial Monitor as the rotation in degrees, then step the motor by that many degrees. So typing 45 rotates the shaft 45°. Negative numbers work too.
Hint
if (Serial.available() == 0) return;
int deg = Serial.parseInt();
if (deg == 0) return;
Serial.print(">> "); Serial.print(deg); Serial.println(" deg");
motor.step(degToSteps(deg));
releaseCoils();parseInt() returns 0 if no number arrives within the timeout, so we filter that out. For a stricter parser, see L01-26.
Goal: Add an "adjustable speed" pot on A0: each command reads the pot just before stepping and calls setSpeed() with a value 5–15 RPM scaled from the pot. So users can throttle the rotation live.
Hint
int pot = analogRead(A0);
int rpm = map(pot, 0, 1023, 5, 15);
motor.setSpeed(rpm);
Serial.print(">> speed "); Serial.print(rpm); Serial.println(" RPM");
motor.step(degToSteps(deg));Important: call setSpeed() before step() for the new speed to take effect on this move. Calling it after has no effect on a step already in progress.
Mini-Challenge · Compare your two sketches 10 min
- Open yesterday's
stepper-manual.inoand today's library version side by side. - Count lines in each (ignore blank lines and comments).
- Write down what's the same: the wiring, the rotation result.
- Write down what's different: the sequence table is gone, the phase counter is gone, the per-step
delayis gone — all replaced bysetSpeed+step. - Write one sentence per: when would you use the manual sketch instead of the library? (Answer: when you need wave-drive instead of full-step for power saving, or when you want to do non-trivial sequencing the library doesn't support — neither is common in school builds.)
This kind of "manual then library" pairing is the standard way to learn Arduino-style abstractions. You did it with Servo (L03-01 → L03-02) and you'll do it again with I²C (L03-17 → L03-18) and SPI (L03-20 → L03-21). The pattern: understand the protocol once by hand, then trust the library forever.
Recap 5 min
The Stepper library is the "Servo library" equivalent for steppers — bundled with the IDE, three method calls, removes the sequence table from your sketch. Pin-order gotcha: Stepper motor(steps, 8, 10, 9, 11), not 8, 9, 10, 11 — because the library expects the bipolar-coil pin order. setSpeed(rpm) sets rotation rate; step(n) commands a relative move (positive or negative). Stays under ~15 RPM on the 28BYJ-48. The library's only downside: step() blocks until the move finishes — for non-blocking stepper motion, reach for AccelStepper. Tomorrow you turn the "quarter-turn-on-command" rig into a finished product: a rotating display platform.
Stepperlibrary- Built-in Arduino library that hides the coil-firing sequence behind
setSpeedandstep. Works for both unipolar (with ULN2003) and bipolar (with H-bridge) steppers. - Constructor
- A function called when you create an object (e.g.
Stepper motor(...)). ForStepperit takes the steps-per-revolution and four pin numbers in coil-pair order. setSpeed(rpm)- Sets the rotation rate in revolutions per minute. The library calculates the required step delay from this and the steps-per-revolution.
step(n)- Commands a relative move by
nsteps. Positive forward, negative reverse. Blocks until done. - Blocking call
- A function that doesn't return until its work is finished. The opposite of non-blocking. For a stepper move of 1 revolution at 12 RPM, that's a 5-second pause.
- AccelStepper
- Third-party library for non-blocking stepper motion with acceleration profiles. Install from the Library Manager. Useful for synchronised multi-motor builds.
- Pin-order swap
- The 28BYJ-48's common-wisdom pin order for the
Stepperlibrary: D8, D10, D9, D11 instead of D8, D9, D10, D11. Get it wrong and the motor vibrates without turning. - Coil release
- Writing LOW to all four control pins after a move so the motor isn't continuously energised. Saves power and heat at the cost of holding torque.
Homework 5 min
- Save today's library-based sketch as
stepper-library.ino. - Bring a piece of cardboard or a small wooden disc (~10 cm diameter) for tomorrow's rotating display platform. We'll glue it to the stepper's output shaft as a tiny turntable.
- Think about: what would you display on top of a rotating platform? A phone? A 3D-printed model? A photo? A glass with a drink for a slow drink-display demo? Bring an idea — bonus marks if you bring the prop.
Bring back next class:
- Saved library sketch.
- Cardboard / wooden disc for the turntable.
- Your "what should rotate?" idea.