Run real electric-unicycle firmware in a 3D simulation with a physical rider. Inspect it, compare it, improve it.
The studio itself, filmed headless, replaying real runs of the stock firmware (26 s). With sound: showcase.mp4. How it is made: video/.
EUC Studio is a workbench for people who want better and safer EUC controller firmware. It runs the controller's actual firmware image on an emulated microcontroller. Motor, battery, wheel, rider and city models supply its inputs. The simulator helps you inspect behavior under stated conditions. It does not establish real riding safety.
Free Ride in the generated city (seed 42): the stock firmware (GW1825007) on the real engine carries the rider through a left turn at 20 km/h in drive mode, on the neutral pedal preset. Chase camera, with the HUD, the pitch gauge and the wheel's display replica; replayed from a recording. Some physical parameters are estimates.
Warning
Custom firmware can make a wheel cut out, injure its rider or destroy its power stage. Read SAFETY.md before you build, flash or ride anything from this repository. Today no build of this repository flashes a real wheel (flasher rules).
- See the firmware work. Watch balance, motor control, tiltback and cutoffs react to a rider in 3D, with scopes, an event timeline and the firmware's own variables live.
- Compare stock and custom firmware. Run both on the same ride, side by side, and find the first moment they differ.
- Change one thing at a time. Our own firmware for the Begode Extreme reproduces the stock behavior, quirks included (the compat profile), and then fixes them one by one (the clean profile), each fix shown in a scenario.
- Ride it yourself. Free Ride puts you on the simulated wheel in an endless generated city, with the keyboard or a gamepad, while the real firmware balances it.
- Grow to other wheels. Wheels, boards and processors are described as data, so new models and brands can follow.
firmware image ─▶ emulated MCU (ARMv7-M core + peripheral models)
│ PWM ▲ currents, halls, voltages, IMU
▼ │
inverter ─▶ motor ─▶ battery (electrical plant, 5 µs steps)
│ torque ▲ wheel speed
▼ │
wheel, tyre, shell, human rider, ground (MuJoCo, 0.5 ms steps; flat floor,
│ terrain strip or streamed city)
▼
EUC Studio app · CLI `euc` · Python bindings
Everything runs on one deterministic clock counted in microcontroller cycles, so every run can be replayed exactly. Our firmware can also run as a host library (SIL) in place of the emulated image, with its quirks switched at run time. See docs/architecture.md.
The first wheel is the Begode Extreme. Its stock firmware boots unchanged in the Rust engine. The simulator reproduces the observed interfaces. Unknown hardware values remain explicit estimates.
Updated 2026-10-23. Open work lives on the public EUC Studio Roadmap board, grouped by milestone. docs/project-status.md has the evidence and the checks for each area.
| Milestone | State | Done | Open |
|---|---|---|---|
| M0 Foundation | Done | Repository, CI, vendor guard, pinned stock fetch, image gate | Keep the gates for new features |
| M1 Stock boots | Done | Stock firmware boots, sends telemetry and answers app commands faster than real time | Broader hardware calibration |
| M2 Stock rides in 3D | Done | Electrical plant, MuJoCo body, rear suspension linkage, terrain, display, lights, sound, Ride workspace | Measured motor, battery and suspension values (calibration) |
| M3 Own CPU core | Done | Own ARMv7-M core, lock-step checks against Unicorn, a selectable per-instruction flash timing model | Exact peripheral timestamps inside a quantum; Tune memory writes and gain edits |
| M4 Human rider | Most done | Articulated rider (the default), opt-in contact feet and lateral rider (L1-L3), 26 scenarios, .eucrec recordings, Python bindings, Free Ride |
Rider balance without external aids (lateral rider L4-L6), measured human response |
| M5 Our firmware | Most done | Compat and clean images boot and ride; the SIL library switches every quirk at run time (SIL) | The 50 % charge full-effort difference (#42), firmware findings, hardware validation |
| M6 Flasher | Simulator only | Gated flasher against a simulated wheel with an emulated bootloader; 320-run fault campaign with no unrecoverable run (spec) | Image signing, the emulator ride of gate 3, bootloader facts from the SWD dump, the app wizard; no real flash (M6) |
| M7 Release | Started | Desktop source builds, a local macOS package (guide), the wheel onboarding guide | Signed bundles, docs site, browser demo, a second wheel (M7) |
| M8 Bench | Planned | The safety procedure only | Controlled bench work and trace comparison (M8) |
Current work:
| Track | State | Open |
|---|---|---|
| W2 Scenarios in the city | Done (2026-10-23) | The shipped scenarios ride the generated city (world model, #11) |
| P1 Physics realism bundle | Draft PR #62 | A rubber tyre at 25 psi with rolling resistance, a finer city grid, the motor fit, the roll fix, and kerb absorb and brace are in the draft (epic #57). The open step is the re-baseline of every scenario, #18: a good task for a contributor |
| Lateral rider L4-L6 | L1-L3 done | The rider balances and turns with its own body instead of external aids (epic #58) |
| Free Ride polish | Play mode, drive mode, Chase camera, daylight, sound and HUD done | Impact sounds, city look up close, buildings, trees, a minimap (milestone) |
| Calibration | Open | Tools and measurements that tie the simulator to real wheels (epic #59) |
Plans and limits per milestone: docs/roadmap.md. All documents: docs/README.md.
You need:
- Rust through rustup. The toolchain version is pinned in
rust-toolchain.toml; rustup installs it on the firstcargocommand. - Python 3.11 or newer.
- For the desktop app: Node 20.19 or newer, pnpm 10, and the Tauri prerequisites for your OS.
- For our firmware: CMake 3.25 or newer, Ninja, and the Arm GNU Toolchain (see firmware/README.md).
The engine and the CLI build on macOS, Linux and Windows. The BLE radio features run on macOS only.
git clone https://github.com/tarikbc/euc-studio && cd euc-studio
git config core.hooksPath tools/hooks # once per clone: the vendor guard runs before each commit
python3 tools/fetch-mujoco/fetch.py # once per clone: the pinned MuJoCo library the physics links
cargo run --release -p euc-cli -- fetch begode-extreme # download and verify the official Begode Extreme buildRun a scenario on the stock firmware. Motor, battery, body, rider and city are simulated, and the scenario's checks are judged at the end:
cargo run --release -p euc-cli -- sim run --scenario scenarios/ride-20kmh-stop.tomlBuild our firmware, then ride it and compare it with stock:
(cd firmware && cmake --workflow --preset target-compat && cmake --workflow --preset target-clean)
cargo run --release -p euc-cli -- sim run --scenario scenarios/ride-20kmh-stop.toml --firmware clean
cargo run --release -p euc-cli -- sim compare --scenario scenarios/ride-20kmh-stop.toml --b compat \
--tolerance scenarios/compare/compat-vs-stock.tomlStart the desktop studio (the first build takes several minutes):
cd app && pnpm install && pnpm tauri devMore: cargo run -p euc-cli -- --help lists every command, docs/scenarios/writing-scenarios.md
explains the scenario files, docs/compare.md the comparison, docs/python.md the
Python API, and docs/spec/flasher.md the simulated flasher (euc flash --sim --synthetic).
The checks for each area are in CONTRIBUTING.md.
In the desktop app, open the Ride workspace and press Free ride above the 3D view. The ride starts at once in the generated city: the firmware boots (about 1.5 s of simulated time) and the keys work. A crash never pauses; press R to respawn.
| Key | Gamepad | Action |
|---|---|---|
| W / ↑, S / ↓ | Left stick, RT / LT | Ride forward, brake |
| A / ←, D / → | Left stick | Turn |
| Space (hold) | LB (hold) | Crouch; at speed also a tuck (less drag) |
| X | B | Step off |
| R, Shift+R | X, Back | Respawn here, at the start |
| P (hold), H (hold) | The wheel's power and handle buttons | |
| L, B, M | Light, horn, pedal mode | |
| Esc | Start | Pause, go on |
| C | Y | Camera |
Keys can be rebound in the panel. The full reference, including drive mode and lean mode, is in app/src/workspaces/freeRide/README.md. Free Ride is a simulation for firmware work, not a riding trainer.
Open work lives as issues on the public roadmap board, grouped into
milestones; a large topic has an epic issue that lists its parts. Comment on an issue before you
start larger work, so the plan is agreed first. Every change goes in as a pull request: CI runs the checks of the
areas it touches (engine, firmware, app, Python bindings, the stock-against-compat comparison and the vendor guard),
every commit carries a DCO sign-off (git commit -s), and a safety-relevant change brings its scenario evidence.
CONTRIBUTING.md has the details.
- Look at the roadmap board and the good first issues: trees and vegetation in the city (#29), smooth route steering (#31), the IMU's low-pass filter (#39), README visuals (#51) and 3D model follow-ups (#56). Issues labelled help wanted and needs: data are open to anyone with the skills or a wheel to measure.
Skills that help, and where they fit:
| You know | Where it helps | Labels |
|---|---|---|
| Rust, CPU emulation, microcontroller peripherals | The ARMv7-M core, STM32F1 peripherals, engine speed (crates/euc-armv7m, crates/euc-stm32f1, crates/euc-sim) |
area: engine |
| MuJoCo, rigid-body physics, biomechanics | Tyre, suspension, the rider models and their controllers (crates/euc-physics) |
area: physics, area: tyre, area: rider |
| Motors, inverters, batteries | The electrical plant and its calibration (crates/euc-plant) |
area: plant, area: calibration |
| Embedded C, motor control | Our firmware's compat and clean profiles (firmware/) |
area: firmware |
| three.js, React, TypeScript | The studio, Free Ride and the city's look (app/) |
area: studio, area: world, area: model |
| Audio synthesis | Free Ride's sound, made from the simulator's signals (app/src-tauri/src/engine/ride_sound.rs) |
area: sound |
| Python, data analysis | Comparing simulated runs with real ride logs (python/; the tool is #35) |
area: calibration |
| A Begode wheel (or another EUC) | Measurements and ride numbers that check the simulator | needs: data |
- Read CONTRIBUTING.md for the ground rules, the area map and the checks per area.
- Read docs/architecture.md, then the guide of the area you work on (docs/README.md maps every document).
- Own a wheel? Measurements and ride data help most. Use the real-wheel data issue form, and never post GPS tracks or raw logs that show where you ride.
euc fetch <wheel> downloads the official image from the manufacturer's own update server to your machine and checks
it against the sha256 pins built into the tool. Nothing from the vendor image is stored here, and a
guard blocks it on every commit and in CI.
| Path | What |
|---|---|
crates/ |
Rust engine: CPU core, peripherals, plant, physics, world, recordings, flasher, CLI euc, Python bindings |
firmware/ |
Our controller firmware in C11 (compat and clean profiles, SIL library) |
app/ |
The EUC Studio desktop app (Tauri, React, three.js) and its design system |
scenarios/ |
Scenario files with their checks, and the compare tolerances |
profiles/ |
Wheel definitions: board, plant, geometry, probes, stock firmware pins |
python/ |
The euc_studio Python package and its tests |
docs/ |
Architecture, roadmap, specifications, model notes, findings (index) |
tests/data/ |
Real-wheel captures used as test data |
tools/ |
Vendor guard, git hooks, MuJoCo fetch |
See CONTRIBUTING.md. In short: sign off your commits (git commit -s), never add vendor material,
and back every safety-relevant change with a scenario. Report a behavior that could hurt a rider privately, as
SAFETY.md explains.
EUC Studio is source-available under the PolyForm Noncommercial License 1.0.0. You may use, study, change and share it for any noncommercial purpose. Commercial use is not allowed. See NOTICE for third-party components and trademarks.
EUC Studio is an independent project and is not affiliated with, endorsed by or sponsored by Begode or any other EUC manufacturer.



