Drive a 28BYJ-48 stepper motor from an Arduino
- 192parts on the bench
- 25boards running now
- 1.00xreal time, on every board
The 28BYJ-48 with its green ULN2003 driver board is the first stepper motor most people meet, and it behaves nothing like a DC motor. It does not go at a speed; it goes to a position and stays there. The circuit above makes that obvious: a potentiometer on A0 sets where the shaft should point, and the motor walks there and stops. There is a logic analyzer on the coil lines too, so you can see the pattern the Uno is sending.
Open it in the editor and turn the knob. Turn it quickly and the shaft does not jump: it follows at its own top speed, because the simulated motor misses steps that come too fast, as a real one does.
What you need
- An Arduino Uno
- A 28BYJ-48 stepper motor with a ULN2003 driver board
- A 10 k potentiometer
- A breadboard and jumper wires
Wiring
Four coils and a table
The 28BYJ-48 is a unipolar motor: the red wire is common and goes to 5 volts, and each of the other four wires is the far end of one coil. Each channel on the ULN2003 board pulls one coil’s end to ground, current flows, and that coil becomes a magnet. The rotor lines up with whichever magnet, or pair of magnets, is on. Switch them in the right order and it walks round.
There are three common orders. Wave drive switches one coil at a time. Two-phase full step switches two neighboring coils at a time, for twice the torque. Half-step alternates between one and two coils, which gives twice as many, smaller steps. The simulated motor is not told which mode a sketch is using: it works out where the magnetic field points from the four coil lines and moves the rotor there, so all three work because they all fall out of the same physics.
Where 4096 comes from
Every 28BYJ-48 sketch has a 4096 or a 2048 in it. The rotor turns 5.625 degrees per half-step, 64 half-steps for one turn of the rotor, and the gearbox then divides by 64 again. So one half-step moves the output shaft 0.088 degrees, and a full turn is 64 times 64, which is 4096 half-steps, or 2048 full steps. The example sweeps half a turn across the knob’s travel.
There is one honest deviation worth knowing: a real gearbox is 63.68 to 1 rather than exactly 64, so a real motor comes back about a degree and a half short after 4096 steps. The simulated one uses the datasheet’s round 64.
void loop() {
int knob = analogRead(KNOB);
if (knob > lastKnob - DEADBAND && knob < lastKnob + DEADBAND) {
return;
}
lastKnob = knob;
long want = ((long)knob * SWEEP_STEPS) / 1023;
long move = want - at;
if (move == 0) return;
motor.stepHalf(move, 1500); // microseconds between half-steps
at = want;
motor.release(); // let the coils go once it is there
}It does not know where it is
Nothing in this circuit reads the shaft. The sketch counts the steps it has sent and assumes the motor made them all. That is true until something goes wrong: a jam, a heavy load, or steps sent faster than the rotor can follow. After that, the count and the shaft disagree and the sketch never finds out. Real positioning machines, from 3D printers to plotters, solve this with a limit switch they drive into at start-up to find a known zero.
The deadband at the top of the loop is a small detail with a real purpose. The last bit of an analog reading wobbles, and without the deadband the motor would twitch back and forth by a step forever. The release() at the end switches all four coils off once the shaft is in place, so it stops drawing current. The price is that nothing holds it there.
Try it in the editor
Open the circuit in the editor and change the 1500 in stepHalf() to 500. Now the steps come faster than the motor can follow. Turn the knob a long way and watch the shaft: it falls behind the count and never catches up, because the missed steps are simply lost. The serial monitor still reports the angle the sketch thinks it reached, which is the whole problem with open-loop positioning in one experiment.
Put it back and try stepFull() instead of stepHalf(), which switches two coils at a time. Look at the logic analyzer: the four coil lines now show the two-phase pattern rather than the half-step one.
Removing release() leaves the coils energized when the shaft stops, which holds it in place against a load, at the cost of the current it draws on a real bench.
Common mistakes
Coils wired out of order. The motor buzzes or shuffles back and forth instead of turning. IN1 to IN4 must follow the sequence the code expects.
Stepping too fast. The 28BYJ-48 cannot follow steps closer together than about a millisecond. The example leaves 1.5 milliseconds between half-steps. Go faster and it loses steps, here and on your desk.
Powering it from the board’s 5 volt pin on real hardware. Each coil draws around 80 milliamps, and a USB-powered Uno is not happy feeding that for long. The simulator does not model supply current, so it will not warn you.
Expecting it to be strong. Each driver channel drops about a volt, so the coils see nearer 4 volts than 5, and the torque is modest.
Questions
Can I use the Arduino Stepper library?
The standard Stepper.h is not one of the headers the browser compiler carries. The example uses Mokxi’s own stepper helper, and the step sequence is short enough to write out by hand with digitalWrite() if you prefer.
Why does my 28BYJ-48 vibrate but not turn?
Usually the coil order is wrong, or the steps are too fast. Check that IN1 to IN4 are on the pins the sketch names, in that order, and slow the stepping down.
How many steps per revolution is the 28BYJ-48?
4096 half-steps or 2048 full steps for one turn of the output shaft, using the datasheet’s 64 to 1 gearbox.
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.