Result. A 28BYJ-48 stepper turns cleanly up to 18 RPM, driven by the ESP32 through the PCA9685 servo driver and a ULN2003 board. This is milestone 1 of the pen plotter. Ten turns measured 4096 half-steps per turn exactly. It took four problems to get there, one of them a design mistake.
The setup.
| Part | Goes to |
|---|---|
| PCA9685 channels 12–15 (yellow PWM pins) | ULN2003 IN1–IN4, through servo-board columns 24–27 (two male–female jumpers per column, no female–female ones in the bin) |
| ULN2003 + / − | servo board's bottom rails: MB102 5 V side |
| Variable adapter | 7.0 V into the MB102 → rails 5.0 V |
| 28BYJ-48 | white plug into the ULN2003's socket |
| PCA9685 | as for the servos: SDA 33, SCL 32, VCC ← GPIO 25, GND |
What happened
1. It worked once. The slow demo walked the four LEDs A → AB → B → BC → … with a tick at every step, and the first @turn 1 made a full turn at 10 RPM.
2. Then it stopped turning. Ten turns ended "a quarter turn past the mark", and the next moves barely moved the shaft. Lost steps always leave a stepper short, never past, so that reading was already a sign something was wrong.
3. The supply sagged. 3.84 V at the ULN2003 instead of ~5. With the adapter at 5.36 V, the MB102 (0.4 V diode + a regulator that needs ~1 V of headroom under load) couldn't hold 5 V. Adapter to 7.0 V: rails 5.0 V, 4.92 V at the ULN2003 while stepping. It still hummed.
4. Ruling things out. The LEDs walked in order and every step ticked, so the coils and wiring were fine. The other two coil orders (1-3-2-4, 1-2-4-3) only made it worse. The I2C messages all arrived (0 errors), even slowed to 100 kHz. Taking the flag off changed nothing.
5. A jammed motor. The first motor's shaft wouldn't budge by hand: its gearbox had seized. Set it aside. But a fresh motor, which turns by hand with normal resistance, also only managed about 2 RPM.
6. The real cause: the driver's 50 Hz. The PCA9685 only applies new settings at the end of each PWM cycle, so LEDs and servos don't glitch. The sketch ran it at 50 Hz for servos, so a coil could change only every 20 ms. At 2 RPM the motor needs ~136 half-steps a second: the coils jumped 2–3 half-steps at a time, which the rotor could just follow. At 6 RPM they jumped 8 at once, and the rotor couldn't tell which way to go, so it hummed. At 1500 Hz a coil can change every 0.67 ms: a full turn at 10 RPM, then clean quarter turns at 12, 15 and 18 RPM.
Measurements
| Half-steps per turn | 4096 (10 turns at 10 RPM stopped ~1° past the mark: 40,960 ÷ 10.003) |
| Ten turns | 60.7 s at 10 RPM |
| Supply under load | 4.92 V at the ULN2003 (adapter 7.0 V) |
| Top speed tested | 18 RPM, clean |
What I learned
- A stepper that misses steps falls short. If it ends up past the target, it wasn't stepping properly at all.
- Hum and twitch with the LEDs walking correctly means the coils are on but the rotor can't follow: the order, the timing, or something resisting.
- A stepper is strongest at low speed. "Works slowly, stalls fast" points at strength, friction or step timing, not wiring.
- A part's settings can lag behind its commands. The PCA9685 latches new values once per PWM cycle, so its frequency limits how fast an output can change. It's one frequency for all 16 channels, so the plotter's pen servo needs its own ESP32 pin.
- Feel the shaft. A 28BYJ-48 turns by hand with firm, even resistance. One that won't budge has a jammed gearbox.
- My motors are exactly 64:1 (4096 half-steps per turn), not the 63.68:1 many of them are. Measured, not assumed.
Comments (0)