Built-in project · Arduino Uno R3

Stepper turntable on an Arduino Uno

A photo turntable on a 28BYJ-48 and its ULN2003 board with AccelStepper: press the button and the plate turns a full circle in eight stops, flashing the camera LED at each. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $33. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Driving a stepper motor through a ULN2003 board
  • Half stepping and steps per turn
  • Smooth acceleration with the AccelStepper library
  • Non-blocking motion in a loop
  • Why the coil pin order matters
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 4

How it works

A 28BYJ-48 stepper motor on its ULN2003 board turns a plate. The board's four inputs go to pins 8, 9, 10 and 11. A button on pin 2 goes to ground.

Press the button and the plate makes one full circle in eight stops. At each stop it pauses while pin 13, the Uno's own LED, blinks to act as a camera trigger. The motor speeds up and slows down for each stop. After the eighth photo and the last turn, the motor outputs are switched off and it waits for another press.

Parts list

4 parts, plus the jumper wires. They retail for about $33; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Stepper motor, 28BYJ-48 with ULN2003 pin IN4Arduino Uno R3 pin 11
Stepper motor, 28BYJ-48 with ULN2003 pin IN3Arduino Uno R3 pin 10
Stepper motor, 28BYJ-48 with ULN2003 pin IN2Arduino Uno R3 pin 9
Stepper motor, 28BYJ-48 with ULN2003 pin IN1Arduino Uno R3 pin 8
Pushbutton pin 1aArduino Uno R3 pin 2
Stepper motor, 28BYJ-48 with ULN2003 pin VCCArduino Uno R3 pin 5V
Pushbutton pin 2aArduino Uno R3 pin GND
Stepper motor, 28BYJ-48 with ULN2003 pin GNDArduino Uno R3 pin GND

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Stepper turntable: a photo turntable on a 28BYJ-48 and its ULN2003 board,
// with AccelStepper. Press the button and it turns the plate a full circle in
// eight stops, pausing at each one while the LED fires the camera.
//
//   ULN2003  IN1 pin 8, IN2 pin 9, IN3 pin 10, IN4 pin 11, + to 5V, - to GND
//   button   pin 2 to GND, INPUT_PULLUP
//   LED      pin 13, the camera trigger (the Uno's own LED)
//
// The pins go to AccelStepper as 8, 10, 9, 11: IN1, IN3, IN2, IN4. The library
// numbers its four pins for a motor on H bridges, and that order is what makes
// its sequence walk a ULN2003's four coils round in order. Given 8, 9, 10, 11
// the motor hums and stays where it is.
//
// HALF4WIRE is half stepping: 4096 steps to a turn of the output shaft, so a
// stop every eighth of a turn is 512 steps. The plate speeds up and slows down
// for each stop, which is what keeps a real one from sliding the object on it.
// run() makes at most one step a call, so the loop never waits while the
// plate is moving.

#include <AccelStepper.h>

const int BUTTON = 2;
const int SHUTTER = 13;
const int STOPS = 8;
const long STEPS_PER_STOP = 4096 / STOPS;  // 4096 half steps to a turn

AccelStepper plate(AccelStepper::HALF4WIRE, 8, 10, 9, 11);

int stop = 0;
bool turning = false;

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(SHUTTER, OUTPUT);
  plate.setMaxSpeed(800);
  plate.setAcceleration(400);
  Serial.begin(115200);
  Serial.println("turntable: press the button to start a circle");
}

void photo() {
  // One digit is all eight stops need.
  Serial.print("photo ");
  Serial.write('1' + stop);
  Serial.println();
  digitalWrite(SHUTTER, HIGH);
  delay(200);
  digitalWrite(SHUTTER, LOW);
  delay(300);
}

void loop() {
  if (!turning) {
    if (digitalRead(BUTTON) == LOW) {
      turning = true;
      stop = 0;
      plate.setCurrentPosition(0);
      photo();
      plate.moveTo(STEPS_PER_STOP);
    }
    return;
  }
  if (plate.distanceToGo() != 0) {
    plate.run();
    return;
  }
  stop++;
  if (stop < STOPS) {
    photo();
    plate.moveTo((stop + 1) * STEPS_PER_STOP);
  } else {
    Serial.println("full circle: done");
    plate.disableOutputs();
    turning = false;
  }
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Sets the button as INPUT_PULLUP and the shutter pin as an output. It limits the plate to a top speed of 800 and an acceleration of 400, which makes the starts and stops gentle. Then it opens serial at 115200.

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(SHUTTER, OUTPUT);
  plate.setMaxSpeed(800);
  plate.setAcceleration(400);
  Serial.begin(115200);
  Serial.println("turntable: press the button to start a circle");
}

photo sketch.ino

Prints the number of the photo, then lights the shutter pin for 200 ms, turns it off and waits another 300 ms. The plate is standing still during this.

void photo() {
  // One digit is all eight stops need.
  Serial.print("photo ");
  Serial.write('1' + stop);
  Serial.println();
  digitalWrite(SHUTTER, HIGH);
  delay(200);
  digitalWrite(SHUTTER, LOW);
  delay(300);
}

loop sketch.ino

When idle, it waits for the button, then resets the position, takes the first photo and aims at the first stop. While turning, plate.run() makes at most one step per call, so the loop never blocks. When the plate arrives it takes a photo and sets the next stop, 512 steps further, until STOPS is reached.

void loop() {
  if (!turning) {
    if (digitalRead(BUTTON) == LOW) {
      turning = true;
      stop = 0;
      plate.setCurrentPosition(0);
      photo();
      plate.moveTo(STEPS_PER_STOP);
    }
    return;
  }
  if (plate.distanceToGo() != 0) {
    plate.run();
    return;
  }
  stop++;
  if (stop < STOPS) {
    photo();
    plate.moveTo((stop + 1) * STEPS_PER_STOP);
  } else {
    Serial.println("full circle: done");
    plate.disableOutputs();
    turning = false;
  }
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The motor hums but does not turn. This project
    The pins are given to AccelStepper in the order 8, 10, 9, 11, not 8, 9, 10, 11. Leave that order in the code, and wire IN1 to IN4 to pins 8 to 11.
  • Nothing happens when you press the button. This project
    Pin 2 goes to the button and the other side to GND. The pull-up is inside the Uno.
  • The input reads random HIGH and LOW when the button is not pressed. Pushbutton
    A floating pin picks up noise. Add a 10 k pull-down or pull-up resistor, or use INPUT_PULLUP and wire the button between the pin and GND. Then pressed reads LOW.
  • The button seems always pressed, or never changes. Pushbutton
    A 4-leg tactile switch has legs joined in pairs. Place it across the breadboard center gap so the pairs sit on different rows. Wired along one side, the legs are already connected.
  • The motor buzzes or shakes and does not turn. The coil pins IN1 to IN4 are in the wrong order for the library. Stepper motor
    For Arduino's Stepper library on a 28BYJ-48, create it with pins 8, 10, 9, 11 (IN1, IN3, IN2, IN4). Or use AccelStepper with HALF4WIRE and the same pin order.
  • It stalls or skips when spun fast or powered from the Arduino 5 V pin. It also draws more than a USB port may give. Stepper motor
    Power the ULN2003 board from a separate 5 V supply and join the grounds. Run it slowly, about 10 to 15 RPM, and ramp up speed gradually.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.
  • Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
    Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.