Community project
Split-Flap Digit Controller
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

Gather all the parts
| Qty | Component |
|---|---|
| 1 | 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 | 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"
| Function | uln2003-stepper-driver-1 | ESP32 |
|---|---|---|
| digital | IN1 | GPIO 16 |
| digital | IN2 | GPIO 17 |
| digital | IN3 | GPIO 18 |
| digital | IN4 | GPIO 19 |
| power | 5V | 5V |
| ground | GND | GND |
| motor | OUT1 → 28byj-48-stepper:COIL1 | EXT |
| motor | OUT2 → 28byj-48-stepper:COIL2 | EXT |
| motor | OUT3 → 28byj-48-stepper:COIL3 | EXT |
| motor | OUT4 → 28byj-48-stepper:COIL4 | EXT |
2. Connections between "28byj-48-stepper-1" and "ESP32"
| Function | 28byj-48-stepper-1 | ESP32 |
|---|---|---|
| digital | COIL1 → uln2003-stepper-driver:OUT1 | EXT |
| digital | COIL2 → uln2003-stepper-driver:OUT2 | EXT |
| digital | COIL3 → uln2003-stepper-driver:OUT3 | EXT |
| digital | COIL4 → uln2003-stepper-driver:OUT4 | EXT |
| power | COM → uln2003-stepper-driver:5V | EXT |
3. Connections between "ky-035-hall-sensor-1" and "ESP32"
| Function | ky-035-hall-sensor-1 | ESP32 |
|---|---|---|
| power | GPIO 3V3 | |
| ground | GPIO GND | |
| analog | GPIO 34 |
Deploy the firmware
#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.




