Jumper Factory is a split-architecture wire cutting machine controller.
- Raspberry Pi 5 is the primary controller (UI/orchestration owner; Qt touchscreen UI on a directly-connected MIPI DSI display).
- Arduino UNO R4 Minima is the real-time executor and safety authority.
- Pi and UNO R4 communicate over framed USB serial protocol v1.
- Arduino UNO R4 Minima provides 32 KB SRAM (32,768 bytes) for runtime state.
This repository is migration-locked to Pi-first control flow. Legacy Arduino-only runtime is deprecated and not part of the active workspace.
Building your own machine? Start with GETTING_STARTED.md — it walks through hardware, wiring, software setup, and your first run in order. This README is the technical reference; the getting-started guide is the narrative path through it.
firmware_uno/- active Arduino firmware source of truthfirmware_uno/comm/- v1 protocol parser/codec/dispatcher/watchdog modulespi_controller/- Python Pi runtime (serial client, orchestrator, Qt touchscreen UI)docs/overview/- theory/how-it-works docs for new learners (start atdocs/overview/README.md)docs/developer/SERIAL_PROTOCOL.md- wire protocol contractdocs/developer/DEVELOPER.md- architecture and contributor contractJumper_Factory.ino- generated monolith (do not edit directly)cad/- mechanical design files (CAD, drawings)BOM.md- bill of materialsGETTING_STARTED.md- build-your-own walkthrough: hardware, wiring, software, first runinstall.sh- guided setup script for a fresh Pi
Compatibility note:
- Some legacy notes may still mention
firmware_mega/orscripts/flash_mega.sh. - In this workspace, use
firmware_uno/andscripts/flash_uno.sh.
Subsystem docs:
firmware_uno/README.md- UNO R4 wiring, pinout, runtime behavior, build/uploadpi_controller/README.md- Pi wiring, touchscreen DSI bring-up, runtime behavior, run/test
+---------------------------+
| Raspberry Pi 5 |
| - Python controller |
| - Qt touchscreen UI owner |
+-------------+-------------+
| USB serial (protocol v1, heartbeat)
v
+-------------+-------------+
| Arduino UNO R4 Minima |
| - Motion execution (X/Y/Z)|
| - Safety and failsafe |
| - Encoder capture/events |
+-------------+-------------+
|
+---------------+----------------+
| steppers, limits, IR, hall, |
| buzzer, wire handling hardware |
+---------------------------------+
Raspberry Pi 5's MIPI DSI port also connects directly to the touchscreen
(800x480, 5-point capacitive touch); the rotary encoder stays wired to the
UNO R4 and remains a secondary input alongside touch.
Use this sequence on a Raspberry Pi host. For the full narrative version (hardware list, wiring, first run), see GETTING_STARTED.md.
- Enter the repository root:
cd "Jumper-Factory"- Run the guided installer, or run one-time Pi setup directly:
./install.sh # guided: setup, flash, optional camera/autostart
# or, just the base setup:
./scripts/setup_pi5.sh- Upload firmware to the UNO R4 (adjust port if needed;
install.shalso offers this):
./scripts/flash_uno.sh --port /dev/ttyACM0- Start the Pi controller + WebUI backend (recommended launcher):
./run_pi_app.sh- Open the UI:
http://localhost:8787
From another device on the same network, use http://<pi-ip>:8787.
The dashboard has a dark/light theme toggle (top-right), iconography, and a guided run-batch flow. Set the remote web username/password from the Pi touchscreen under Settings → Web Access.
- Run the launcher from repository root. Running
run_pi_app.shfrom a parent directory without./can fail with exit code127. - The launcher starts
python -m jf_pi.app, enables web backend, and serves the frontend frompi_controller/webui/dist. - Launcher defaults to Pi-triggered jingles enabled (
JF_AUDIO_SAFE_MODE=0) when run with no flags. Pass--safe-audioto suppress jingles (setsJF_AUDIO_SAFE_MODE=1); only skip it once buzzer driver-stage wiring is confirmed. --enable-audiois explicit/redundant with the default but documents intent;--safe-audiois the flag that actually suppresses jingles.- The launcher reflashes the UNO R4 only when its firmware is out of date (compares semver
JF_FW_VERSION_MAJOR/_MINOR/_PATCHagainst the running board); use--force-flashto override. Bump the firmware version infirmware_uno/comm/jf_protocol.hon every firmware change — see the per-version changelog indocs/developer/VERSIONS.md. - If the UNO R4 is missing, the launcher offers to boot without it, retry connecting, or exit. With
--auto-offline(JF_AUTO_OFFLINE=1, used by the systemd service) it skips the prompt and boots offline directly. Booting offline keeps the touchscreen usable with a persistent "Arduino not connected" banner; useSettings -> Machine Linkto retry connecting or flash. Settings you change persist across restarts and re-apply to the board on reconnect. - On startup, the touchscreen shows a brief splash screen ("Jumper Factory" + the Pi software version and Arduino firmware version) before the home screen appears.
- Physical UI
Settings -> Jingle Settingscan toggle firmwarejingle_enabledand trigger a shortAUDIO_PLAY_SEQtest jingle. - Physical UI
Settings -> Clock Formattoggles the top-right header clock between24-hourand12-hour (AM/PM)display. - While a batch runs, the touchscreen run card and the web dashboard show elapsed time and a measured time-remaining estimate (based on how long each wire has actually taken this batch), and the completion screen shows the total run time. Wire → Recent Jobs lists the last 10 batches (with duration and outcome; tap one for stats or Repeat Job), and Wire → Profiles stores named wire setups you can load with one tap — both also available on the web dashboard's Jobs tab.
- If serial ports differ from defaults, use the manual command block in
pi_controller/README.mdunder Run.
- The app runs as a systemd user service (
jumper-factory.service): it starts automatically at boot — including after a power loss — and restarts itself if it crashes. Install/update it with./scripts/install_systemd_service.sh(or via./install.sh); manage it withsystemctl --user status|restart|stop jumper-factoryandjournalctl --user -u jumper-factory -f. Seedeploy/systemd/README.md. This relies on the Pi being configured for desktop autologin (raspi-config→ System Options → Boot / Auto Login → Desktop Autologin) so the graphical session the Qt UI renders into comes up without anyone signing in. - The service launches
run_pi_app.sh --force-restart --auto-offline: if the UNO R4 isn't connected at boot, the app comes up in offline banner mode (no tty prompt) and the operator reconnects from Settings → Machine Link → Retry Connect / Flash Firmware on the touchscreen. - Power-loss recovery: while a batch is cutting, the Pi checkpoints progress to
pi_controller/data/active_job.json. If power (or the app) dies mid-batch, the next link bring-up shows a "Batch interrupted — Resume (N left) / Discard" prompt on the touchscreen; Resume opens the normal Start wizard preloaded with the remaining quantity (wire check → homing → cut). A batch that ends normally (complete/STOP/CANCEL) deletes the checkpoint, so the prompt only appears after an unclean death. The wire that was mid-cut at the moment of power loss is scrap — inspect/trim it before resuming. - The legacy
~/.config/autostart/jumper-factory.desktoplxterminal autostart is disabled by the service installer (kept on disk withHidden=true). Don't run./run_pi_app.shmanually while the service is enabled — usesystemctl --user restart jumper-factoryinstead. ~/Desktop/Jumper Factory Pi App.desktopremains as a manual-launch shortcut for setups without the service (runsrun_pi_app.shin a terminal, where the offline/retry prompt is interactive).- The touchscreen UI has an X button in the top-right header (next to minimize) that asks for confirmation before closing; confirming quits the app fully (Qt UI + backend thread). Note: with the service enabled, systemd treats a clean exit as stopped-until-next-boot (
Restart=on-failure), so relaunch withsystemctl --user start jumper-factory.
From repository root:
./scripts/setup_pi5.shThis script:
- creates/updates
pi_controller/.venv, - installs Python dependencies and editable package,
- installs Arduino core
arduino:renesas_unoand required libraries (U8g2,Encoder,AccelStepper), - runs Pi unit tests.
Flash firmware with Arduino CLI:
./scripts/flash_uno.sh --port /dev/ttyACM0Compile-only check (no hardware required):
./scripts/flash_uno.sh --compile-only./install.sh runs the above in order and offers the optional camera driver and autostart service steps interactively — see GETTING_STARTED.md.
- UNO R4
setup()/loop()entry points are infirmware_uno/jf_setup_loop.hand included byfirmware_uno/jumper_factory_main.ino. - UNO R4 loop always services steppers, sensors, safety checks, comms watchdog, serial command handling, encoder events, and production state machine.
- Protocol/event functions are in
firmware_uno/jf_serial.hplusfirmware_uno/comm/jf_*.h. - Pi runtime entry point is
pi_controller/src/jf_pi/app.py(thin CLI wrapper); the backend loop itself isControllerRuntimeinpi_controller/src/jf_pi/orchestrator/runtime.py. New to the codebase? The module map indocs/developer/DEVELOPER.md("Pi controller") is the guided tour. - Pi runtime sends heartbeat, polls state, consumes async events, updates UI state, and renders Qt touchscreen frames. Unless
--headlessis passed, the backend loop runs on a background thread while Qt's event loop owns the main thread; the two communicate only throughui/ui_bridge.py's thread-safeUiBridge.
- Rotary encoder deltas from UNO R4 are coalesced and then applied on Pi at a stable visual cadence to reduce jumpy redraw bursts.
- Touch tap-to-activate and submenu drag/kinetic scrolling are handled natively by Qt's widgets - there is no custom touch debounce state machine to maintain.
- Root tiles and guided wizard buttons remain tap-focused interactions.
- Touchscreen header includes a digital clock at top-right with an operator-selectable 12h/24h format.
- Physical
Wiremenu is organized asLoad / Unload Wire(submenu),Length,Quantity,Wire Ends(3-way cycler: Off / Strip / Notch - Strip runs the Method A dual-end strip with the trailing-end pass always following; Notch scores rings at the strip boundaries without removing insulation),Strip Length,Wire Gauge/Diameter,Back- noStartrow, since the Home screen'sStarttile is the only entry point into the cut wizard. Length,Quantity, andWire Gauge/Diametereach open a dedicated value-picker screen: tap an on-screen preset or dial/-/+for a custom value, thenSetto commit asSET_JOB_PARAMSorBackto discard the edit (a real cancel - entering the screen snapshots the current value and restores it on discard).- Physical wire edits use machine-safe ranges and increments: length step 5 mm (5-5000), quantity step 1 (1-999), gauge step 1 AWG (10-40), diameter step 0.1 mm (0.1-8.0).
Wire Gauge/Diameteris a mode screen (AWG/Custom Diametertabs): AWG mode shows wire size everywhere as22 AWG (0.64 mm)(gauge plus a display-only mm conversion in brackets); Custom mode shows only the diameter, e.g.1.50 mm. The mode is a Pi-local display preference only - AWG and diameter remain the two distinct values firmware uses together for the strip-cut safety math, so no firmware/protocol changes are involved.- Physical
Startnow opens a guided full-screen Run Batch flow that enforces sequence order: verify wire present -> guide wire insert if missing -> run homing -> start cutting, withSTOPandCANCELvisible during active stages. - Physical
Load Wire/Unload Wireactions run as guided touchscreen flows with step prompts, debounced sensor confirmation, explicitCompleteat load verification, always-available cancel (STOP), and manualFeed More/Retract Moreadjustments. - Physical
Control -> Home Axesnow opens a guided Home flow withStart Homeconfirmation, in-progressCancelsupport, andDonedismissal on completion/error. - Guided load/unload stages include distinct buzzer cues for start, verification/extra-adjust, completion, and cancel/error milestones.
- Web
RUN BATCHnow opens a full-screen flow that enforces the same startup sequence (wire check, guided insert, homing, then cutting) with interrupt controls (STOP/CANCEL) and a completionDonescreen.
UNO R4 build/upload is done in Arduino IDE targeting Arduino UNO R4 Minima.
Board package for Arduino CLI/IDE: arduino:renesas_uno.
Required Arduino libraries:
U8g2EncoderAccelStepper
When firmware modules change, regenerate monolith output:
python tools/generate_monolith.py- Do not bypass limit switches or comms timeout alarm behavior.
- UNO R4 keeps
limitFailsafeLatchedand comms-timeout safety local and authoritative. - If communication drops during active motion, UNO R4 stops and latches
ALARM_COMMS_TIMEOUT. - Pi now applies command preflight gating before serial send: invalid UI/web commands are blocked by machine state and state freshness.
- Non-emergency commands fail closed when machine state is unknown or stale;
STOPremains allowed as emergency intent. - Do not wire a buzzer directly to UNO R4 D13; use a transistor/MOSFET driver stage for buzzer loads.
run_pi_app.shdefaults to Pi-triggered jingles enabled (JF_AUDIO_SAFE_MODE=0); pass--safe-audio(JF_AUDIO_SAFE_MODE=1) to suppress them until buzzer driver-stage wiring is confirmed.
GETTING_STARTED.md- build-your-own walkthrough, start to finishBOM.md- bill of materialscad/- mechanical design filesdocs/overview/- start here: theory and how-it-works docs for new learners (README.md,01-06chapters,GLOSSARY.md)docs/developer/- contributor reference contracts:DEVELOPER.md,SAFETY_MODEL.md,SERIAL_PROTOCOL.md,STATE_MACHINES.md,UI_MENU_SYSTEM_MAP.md,PROGRAM_ARCHITECTURE_MERMAID_CATALOG.md,VERSIONS.md,CHANGELOG.md
The plans and code are publicly available to build, study, and modify for noncommercial use — this is a source-available project, not an OSI-approved "open source" one, because commercial use is restricted to the original author.
- Firmware and software (
firmware_uno/,Jumper_Factory.ino,pi_controller/,scripts/,tools/,deploy/,install.sh) — PolyForm Noncommercial 1.0.0. - Hardware and mechanical design (
cad/,BOM.md, wiring diagrams) — CC BY-NC-SA 4.0.
Building your own machine, and modifying or sharing the design and code, is permitted and encouraged for personal, educational, and research use. Selling assembled units, kits, or derivative products based on this design is a commercial use reserved exclusively to Bryan Ma; contact him if you're interested in a commercial license.