If you need precise open-loop position control for an Arduino project, the default concrete pick for 90% of maker applications is a NEMA 17 bipolar stepper (specifically the StepperOnline 17HS19-2004S1) paired with a Pololu DRV8825 driver, running on a 12V to 24V supply. This combination delivers up to 59 N-cm (84 oz-in) of holding torque, handles the math for microstepping, and integrates seamlessly with the AccelStepper library.
But writing the step motor code for Arduino is only the final 10% of the job. The other 90% is mechanical sizing, current limiting, and wiring. If you skip the physics and just copy-paste code, your motor will hum, overheat, or stall at high speeds. Here is the bench-to-code pipeline for getting it right the first time.
The Stepper vs. Servo vs. DC Decision Matrix
Steppers are not interchangeable with servos or standard DC gear motors. Each topology has a distinct torque curve and control requirement. Use this decision path to confirm a stepper is actually what you need before buying parts.
| Motor Type | Torque Profile | Control Needs | Typical Cost (USD) |
|---|---|---|---|
| Stepper (NEMA 17) | Max torque at 0 RPM (holding); drops sharply at high RPM. | Open-loop step/direction pulses. No encoder required. | $12 - $25 (motor + driver) |
| DC Gear Motor | High torque at low RPM; relatively flat curve until no-load speed. | Closed-loop (needs encoder) for position. PWM for speed. | $15 - $40 (with encoder) |
| RC Servo | High torque within a limited arc (usually 180° or 270°). | Closed-loop internal pot. PWM signal (50Hz). | $10 - $30 |
- If you need continuous 360° rotation at high speeds (>300 RPM) with high torque → Choose a DC Gear Motor.
- If you need absolute position tracking without a homing switch and can tolerate a 270° physical limit → Choose a Servo.
- If you need high holding torque at a standstill, precise sub-millimeter linear movement via lead screws, and open-loop reliability under $25 → Choose a Stepper.
Sizing Your Stepper: Torque Curves and Load Math
The most common mistake beginners make is sizing a stepper based solely on its holding torque spec. Holding torque is what the motor outputs when it is completely stopped and energized. The moment it starts spinning, torque drops off a cliff due to the inductance of the coils limiting current rise time at higher step frequencies.
The Sizing Rule of Thumb: Calculate your required running torque, then apply a 2.5x safety factor. Select a motor whose holding torque exceeds this factored number.
Worked Load Example: Belt-Driven Z-Axis
Let's say you are building a camera slider lifting a 2 kg (4.4 lb) payload using a timing belt wrapped around a 3D-printed pulley with a 10 mm (0.01 m) radius.
- Calculate Force: F = mass × gravity = 2 kg × 9.81 m/s² = 19.62 N.
- Calculate Running Torque: Torque = Force × radius = 19.62 N × 0.01 m = 0.1962 N-m (or 19.62 N-cm).
- Apply Safety Factor: 19.62 N-cm × 2.5 = 49.05 N-cm.
You need a motor with a holding torque of at least 49 N-cm. The ubiquitous StepperOnline 17HS19-2004S1 is rated for 59 N-cm (84 oz-in). This gives you enough overhead to handle acceleration spikes and belt friction without stalling.
Driver Selection and Terminal Wiring
A microcontroller GPIO pin cannot source the 2 amps required by a NEMA 17. You need a dedicated chopper driver. While the A4988 is famous, it is outdated and loud. For 2026 builds, the Pololu DRV8825 is the baseline workhorse (handling up to 2.2A with cooling), and the BigTreeTech TMC2209 is the premium pick for silent operation and sensorless stall detection.
For this guide, we will wire the DRV8825.
Identifying Stepper Coils (Wiring)
NEMA 17 bipolar steppers have 4 wires, representing two distinct coils (A and B). Do not guess the colors; manufacturers change them constantly. Grab your multimeter and set it to continuity or resistance mode.
- Probe pairs of wires until you find two that show a low resistance (typically 1 to 5 ohms). This is Coil A.
- The remaining two wires will also show continuity with each other. This is Coil B.
- Wires from different coils will show infinite resistance (open loop).
- Connect Coil A to the driver's 1A and 1B (or A+, A-) terminals, and Coil B to 2A and 2B (or B+, B-). Polarity within the same coil only dictates rotation direction; if it spins backward, just swap the two wires of Coil A.
Setting the VREF (Current Limiting)
Never plug in a stepper without setting the driver's current limit potentiometer. Sending 2A through a 1.5A motor coil will melt the insulation and brick the motor.
The DRV8825 uses a sense resistor (Rs), usually 0.1Ω. The formula is:
VREF = Max Current × 8 × Rs
If your motor is rated for 2A, but you are running the DRV8825 without a heatsink and active fan (safe continuous limit ~1.5A), calculate for 1.5A:
VREF = 1.5 × 8 × 0.1 = 1.2V
Power the driver's logic (VDD) with 5V from the Arduino. Put your multimeter's black probe on the Arduino GND and the red probe on the metal top of the DRV8825 potentiometer. Turn the pot with a ceramic screwdriver until you read exactly 1.2V. Refer to the Pololu DRV8825 documentation to verify your specific board's sense resistor value.
Step Motor Code for Arduino: AccelStepper Implementation
Do not use the default Arduino Stepper.h library. It blocks the main loop and does not handle acceleration. If you command a heavy load to instantly jump to 1000 steps/second, the rotor's inertia will cause it to stall and hum. You must ramp the speed.
We use Mike McCauley's AccelStepper library. Install it via the Arduino Library Manager.
Pin Mapping Table
| DRV8825 Pin | Arduino Uno Pin | Function |
|---|---|---|
| STEP | 3 | Receives one pulse per microstep |
| DIR | 4 | High = CW, Low = CCW |
| EN | 5 | Active LOW to enable driver |
| M0, M1, M2 | GND (or specific pins) | Microstep resolution (see below) |
| VMOT | 12V-24V PSU (+) | Motor power (add 100μF cap!) |
| GND | PSU (-) & Arduino GND | Common ground is mandatory |
Complete Arduino Code
This code ramps the motor up to 800 steps/second, moves 6400 steps (exactly one full revolution in 1/32 microstepping mode), and ramps down safely.
#include <AccelStepper.h>
// Pin definitions
const int STEP_PIN = 3;
const int DIR_PIN = 4;
const int EN_PIN = 5;
// Define the stepper and the pins it uses (Driver type 1 = STEP/DIR)
AccelStepper stepper(1, STEP_PIN, DIR_PIN);
void setup() {
// Configure enable pin
pinMode(EN_PIN, OUTPUT);
digitalWrite(EN_PIN, LOW); // LOW enables the DRV8825
// Set microstepping pins on DRV8825 (M0=HIGH, M1=HIGH, M2=HIGH for 1/32)
// If hardwired to GND, leave these out and adjust max speed accordingly.
// Motor parameters (tune these to your mechanical load)
stepper.setMaxSpeed(800); // Max speed in steps/second
stepper.setAcceleration(400); // Acceleration in steps/second^2
// Move to target position
stepper.moveTo(6400); // 6400 steps = 1 rev at 1/32 microstepping
}
void loop() {
// Check if the motor has reached the target
if (stepper.distanceToGo() == 0) {
// Pause for 2 seconds before reversing
delay(2000);
// Reverse direction by negating the current position
stepper.moveTo(-stepper.currentPosition());
}
// run() must be called as frequently as possible to handle stepping
stepper.run();
}
Troubleshooting Failure Signatures: Hum, Overheat, and Stall
When your step motor code for Arduino fails, the hardware will tell you exactly what is wrong through physical symptoms. Here is how to read them.
1. The Motor Hums and Vibrates but Doesn't Spin
- Cause A (Most Likely): Acceleration is set too high in the code. The rotor cannot overcome inertia. Fix: Lower
setAcceleration()from 1000 to 200 and re-upload. - Cause B: Coils are wired out of phase (e.g., A+ and B+ mixed). Fix: Re-do the multimeter continuity test.
- Cause C: VREF is set too low, starving the motor of current. Fix: Re-measure the potentiometer voltage.
2. The Motor Overheats (Too Hot to Touch, >70°C)
- Cause: VREF is set too high, or you are running full-step mode at a standstill. Stepper motors draw maximum current when holding still. While NEMA 17s are rated to run hot (up to 80°C internally), excessive heat degrades 3D printed mounts and belts. Fix: Lower VREF by 0.2V. If your application allows, use the
enableOutputs(false)command in AccelStepper when the motion sequence is complete to cut holding current entirely.
3. The Motor Stalls at High Speeds
- Cause: You have hit the limits of the stepper's torque curve. Inductance prevents the current from reaching the target level before the next step pulse arrives. Fix: You cannot fix this in code. You must either increase the supply voltage (e.g., jump from 12V to 24V, which forces current through the inductance faster), add a mechanical gear reduction, or switch to a closed-loop DC servo system.






