Development Setup
The daily-workflow reference for HelixScreen development: running the app, test mode, logging, config, screenshots, and the contribution process.
New here? The entry path is CONTRIBUTING.md → ONBOARDING.md (environment setup + build + the 15-minute mental model) → YOUR_FIRST_CONTRIBUTION.md. This doc is the reference you return to; each topic lives in exactly one place:
| Topic | Lives in |
|---|---|
| Environment setup, first build | ONBOARDING.md |
| Makefile internals, cross-compilation, worktrees, fonts/icons | BUILD_SYSTEM.md |
| Architecture, subsystem deep dives | ARCHITECTURE.md + architecture/ |
| Installer scripts | INSTALLER.md |
Quick Start
Section titled “Quick Start”make -j./build/bin/helix-screen --test -vv # Mock printer + DEBUG logsEnvironment setup and dependencies per OS: → ONBOARDING.md.
Build targets beyond make -j (make build, make dev, make V=1, clean
targets, cross-compilation): → BUILD_SYSTEM.md.
Running the Application
Section titled “Running the Application”./build/bin/helix-screen # Production mode./build/bin/helix-screen --test # Full mock mode (no hardware)./build/bin/helix-screen --test --real-wifi # Mix real WiFi + mock printer./build/bin/helix-screen --dark # Force dark theme./build/bin/helix-screen --light # Force light theme./build/bin/helix-screen -d 1 -s small # Display 1, small sizeTest Mode Flags
Section titled “Test Mode Flags”| Flag | Effect |
|---|---|
--test | Enable test mode (required for mocks) |
--real-wifi | Use real WiFi instead of mock |
--real-ethernet | Use real Ethernet instead of mock |
--real-moonraker | Connect to real printer |
--real-files | Use real printer files |
--real-ams | Use a real AMS backend instead of mock |
--real-sensors | Use real sensor data instead of mock |
--no-ams | Disable mock AMS (for runout modal testing) |
--disconnected | Simulate a disconnected printer |
Test mode keyboard shortcuts (SDL builds): S=screenshot, M=memory stats, D=toggle dark/light, Z=cycle screensavers, Cmd/Win+Q=quit; test-mode-only: A=test action prompt, N=test notification, P=cycle configured printers, F=filament-runout simulation.
Wizard Flags
Section titled “Wizard Flags”| Flag | Effect |
|---|---|
-w, --wizard | Force the first-run configuration wizard |
--wizard-step <step> | Jump to a specific wizard step (0-12) for testing |
--wizard is destructive to wizard-managed config — it is meant to fully re-run setup, not just re-open it. On startup with --wizard, the app clears all wizard-managed state in settings.json so a stale or wrong install-time seed is recoverable:
- Clears the preset marker via
Config::clear_preset()(erases the top-level"preset"key). Becausehas_preset()is then false, the re-run becomes a full wizard — the identify and hardware pages reappear instead of being skipped. - Clears the active printer’s
moonraker_host(set to""), so the connection step is re-entered. - Clears the cached hardware snapshot and the wizard’s heater/sensor/fan/LED/filament-sensor selections, so stale hardware choices don’t trigger false hardware-health warnings.
This is the recovery path when an install-time auto-seed (see INSTALLER.md) picked the wrong printer: re-run with --wizard to drop the seed and reconfigure from scratch.
Printer Detection (--detect-printer)
Section titled “Printer Detection (--detect-printer)”A headless one-shot that queries a running Moonraker over its REST API, runs the same detection logic the wizard uses, prints a JSON verdict to stdout, and exits. No UI is started. Useful for debugging which preset a given printer matches, or for scripting install-time detection.
| Flag | Argument | Default | Effect |
|---|---|---|---|
--detect-printer | (none) | — | Run headless detection, print JSON, exit |
--host <addr> | host/IP | 127.0.0.1 | Moonraker host to query |
--port <n> | 1-65535 | 7125 | Moonraker port to query |
# Detect the local printer./build/bin/helix-screen --detect-printer
# Detect a printer on another host./build/bin/helix-screen --detect-printer --host 192.168.1.74 --port 7125Internally it does three REST GETs against http://HOST:PORT (3-second timeout each):
/printer/objects/list, /printer/info (hostname), and
/printer/objects/query?configfile=settings (kinematics + build volume from the
stepper_x/y/z config). The resulting hardware profile is fed to
PrinterDetector::auto_detect().
Exit codes: 0 on success (verdict printed). 1 if Moonraker’s object list is
unavailable at the given host/port (a warning is logged; no JSON is printed).
JSON output shape — one compact line, terminated by a newline:
{"model":"Creality K1 Max","preset":"k1_max","confidence":92,"runner_up_preset":"qidi_q2","runner_up_confidence":43}| Key | Type | Meaning |
|---|---|---|
model | string | Human-readable detected printer name (PrinterDetectionResult::type_name) |
preset | string or null | Platform preset id for the match; null when the result has no preset |
confidence | integer (0-100) | Detection confidence for the top match |
runner_up_preset | string or null | Preset id of the second-place candidate; null when there is none |
runner_up_confidence | integer (0-100) | Confidence score of the runner-up |
When there is no credible second candidate, the runner-up fields collapse:
{"model":"FlashForge AD5M","preset":"ad5m","confidence":88,"runner_up_preset":null,"runner_up_confidence":0}The installer’s Tier-2 detection (see INSTALLER.md) parses exactly this
output and applies its confidence gate to confidence and the
confidence - runner_up_confidence margin.
For cross-compilation, patches, and advanced options, see BUILD_SYSTEM.md.
Logging
Section titled “Logging”Verbosity Levels
Section titled “Verbosity Levels”| Level | Flag | Use For |
|---|---|---|
| WARN | (default) | Errors and warnings only |
| INFO | -v | User-visible milestones |
| DEBUG | -vv | Troubleshooting, summaries |
| TRACE | -vvv | Per-item loops, wire protocol |
When to use each level in your own code, and the spdlog-only rule (never
printf/cout/LV_LOG_*): → LOGGING.md.
Log Destinations
Section titled “Log Destinations”./build/bin/helix-screen --log-dest=console # Console (default on macOS)./build/bin/helix-screen --log-dest=journal # systemd journal (Linux)./build/bin/helix-screen --log-dest=file --log-file=/tmp/helix.logViewing logs on Linux:
journalctl -t helix -f # systemdtail -f /var/log/helix-screen.log # fileLogging on target devices (backend auto-detection, systemd service): → BUILD_SYSTEM.md § “Logging on Target”.
Configuration
Section titled “Configuration”Config File Pattern
Section titled “Config File Pattern”cp config/settings.json.template config/settings.json # First-time setupconfig/settings.json- User settings (git-ignored)config/settings.json.template- Defaults (versioned)
Never commit user config. Legacy root location auto-migrates.
Config Structure
Section titled “Config Structure”{ "printer": { "heaters": { "bed": "heater_bed", "hotend": "extruder" }, "temp_sensors": { "bed": "heater_bed", "hotend": "extruder" }, "fans": { "part": "fan", "hotend": "heater_fan hotend_fan" } }}Naming: Container keys plural (heaters), role keys singular (bed).
DPI & Hardware Profiles
Section titled “DPI & Hardware Profiles”LVGL scales UI based on DPI. Default: 160 (reference, no scaling).
./build/bin/helix-screen --dpi 170 # 7" @ 1024x600 (BTT Pad 7)./build/bin/helix-screen --dpi 187 # 5" @ 800x480./build/bin/helix-screen --dpi 201 # 4.3" @ 720x480 (AD5M)| Hardware | Resolution | DPI |
|---|---|---|
| Reference | — | 160 |
| 7” LCD | 1024×600 | 170 |
| 5” LCD | 800×480 | 187 |
| AD5M | 720×480 | 201 |
Multi-Display (macOS)
Section titled “Multi-Display (macOS)”-d <n> picks the display (./build/bin/helix-screen -d 1 -s small), --x-pos/--y-pos
position the window exactly. Details: → BUILD_SYSTEM.md § “Multi-Display Support”.
Screenshots
Section titled “Screenshots”# Interactive: Press 'S' in running UI
# Automated:./scripts/screenshot.sh helix-screen output-name [token] [options]./scripts/screenshot.sh helix-screen home-screen home./scripts/screenshot.sh helix-screen motion motion -s small
# Environment overrides:HELIX_SCREENSHOT_DISPLAY=0 ./scripts/screenshot.sh helix-screen test homeHELIX_SCREENSHOT_OPEN=1 ./scripts/screenshot.sh helix-screen review homeOutput: /tmp/ui-screenshot-[name].png
To bring up an arbitrary panel/overlay for debugging (the old -p/--panel
flags are gone), drive a running instance with helix-screen ctl — see
docs/devel/HELIXCTL.md.
Icon & Font Workflow
Section titled “Icon & Font Workflow”make regen-fonts # Regenerate MDI fonts + icon constants together (canonical path)Adding a new icon glyph, font generation internals, and make icon:
→ BUILD_SYSTEM.md § “Font Generation” / “Icon Generation”.
IDE Setup
Section titled “IDE Setup”make compile_commands # Generates compile_commands.json (requires bear)VS Code: C/C++ extension + clangd extension Vim/Neovim: Configure clangd LSP client CLion: Import as Makefile project
Daily Workflow
Section titled “Daily Workflow”- Edit code in
src/orinclude/ - Edit XML in
ui_xml/— no rebuild, no restart needed (hot reload is ON by default; see below) - Build with
make -j(only when C++ changes) - Test with
./build/bin/helix-screen --test -vv - Screenshot with S key or
./scripts/screenshot.sh - Commit working incremental changes
XML vs C++ Changes
Section titled “XML vs C++ Changes”| Change Type | Location | Rebuild? | Hot Reload? |
|---|---|---|---|
| Layout, styling, colors | ui_xml/*.xml | No | Yes — ON by default for native builds |
| Logic, bindings, handlers | src/*.cpp, include/*.h | Yes (make -j) | No |
| Theme colors | config/themes/*.json | No — just restart | No |
| Translations | translations/*.yml (e.g. translations/en.yml) | Yes (code generation step) | No |
XML Hot Reload
Section titled “XML Hot Reload”Hot reload is ON by default for native (non-release) builds — you don’t need to set any env var. Just run the app, edit XML, save, and the active panel/overlay/modal rebuilds in place within ~500ms.
./build/bin/helix-screen --test -vv# Edit ui_xml/home_panel.xml in another terminal, save, watch the UI update live.What you’ll see in the log on each save:
[HotReload] Detected change: home_panel[HotReload] Reloaded: home_panel (0.3ms)[PanelBase::rebuild] Home Panel — tearing down and re-creatingRobustness: if you save mid-write (truncated file, atomic-rename window) or the XML has a syntax error, the reloader silently skips that poll cycle — the existing UI stays live and the next poll retries. No crash, no stale state.
Override the default with HELIX_HOT_RELOAD=0 (force off, e.g. for benchmarking) or HELIX_HOT_RELOAD=1 (force on, e.g. on a device running a release build for live debugging). See HELIX_HOT_RELOAD for full details.
UI Development
Section titled “UI Development”For layout work, styling fixes, and alternate screen layouts, the UI Contributor Guide is the primary reference. It covers breakpoints, design tokens, pre-themed widgets, layout overrides, and what needs work.
Key points for UI contributors:
- XML layouts load at runtime + hot reload by default — edit
ui_xml/*.xml, save, see changes immediately without rebuilding or restarting - Design tokens are mandatory — use
#space_md,#card_bg,<text_body>instead of hardcoded values - 7 breakpoint tiers based on screen height: micro (≤272px), tiny (≤390px), small (≤460px), medium (≤550px), large (≤700px), xlarge (701–1000px), xxlarge (>1000px)
- Layout overrides let you provide alternate XML for ultrawide, portrait, or tiny screens without touching the standard layouts
- Test at multiple sizes with
-s WIDTHxHEIGHT:Terminal window ./build/bin/helix-screen --test -vv -s 480x320 # Tiny./build/bin/helix-screen --test -vv -s 800x480 # Standard./build/bin/helix-screen --test -vv -s 1920x480 --layout ultrawide
Where UI files live
Section titled “Where UI files live”| Path | Contents |
|---|---|
ui_xml/ | All XML layouts (~170 files) |
ui_xml/components/ | Reusable XML components |
ui_xml/ultrawide/ | Ultrawide layout overrides |
ui_xml/globals.xml | Design tokens and global variables (shared, never override) |
config/themes/ | Theme JSON files (color palettes) |
Worktrees
Section titled “Worktrees”For major feature work, use git worktrees to isolate your changes:
scripts/setup-worktree.sh feature/my-branch # Creates in .worktrees/What the script shares/symlinks for fast builds, ccache configuration, and worktree cleanup: → BUILD_SYSTEM.md § “Git Worktrees”.
macOS WiFi Permission
Section titled “macOS WiFi Permission”Real WiFi scanning requires Location Services (network SSIDs reveal location).
Easiest: System Settings → Privacy & Security → Location Services → Enable Terminal
Without permission, app falls back to mock WiFi. Check with:
./build/bin/helix-screen --wizard -vv 2>&1 | grep -i "location\|wifi"Troubleshooting
Section titled “Troubleshooting”make check-deps # Check missing dependenciesmake install-deps # Auto-installmake clean && make V=1 # Verbose rebuildSDL2 not found: brew install sdl2 (macOS) or sudo apt install libsdl2-dev (Linux)
For complete troubleshooting, see BUILD_SYSTEM.md.
Contributing
Section titled “Contributing”First-Time Setup
Section titled “First-Time Setup”make setup # Configures pre-commit hook + commit templateThe pre-commit hook auto-formats code (clang-format) and runs quality checks.
Code Standards
Section titled “Code Standards”Class-based architecture required for all new code:
// ✅ CORRECT: Class-based panelclass MotionPanel : public PanelBase {public: explicit MotionPanel(lv_obj_t* parent); void show() override;};
// ❌ AVOID: Function-based (legacy)void ui_panel_motion_init(lv_obj_t* parent);Naming conventions:
- Functions/variables:
snake_case(ui_panel_home_init,temp_target) - XML files:
snake_case(bed_temp_panel.xml) - Constants:
UPPER_SNAKE_CASE(MAX_TEMP) — includingconstexpr, file-scopestatic const, and enum enumerators. Not Google’skCamelCase: the tree drifted into it for a while and was swept back, so akFooyou find in a diff is a mistake to fix, not a precedent to follow. The exceptions are names that mirror a third-party API verbatim (Apple’skCWSecurityWPA2Personal) — match the foreign spelling there rather than inventing a local one.
Namespace organization: all HelixScreen code lives under helix:: (UI
helpers in helix::ui::, sensor managers in helix::sensors):
- No
usingdeclarations in headers — always fully-qualified names in.hfiles;using namespace helix;is acceptable in.cppfiles only - Enums are
enum classwithinhelix::(e.g.,helix::PanelId,helix::PrintState) - JSON forward declarations come from
#include "json_fwd.h"
Critical patterns:
// Widget lookup: use names, not indiceslv_obj_t* label = lv_obj_find_by_name(panel, "temp_display"); // ✅lv_obj_t* label = lv_obj_get_child(panel, 3); // ❌
// LVGL API: public onlylv_obj_get_x(); // ✅ Public_lv_obj_mark_dirty(); // ❌ Private (underscore prefix)Copyright headers (all new files):
// Copyright (C) 2025-2026 356C LLCCommit Messages
Section titled “Commit Messages”type(scope): description
Optional detailed explanation.Types: feat, fix, docs, style, refactor, test, chore
Examples:
feat(ui): add temperature control overlay panelfix(build): resolve SDL2 linking on macOS Sequoiadocs(readme): update build instructionsPull Requests
Section titled “Pull Requests”Before submitting:
- Rebase on latest
main - Test build and runtime
- Update docs if patterns changed
- Add screenshots for UI changes
PR description includes:
- What changed (summary)
- Why (context/problem solved)
- How to test
- Screenshots (if visual)
- Breaking changes (if any)
Code Review Focus
Section titled “Code Review Focus”- Architecture compliance (XML/Subject patterns)
- Error handling (logging, null checks)
- Performance (no blocking in UI thread)
- Documentation updated
Installer Scripts
Section titled “Installer Scripts”The installation system is modular POSIX shell (scripts/lib/installer/, 17
modules) bundled into the one-line scripts/install.sh for BusyBox-compatible
curl | sh distribution. Module structure, BusyBox rules, bundle regeneration,
and installer testing: → INSTALLER.md.
Related Documentation
Section titled “Related Documentation”- CONTRIBUTING.md - The front door for contributors
- ONBOARDING.md - Fresh checkout → first change
- YOUR_FIRST_CONTRIBUTION.md - Annotated walkthrough of a real contribution
- ARCHITECTURE.md - The 15-minute whole-app model + routing into the chapter series
- UI Contributor Guide - Start here for UI/layout work
- BUILD_SYSTEM.md - Complete build reference
- INSTALLER.md - Installer system
- LVGL9_XML_GUIDE.md - XML syntax reference
- DEVELOPER_QUICK_REFERENCE.md - Common patterns
- TESTING.md - Test infrastructure and Catch2 usage
- LOGGING.md - Log levels and when to use each