576  Stepper Motors

Learning Objectives

After completing this chapter, you will be able to:

  • Understand stepper motor construction and step sequences
  • Control stepper motors with ULN2003 and A4988/DRV8825 drivers
  • Implement acceleration profiles to prevent missed steps
  • Use the AccelStepper library for smooth motion
  • Configure microstepping for increased resolution
  • Size stepper motors for precision applications

576.1 Stepper Motor Fundamentals

Stepper motors move in discrete steps, providing precise position control without feedback sensors (open-loop control).

Characteristics:

  • Precise angular positioning (typically 1.8 deg/step = 200 steps/rev)
  • Open-loop control (no encoder needed)
  • High holding torque at standstill
  • Step-by-step movement (can be jerky at low speeds)
  • Higher power consumption than servos

576.1.1 Step Sequences

Full-Step Sequence (28BYJ-48 with ULN2003):

Step IN1 IN2 IN3 IN4
1 1 0 0 0
2 0 1 0 0
3 0 0 1 0
4 0 0 0 1

Half-Step Sequence (smoother, double resolution):

Step IN1 IN2 IN3 IN4
1 1 0 0 0
2 1 1 0 0
3 0 1 0 0
4 0 1 1 0
5 0 0 1 0
6 0 0 1 1
7 0 0 0 1
8 1 0 0 1

576.2 28BYJ-48 with ULN2003

The 28BYJ-48 is a popular low-cost stepper for hobby projects.

Specifications:

  • Steps per revolution: 2048 (with 64:1 gearbox)
  • Operating voltage: 5V DC
  • Current: 200-300mA
  • Step angle: 5.625 deg/64 = 0.18 deg effective
#include <AccelStepper.h>

#define MOTOR_PIN1 19
#define MOTOR_PIN2 18
#define MOTOR_PIN3 5
#define MOTOR_PIN4 17

// 28BYJ-48 with 64:1 gearbox = 2048 steps per revolution
AccelStepper stepper(AccelStepper::HALF4WIRE,
                     MOTOR_PIN1, MOTOR_PIN3, MOTOR_PIN2, MOTOR_PIN4);

const int STEPS_PER_REV = 2048;

void setup() {
  Serial.begin(115200);

  stepper.setMaxSpeed(1000);       // Steps per second
  stepper.setAcceleration(500);    // Steps per second^2

  Serial.println("Stepper Motor Controller");
  Serial.println("Commands: 0-360 = angle, h = home, f = full rotation");
}

void loop() {
  if (Serial.available()) {
    String input = Serial.readStringUntil('\n');
    input.trim();

    if (input == "h") {
      Serial.println("Homing to 0 degrees...");
      stepper.moveTo(0);
    }
    else if (input == "f") {
      Serial.println("Full rotation...");
      stepper.move(STEPS_PER_REV);
    }
    else {
      int angle = input.toInt();
      if (angle >= 0 && angle <= 360) {
        long targetSteps = (long)angle * STEPS_PER_REV / 360;
        Serial.print("Moving to ");
        Serial.print(angle);
        Serial.println(" degrees");
        stepper.moveTo(targetSteps);
      }
    }
  }

  stepper.run();  // Must be called frequently
}

// Convert angle to steps
long angleToSteps(int angle) {
  return (long)angle * STEPS_PER_REV / 360;
}

// Convert steps to angle
int stepsToAngle(long steps) {
  return (int)(steps * 360 / STEPS_PER_REV);
}

576.3 NEMA 17 with A4988/DRV8825

For higher performance, NEMA 17 steppers with A4988 or DRV8825 drivers are the standard choice.

NEMA 17 Specifications:

  • Steps per revolution: 200 (1.8 deg/step)
  • Holding torque: 40+ kg-cm
  • Operating current: 1.2-2.0A per phase
  • Voltage: 12-24V

A4988 Driver Features:

  • Microstepping: 1, 1/2, 1/4, 1/8, 1/16
  • Current limit adjustment
  • Sleep mode for power saving
  • Step/direction interface

Microstepping Settings:

MS1 MS2 MS3 Resolution Steps/Rev
0 0 0 Full step 200
1 0 0 1/2 step 400
0 1 0 1/4 step 800
1 1 0 1/8 step 1600
1 1 1 1/16 step 3200
#include <AccelStepper.h>

#define STEP_PIN 25
#define DIR_PIN 26
#define ENABLE_PIN 27

// NEMA 17 with 1/16 microstepping = 3200 steps per revolution
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);

const int STEPS_PER_REV = 3200;

void setup() {
  Serial.begin(115200);

  pinMode(ENABLE_PIN, OUTPUT);
  digitalWrite(ENABLE_PIN, LOW);  // Enable driver (active LOW)

  stepper.setMaxSpeed(3000);      // Steps per second
  stepper.setAcceleration(1000);  // Steps per second^2

  Serial.println("NEMA 17 Stepper Ready");
}

void loop() {
  if (Serial.available()) {
    char cmd = Serial.read();

    switch(cmd) {
      case 'f':  // Forward one revolution
        stepper.move(STEPS_PER_REV);
        break;
      case 'b':  // Backward one revolution
        stepper.move(-STEPS_PER_REV);
        break;
      case 'h':  // Home position
        stepper.moveTo(0);
        break;
      case 's':  // Stop
        stepper.stop();
        break;
      case 'd':  // Disable (power saving)
        digitalWrite(ENABLE_PIN, HIGH);
        break;
      case 'e':  // Enable
        digitalWrite(ENABLE_PIN, LOW);
        break;
    }
  }

  stepper.run();
}

576.4 Acceleration Profiles

WarningWhy Acceleration Matters

Steppers cannot instantly change speed. Attempting to start at full speed causes missed steps because the rotor can’t keep up with the magnetic field changes.

Trapezoidal velocity profile:

  1. Acceleration phase: Gradually increase speed
  2. Constant velocity: Maintain max speed during travel
  3. Deceleration: Gradually decrease speed to prevent overshoot

The AccelStepper library handles this automatically!

// Manual trapezoidal profile (if not using AccelStepper)
void moveWithAcceleration(int targetSteps, int maxSpeed, int acceleration) {
  int currentSpeed = 0;
  int position = 0;
  int direction = (targetSteps > 0) ? 1 : -1;
  targetSteps = abs(targetSteps);

  // Calculate distances for accel/decel phases
  int accelSteps = (maxSpeed * maxSpeed) / (2 * acceleration);
  int decelSteps = accelSteps;
  int constSteps = targetSteps - accelSteps - decelSteps;

  if (constSteps < 0) {
    // Can't reach max speed, use triangular profile
    accelSteps = targetSteps / 2;
    decelSteps = targetSteps - accelSteps;
    constSteps = 0;
    maxSpeed = sqrt(2 * acceleration * accelSteps);
  }

  // Acceleration phase
  for (int i = 0; i < accelSteps; i++) {
    currentSpeed = sqrt(2 * acceleration * i);
    step(direction);
    delayMicroseconds(1000000 / currentSpeed);
  }

  // Constant speed phase
  for (int i = 0; i < constSteps; i++) {
    step(direction);
    delayMicroseconds(1000000 / maxSpeed);
  }

  // Deceleration phase
  for (int i = decelSteps; i > 0; i--) {
    currentSpeed = sqrt(2 * acceleration * i);
    step(direction);
    delayMicroseconds(1000000 / currentSpeed);
  }
}

576.5 Common Pitfalls

CautionPitfall: Undersized Current Limiting for Stepper Motors

The Mistake: Setting the stepper driver current limit to the motor’s rated current (e.g., 2.0A) without considering that stepper motors are rated for maximum holding torque at stall, not continuous operation.

Why It Happens: Datasheets list “rated current per phase” which represents the maximum the motor can handle when stationary with proper heat sinking.

The Fix: Start with 70-80% of rated current (e.g., 1.4-1.6A for a 2.0A motor) and increase only if torque is insufficient. Monitor motor temperature after 30 minutes of continuous operation - the case should not exceed 70-80C.

CautionPitfall: Using Open-Loop Control for Precision Applications

The Mistake: Assuming a stepper motor will always reach the commanded position without feedback, then discovering position errors accumulate over time.

Why It Happens: Stepper motors are marketed as “precise” (1.8 deg/step), creating false confidence. In reality, missed steps from overloading, mechanical binding, or acceleration too fast cause cumulative drift.

The Fix: For precision applications, add encoder verification or use limit switches for homing/calibration routines.

576.6 Interactive Lab: Stepper Precision

Challenges:

  1. Make the stepper rotate exactly 90 degrees
  2. Implement a clock with the second hand driven by the stepper
  3. Add buttons for forward/reverse step control

576.7 What’s Next?

Now that you understand motor control, you’re ready to explore relay and solenoid switching for on/off control applications.

Continue to Relays and Solenoids →