Community project

Split-Flap Digit Controller

ESP32
Photo of Split-Flap Digit Controller
Generated with AI

JARON JOYSON

Published September 5, 2026

This guide builds a split-flap digit controller that rotates a mechanical display wheel to show any digit 0-9 on demand. The system uses a 28BYJ-48 stepper motor driven by a ULN2003 board, with a Honeywell Hall-effect sensor and small neodymium magnet for reliable homing. The ConnorQuist split-flap mechanism provides the printed wheel with 40 flaps, each precisely indexed by the stepper motor.

Following this guide, makers will receive a complete wiring diagram, parts list with sourcing links, and ESP32 firmware that handles motor control, sensor calibration, and digit selection via serial commands. The assembly steps cover mounting the motor and sensor to the printed mechanism, calibrating the home position, and testing individual digit movements before integration into larger display projects.

Wiring diagram

Wiring diagram for Split-Flap Digit Controller

Gather all the parts

QtyComponent
1

ULN2003 stepper driver board

Driver board for the 28BYJ-48 geared stepper motor. It takes four ESP32 GPIO signals and switches the motor coils from a 5 V rail.

1

28BYJ-48 stepper motor

Small 5 V geared stepper motor that advances the split-flap wheel.

1

Honeywell SS49E linear Hall-effect sensor

Analog Hall sensor used with a 10 x 3 mm magnet to find the home position after boot.

1

Supermagnete S-03-02-N 3 x 2 mm N48 disc magnet

Exact 3 x 2 mm axial N48 magnet required by the referenced Split Flap Display V1 drum.

1

ConnorQuist Split Flap Display V1 printed mechanism

Downloadable modular split-flap mechanism with drum, flaps, enclosure and 28BYJ-48 motor mount.

Assemble it in 5 steps

1.

2.

3.

4.

5.

Review all connections

1. Connections between "uln2003-stepper-driver-1" and "ESP32"

Functionuln2003-stepper-driver-1ESP32
digitalIN1GPIO 16
digitalIN2GPIO 17
digitalIN3GPIO 18
digitalIN4GPIO 19
power5V5V
groundGNDGND
motorOUT1 → 28byj-48-stepper:COIL1EXT
motorOUT2 → 28byj-48-stepper:COIL2EXT
motorOUT3 → 28byj-48-stepper:COIL3EXT
motorOUT4 → 28byj-48-stepper:COIL4EXT

2. Connections between "28byj-48-stepper-1" and "ESP32"

Function28byj-48-stepper-1ESP32
digitalCOIL1 → uln2003-stepper-driver:OUT1EXT
digitalCOIL2 → uln2003-stepper-driver:OUT2EXT
digitalCOIL3 → uln2003-stepper-driver:OUT3EXT
digitalCOIL4 → uln2003-stepper-driver:OUT4EXT
powerCOM → uln2003-stepper-driver:5VEXT

3. Connections between "ky-035-hall-sensor-1" and "ESP32"

Functionky-035-hall-sensor-1ESP32
powerGPIO 3V3
groundGPIO GND
analogGPIO 34

Deploy the firmware

schematik_esp32.inoOpen in Schematik
#include <Arduino.h>
#include <Stepper.h>

#define STEPS_PER_REV 2048
#define MOTOR_IN1 16
#define MOTOR_IN2 17
#define MOTOR_IN3 18
#define MOTOR_IN4 19
#define HALL_PIN 34
#define HOME_THRESHOLD 2600
#define FLAP_COUNT 40
#define FIRST_DIGIT_FLAP 27
#define STEPS_PER_FLAP (STEPS_PER_REV / FLAP_COUNT)

Stepper flapMotor(STEPS_PER_REV, MOTOR_IN1, MOTOR_IN3, MOTOR_IN2, MOTOR_IN4);
int currentFlap = 0;
int currentDigit = -1;
bool demoMode = false;
unsigned long lastDemoStep = 0;

bool magnetDetected() { return analogRead(HALL_PIN) > HOME_THRESHOLD; }
void stepForward(int steps) { flapMotor.step(-steps); }
void homeWheel() {
  Serial.println("Homing split-flap wheel...");
  for (int i = 0; i < STEPS_PER_REV + 300; i += 4) {
    if (magnetDetected()) {
      stepForward(5);
      currentFlap = 0;
      currentDigit = -1;
      Serial.println("Home magnet found at blank flap");
      return;
    }
    stepForward(4);
  }
  Serial.println("Home magnet not found. Check sensor alignment.");
}
void moveToFlap(int targetFlap) {
  targetFlap = constrain(targetFlap, 0, FLAP_COUNT - 1);
  if (targetFlap < currentFlap) homeWheel();
  stepForward((targetFlap - currentFlap) * STEPS_PER_FLAP);
  currentFlap = targetFlap;
}
void showDigit(int targetDigit) {
  targetDigit = constrain(targetDigit, 0, 9);
  moveToFlap(FIRST_DIGIT_FLAP + targetDigit);
  currentDigit = targetDigit;
  Serial.printf("Showing digit %d on source flap %d\n", currentDigit, currentFlap);
}
void printHelp() { Serial.println("Commands: ? help, r Hall reading, h home, 0-9 digit, n next, d demo"); }
void handleCommand(char command) {
  if (command >= '0' && command <= '9') { demoMode = false; showDigit(command - '0'); return; }
  switch (command) {
    case '?': printHelp(); break;
    case 'r': Serial.printf("Hall reading: %d\n", analogRead(HALL_PIN)); break;
    case 'h': demoMode = false; homeWheel(); break;
    case 'n': demoMode = false; showDigit(currentDigit < 0 ? 0 : (currentDigit + 1) % 10); break;
    case 'd': demoMode = !demoMode; lastDemoStep = millis(); Serial.printf("Demo %s\n", demoMode ? "on" : "off"); break;
    case '\n': case '\r': break;
    default: Serial.println("Unknown command. Send ? for help.");
  }
}
void setup() {
  Serial.begin(115200); analogReadResolution(12); pinMode(HALL_PIN, INPUT); flapMotor.setSpeed(12); delay(500); homeWheel(); printHelp();
}
void loop() {
  while (Serial.available()) handleCommand((char)Serial.read());
  if (demoMode && millis() - lastDemoStep >= 1200) { showDigit(currentDigit < 0 ? 0 : (currentDigit + 1) % 10); lastDemoStep = millis(); }
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik