Source: https://mokxi.com/learn/accelstepper

Learn

# AccelStepper on Arduino: run(), acceleration, homing and MultiStepper

Intermediate

Written by the Mokxi team

Start building free Open the editor

Or see every lesson.

- 455 parts on the bench
- 43 boards running now
- 1.00x real time, on every board

AccelStepper is the stepper library most Arduino projects end up on, because it speeds a motor up and slows it down, which the plain Stepper library does not, and it does it one step per call, so the rest of the sketch keeps running. In Mokxi it is built in, with MultiStepper.h beside it.

The circuit above is a photo turntable: a 28BYJ-48 on its ULN2003 board, an Uno and a start button. Press the button and the plate turns a full circle in eight stops of 512 half-steps, easing in and out of each one, while the serial monitor counts the photos.

Every number on this page was printed by a sketch run on Mokxi’s engine, on a 28BYJ-48 or on a NEMA 17 behind an A4988.

## Make a motor: DRIVER for an A4988, HALF4WIRE for a 28BYJ-48

The constructor says how the motor is wired. AccelStepper stepper(AccelStepper::DRIVER, 3, 4); is a driver board that takes STEP and DIR, such as an A4988 or a DRV8825, with STEP named first. AccelStepper stepper(AccelStepper::HALF4WIRE, 8, 10, 9, 11); is four pins straight into a ULN2003 board, half-stepping, and FULL4WIRE is the same four pins in full steps.

Every number after that is in the unit the constructor chose. A 28BYJ-48 turns once in 4096 steps with HALF4WIRE and in 2048 with FULL4WIRE. A 200-step NEMA 17 turns once in 200 STEP pulses at full step and in 3200 at 1/16, because in DRIVER mode one AccelStepper step is one pulse and the driver decides how far a pulse goes.

The sketch below moves a 28BYJ-48 a quarter turn. It printed "at 1024 after 3116 ms" and left the shaft at 90.0 degrees.

A 28BYJ-48 on HALF4WIRE, a quarter turn

## Why the 28BYJ-48’s pins go in as 8, 10, 9, 11

AccelStepper numbers its four pins for a motor on two H bridges, where the first two pins are one coil and the last two the other. A ULN2003 board numbers its four coils in order around the motor instead. Handing the pins over as IN1, IN3, IN2, IN4 is what makes the library’s sequence walk those coils around in turn.

Give it 8, 9, 10, 11 and the same sketch still printed "at 1024 after 3116 ms", but the shaft never left 0 degrees. In that order the sequence switches on opposite coils together, whose pulls cancel, and the field never walks around in one direction, so the rotor is tugged back and forth on the spot. That is the motor that hums and does not move, and the library cannot tell: currentPosition() counts the steps it sent, not the steps the shaft made.

## run(), runSpeed() and runToPosition()

run() makes at most one step per call, when one is due, and works out the next step’s speed from the acceleration, so it belongs in loop() with nothing slow beside it. runSpeed() steps at the constant speed setSpeed() gave, with no ramp and no target. runToPosition() and runToNewPosition() call run() until the motor arrives, and nothing else runs until they return.

Here is run() on a NEMA 17 behind an A4988 in full step, with setMaxSpeed(500), setAcceleration(250) and moveTo(1000), printing the time, the position and the speed every half second. It printed "500 35 133", "1000 132 257", "1500 291 381" and "2000 512 493", the peak, then "2500 727 369", "3000 880 245" and "3500 971 121", and "at 1000 after 3893 ms". The arithmetic says four seconds: two to reach 500 steps a second at 250 a second squared, covering 500 steps, and two to stop.

The same move with runToNewPosition(1000) printed "there after 3889 ms" and the move back "back after 7779 ms", with nothing in between, because loop() never ran. runSpeed() with setSpeed(200) made 199 steps in its first second: a constant speed from the first step, and no ramp.

run() on an A4988, printing the ramp

## setMaxSpeed() and setAcceleration(): why the ramp matters

The 28BYJ-48’s datasheet says it starts from a standstill on up to 600 steps a second and keeps up with more than 1000 once it is turning, and Mokxi’s model has both figures.

So we asked it for 900 full steps a second, FULL4WIRE and moveTo(2048), which is one turn. With setAcceleration(500) it printed "at 2048 after 4054 ms" and the shaft went all the way around. With setAcceleration(100000), which is no ramp at all, it printed "at 2048 after 2304 ms" and the shaft ended 0.8 degrees from where it started: it lost nearly every step. The library reported the same position both times.

Half-steps move the field half as far, and Mokxi’s 28BYJ-48 follows them from rest up to one a millisecond, so HALF4WIRE at 800 steps a second is safe with any ramp. At 1200 half-steps a second with no ramp, a half turn ended 0.9 degrees from the start.

Mokxi’s NEMA 17 follows up to 1000 full steps a second whatever the acceleration. A real one loses torque as it speeds up, which Mokxi does not model, so on a bench the ramp matters more.

## An A4988 or a DRV8825: DRIVER mode and the enable pin

In DRIVER mode the library holds STEP high for setMinPulseWidth() microseconds, 1 by default; a DRV8825 needs 1.9, so call setMinPulseWidth(2). At 1/16 step every speed that was right at full step needs multiplying by 16.

The A4988’s EN pin is active low. Hand it over with setEnablePin(8) and setPinsInverted(false, false, true), and disableOutputs() lets the shaft go after a move, which keeps things cool at the cost of holding torque.

## A homing routine with a limit switch

At power-up AccelStepper believes the motor is at 0. Homing fixes that: creep toward a switch until it closes, call setCurrentPosition(0), and count from there.

Mokxi has a mechanism to try it on: the cabinet in the arcade claw project, whose gantry is a NEMA 17 on a GT2 belt, 0.2 mm a full step, with a lever switch at the home end that the carriage presses for the last fifth of a millimeter. We put its X axis on an A4988 in full step and the switch on pin 2 with the pin’s pull-up, and ran the sketch below. With the carriage 120 mm out it printed "switch closed after 601 steps, 3006 ms", then "at 500, switch 1": 100 mm out and off the switch again. From 60 mm it took 301 steps. With the switch’s wire taken off it printed "homing failed: no switch within 1200 steps" instead of driving the carriage into the end forever, which is the line every homing routine needs.

The search uses runSpeed(), so it is steady and stops on the step the switch closes, and setCurrentPosition() also zeroes the speed. A printer then backs off and touches the switch again, slower, as the stepper homing project does. If your axis heads away from its switch, flip the sign of the speed.

Homing an axis against a switch

## MultiStepper: two motors that arrive together

MultiStepper moves several AccelSteppers so that they finish at the same moment, as a plotter needs for a straight line. It finds the move that takes longest at its own top speed and slows the others to match, with no acceleration.

On two NEMA 17s behind two A4988s, both at setMaxSpeed(400), targets of 800 and 200 arrived together and the sketch printed "800 200 2018 ms": two seconds is 800 steps at 400 a second, so the second motor ran at a quarter of that.

MultiStepper with two A4988s

## AccelStepper on an ESP32

On an ESP32 only the pins change, and the logic level: an A4988 counts a high from 0.7 of its VDD, 3.5 V on 5 V, which a 3.3 V pin never reaches. We ran the sketch below on an ESP32-C3, STEP on GPIO 4 and DIR on GPIO 5. With the A4988’s VDD on 5 V it printed "done after 1869 ms" and the shaft never moved. With VDD on the board’s 3.3 V pin it turned once. A DRV8825 reads 2.2 V as high, so it needs no change.

AccelStepper on an ESP32-C3 and an A4988

## The mistake that makes it stutter: delay() in loop()

Because run() makes one step per call at most, anything slow in loop() caps the speed. The 1000-step move above took 3893 ms. With a delay(10) added to loop() it took 10802 ms, because the motor could make at most one step every 10 milliseconds, 100 a second, whatever setMaxSpeed() said. Keep loop() short and time other work with millis().

There is a ceiling even with nothing else in loop(). On an Uno in Mokxi, run() made about 3900 steps a second at most, however high setMaxSpeed() was set, because after every step it works out the next interval in floating point, three divides and a handful of multiplies, which an Uno does in software. runSpeed(), which does no arithmetic per step, made 9281 steps in a second at setSpeed(10000). The original library’s documentation gives about 4000 steps a second for an Uno.

## Questions

Is AccelStepper built into Mokxi?

Yes. AccelStepper.h and MultiStepper.h are built in, written from the library’s documented calls and speed profile, so a sketch that includes them compiles as it stands.

What is the difference between run() and runSpeed()?

run() accelerates toward the target moveTo() set and slows down to stop on it. runSpeed() steps at the constant speed setSpeed() set, with no acceleration and no target. Both make at most one step per call, so call them as often as you can.

Why does currentPosition() change when my motor does not move?

AccelStepper counts the steps it sends, not the steps the shaft makes. On a 28BYJ-48 the usual cause is the pin order: give the pins as IN1, IN3, IN2, IN4. On a driver board, check EN, the link from RST to SLP and the motor supply.

How many steps is one revolution of a 28BYJ-48 in AccelStepper?

4096 with HALF4WIRE and 2048 with FULL4WIRE, using the datasheet’s 64 to 1 gearbox.

Related

## Keep going

Arduino Stepper Motor: 28BYJ-48 and ULN2003 Arduino A4988 and NEMA 17 Stepper: Wiring and Code Stepper Motor Vibrating but Not Turning: Fixes Arduino DRV8825: Wiring, Code and Microstepping A4988 Pinout: Pins, Microstep Table and Vref Stepper Motor Wiring: 4, 5 and 6 Wire Motors Arduino millis(): Timers and Multiple Tasks The arcade claw machine, whose gantry the homing sketch runs on Advanced Stepper homing with a fast and a slow touch Intermediate How the 28BYJ-48 is modeled Stepper speed control with a knob on a TMC2209 Intermediate The project page, with the full sketch Intermediate

Sources

## Where the facts on this page come from

- AccelStepper class reference: the constructor, run(), runSpeed() and runToPosition()
- Kiatronics 28BYJ-48 5 V datasheet: in-traction above 600 Hz, out-traction above 1000 Hz

## Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.

Start building free Open the editor
