← SHEET 02 · ASSEMBLIES EL-008

Stepper Camera Slider Controller

ELROBOTICS
LIVE DRAWING — HOVER OR DRAG TO CRANK · BUILT FROM THE REAL PLANT PARAMETERS
PART NOEL-008
MATL / SYSTEMESP32 · A4988 · NEMA17
TOOLSArduino IDE · Wokwi · C++

This one bridges the mechanism side of the portfolio and the electronics side: the same belt-and-carriage kinematics behind the folding staircase linkage apply here to a single translating axis, but the interesting problem moves from geometry to timing — how do you get a stepper from rest to a cruise speed and back to rest smoothly, without a motion library doing the math for you. This project answers that with its own trapezoidal step-timing engine.

OVERVIEW & MOTIVATION

Most hobby slider builds pull in AccelStepper and stop thinking about motion profiles. This one deliberately doesn’t: runTrapezoidMove() derives every step’s timing from s = 1/2 a t^2 directly, so the accel ramp, cruise, and decel are all provable from the same one-line kinematic identity rather than borrowed from a library’s internals. Layered on top of that engine: homing against a microswitch endstop with a fast-seek/back-off/slow-creep sequence for repeatable zeroing, soft travel limits measured from that zero, and an OLED + rotary-encoder menu that exposes move distance/speed and a time-lapse mode (N frames, M-second interval, opto-isolator shutter pulse per frame). The target platform is a GT2-belt NEMA17 slider — the same building block behind 3D-printer axes and DIY camera dollies.

COMPONENTS & BOM

REFCOMPONENTSPECROLE
U1ESP32 DevKit-C V4dual-core, 3.3 V logiccontroller, runs the profile generator + UI
U2A4988 stepper driverup to 1/16 microstep, current-limiteddrives the NEMA17 from STEP/DIR pulses
M1NEMA17 stepper1.8°/step (200 steps/rev), ~1.2 A/phasebelt drive motor
SW1Microswitch / pushbutton endstopnormally-open, active LOWhoming reference at one end of travel
U3SSD1306 OLED128×64, I2C, addr 0x3Cmenu / status display
ENC1KY-040 rotary encoderquadrature + push switchmenu navigation and value entry
OPT1Opto-isolator (e.g. 4N35)GPIO-driven LED sideisolated camera remote-shutter trigger
GT2 belt + 20T pulley2 mm pitchtranslates rotation to carriage travel
PSU112 V supplysized to NEMA17 currentA4988 VMOT motor power (not MCU-connected)

WIRING

ESP32 DEVKIT-C V4 SSD1306 OLED 128x64 · I2C 0x3C A4988 1/16 MICROSTEP NEMA17 200 spr KY-040 ENCODER MENU NAV ENDSTOP SW1, N.O. D26 D27 D25 D33/32/14 3V3 GND STEP DIR EN MS1-3 VDD GND 1A 1B 2A 2B B+ B- A+ A- D21 D22 3V3 GND VCC GND SCL SDA D18 D19 D5 3V3 GND D4 GND CLK DT SW VCC GND SW GND RESET+SLEEP jumpered to VDD VMOT/GND ← external 12V PSU, not to MCU shutter opto on D23 (not shown) → camera remote jack
MCU PINNETPERIPHERAL PIN
GPIO26STEPA4988 STEP
GPIO27DIRA4988 DIR
GPIO25ENABLEA4988 ENABLE (active LOW)
GPIO33MS1A4988 MS1
GPIO32MS2A4988 MS2
GPIO14MS3A4988 MS3
3V3LOGIC 3V3A4988 VDD, OLED VCC, encoder VCC
GNDGND RAILA4988 GND, OLED GND, encoder GND, endstop GND
GPIO4ENDSTOP_SWendstop switch (INPUT_PULLUP, active LOW)
GPIO18ENC_CLKKY-040 CLK
GPIO19ENC_DTKY-040 DT
GPIO5ENC_SWKY-040 SW
GPIO21I2C_SDAOLED SDA
GPIO22I2C_SCLOLED SCL
GPIO23SHUTTERopto-isolator input (camera remote trigger)

MOTION CONTROL

Steps/mm. GT2 belt (2 mm pitch) on a 20-tooth pulley, NEMA17 at 1.8°/step (200 full steps/rev), A4988 at 1/16 microstepping:

microsteps/rev = 200 x 16              = 3200
mm/rev (belt)  = 20 teeth x 2 mm pitch = 40
STEPS_PER_MM   = 3200 / 40             = 80.0

Trapezoidal profile, from first principles. Constant-acceleration kinematics give s = 1/2 a t^2, so treating a step count as the distance gives the exact elapsed time to complete step i directly: t(i) = sqrt(2i/a). The interval between consecutive steps is just the difference of consecutive timestamps — no motion library, no lookup table:

float rampIntervalUs(long i, float accelSps2) {
  if (i <= 0) return 0.0f;
  float t_i   = sqrtf(2.0f * (float)i       / accelSps2);
  float t_im1 = sqrtf(2.0f * (float)(i - 1) / accelSps2);
  return (t_i - t_im1) * 1.0e6f;   // seconds -> microseconds
}

A move of steps picks nAccel = min(vMax^2 / (2*accel), steps/2) (the /2 cap falls back to a triangular profile — no cruise phase — when the target is too short to ever reach vMax), mirrors that same count for nDecel, and fills the remainder with a constant-interval cruise at 1e6/vMax µs. Deceleration reuses rampIntervalUs() with a mirrored index (nDecel - d + 1), so the ramp down is the exact time-reverse of the ramp up rather than an approximation of it:

if (i <= nAccel) {
  intervalUs = rampIntervalUs(i, accelSps2);                 // ramp up
} else if (i <= nAccel + nCruise) {
  intervalUs = cCruiseUs;                                    // constant speed
} else {
  long d = i - nAccel - nCruise;
  intervalUs = rampIntervalUs(nDecel - d + 1, accelSps2);    // mirrored ramp down
}

Step timing itself is enforced with plain unsigned long micros()-arithmetic comparisons (wraparound-safe), so while the ramp math is float, the time-critical wait loop is integer-only. Homing runs a fast seek toward the endstop (cut short the instant the switch closes), a fixed back-off, then a slow creep back in for a repeatable zero; soft limits ([0, MAX_TRAVEL_STEPS]) are measured from that zero and refuse any move — single or time-lapse — until homing has succeeded. Time-lapse mode splits a total travel span across N frames, firing the opto-isolator shutter pulse and waiting out the remainder of the M-second interval between each incremental move.

SIMULATION

Runs end-to-end in Wokwi against diagram.json. Watch the wokwi-stepper-motor turn through the homing sequence at boot (fast seek, back off, slow creep), then drive the KY-040 to page through the OLED menu — SET DISTANCE, SET SPEED, MOVE, SET FRAMES, SET INTERVAL, RUN TIME-LAPSE — and watch the motor step out each move with visibly different accel/cruise/decel speed, and the shutter LED blink on each time-lapse frame. To run it: open wokwi.com, start a new ESP32 project, paste sketch.ino and diagram.json over the defaults, and press Play.

Substitutions: all core motion parts (board-esp32-devkit-c-v4, wokwi-a4988, wokwi-stepper-motor, wokwi-pushbutton, wokwi-ssd1306, wokwi-ky-040) are real Wokwi parts, no stand-ins needed. The camera shutter line is represented by a wokwi-led + resistor (shutterLed1/r1) standing in for the real opto-isolator’s input LED, so the pulse is visible in sim; on real hardware GPIO23 drives an actual opto-isolator module instead. Per Wokwi’s wokwi-a4988 reference, VMOT/motor-power GND aren’t simulated (the sim turns the motor from STEP/DIR alone), so those pins are left unconnected in diagram.json and only matter on the real build.

STATUS

Design and firmware are complete and self-consistent — pinout matches exactly across sketch.ino, diagram.json, and this page — and the whole flow (homing, menu, single move, time-lapse) has been exercised in the Wokwi simulator only. No physical hardware has been assembled: no bench measurements, no real motor current tuned on an A4988 trimpot, no belt/pulley cut and mounted, no photos. Every number above (STEPS_PER_MM, SLIDER_TRAVEL_MM, accel/speed defaults, homing speeds) is a design value or an expected simulator behavior, not a measured result. Next step: cut the rail and belt, build the carriage, and bring up this exact firmware unchanged on the real slider.

USE CASES & APPLICATIONS

The trapezoidal step-timing engine here is axis-agnostic — the same accel-ramp math drives any single stepper axis: 3D-printer and CNC linear axes, pick-and- place gantries, and lab XY stages, in addition to motorized camera rigs. The homing-against-an-endstop-then-soft-limit pattern is the standard way any of those machines establishes a safe, repeatable coordinate system at power-up. The time-lapse-specific piece — incremental moves paced against a fixed shutter interval — generalizes to any “move, settle, trigger” sequencing problem: automated inspection stations stepping a part under a camera, or a scanning stage advancing between exposures.

FILES

  • sources/electronics/camera-slider/sketch.ino — full ESP32 firmware: the trapezoidal step-timing engine, homing routine, encoder/OLED menu, single-move and time-lapse execution.
  • sources/electronics/camera-slider/diagram.json — Wokwi wiring diagram, pinout matches the firmware exactly.
  • sources/electronics/camera-slider/README.md — Wokwi run steps, library list, A4988 current-setting math, and real-build wiring notes.

← BACK TO ASSEMBLIES

NAME ODILBEK MARIMOV
DWG NO. PF-2026
SHEET 01 / 07
DISCIPLINE ROBOTICS / MECHATRONICS
SCALE 1:1
REV A
THIRD-ANGLE PROJECTION
DATE 2026-07-11
UNITS mm