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.
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.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × PushbuttonIn Starter Kit, Christmas Lights Kit, Logic Lab Kit.
- 1 × Stepper motor, 28BYJ-48 with ULN2003
Kits that can build it
These 8 starter kits have every part in the circuit. Each kit's page lists what else it can build.
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 pin | Connects to |
|---|---|
| Stepper motor, 28BYJ-48 with ULN2003 pin IN4 | Arduino Uno R3 pin 11 |
| Stepper motor, 28BYJ-48 with ULN2003 pin IN3 | Arduino Uno R3 pin 10 |
| Stepper motor, 28BYJ-48 with ULN2003 pin IN2 | Arduino Uno R3 pin 9 |
| Stepper motor, 28BYJ-48 with ULN2003 pin IN1 | Arduino Uno R3 pin 8 |
| Pushbutton pin 1a | Arduino Uno R3 pin 2 |
| Stepper motor, 28BYJ-48 with ULN2003 pin VCC | Arduino Uno R3 pin 5V |
| Pushbutton pin 2a | Arduino Uno R3 pin GND |
| Stepper motor, 28BYJ-48 with ULN2003 pin GND | Arduino 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.
// 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.