- C 33.6%
- TypeScript 32.6%
- Svelte 22.7%
- Rust 5.2%
- Shell 3.7%
- Other 2.2%
Project Rename (svdx -> sdvx):
- Typo fix across 30 files. The rhythm game is SDVX
(Sound Voltex), not SVDX.
- Tauri bundle identifier:
dev.nullfragment.svdx-pico-configurator
-> dev.nullfragment.sdvx-pico-configurator.
- Rust crate names: svdx-pico-configurator +
svdx_pico_configurator_lib -> sdvx-pico-configurator +
sdvx_pico_configurator_lib.
- User config dir: ~/.svdx-pico/ -> ~/.sdvx-pico/
(capabilities scope + load_config / save_config paths).
- C header guard: SVDX_PICO_TYPES_H -> SDVX_PICO_TYPES_H.
- All docs, plans, and product-name strings
("SVDX Pico Configurator" -> "SDVX Pico Configurator").
- Git remote moved to tools/sdvx-pico on Forgejo outside
this commit.
Forgejo CI (build.yaml):
- Collapsed firmware + desktop + release into a single
ubuntu-latest job. act v6.4.0 chokes on cross-job
needs: and on `runs-on: ${{ matrix.os }}` at workflow-
prepare time, so the multi-job graph + matrix was
traded for sequential steps in one container.
- Mac / Windows matrix entries kept as commented stubs
for when workers come online.
- Pinned actions/upload-artifact@v3; v4 uses the new
GitHub artifact backend Forgejo can't serve
(GHESNotSupportedError).
- Release publishing now via raw curl against the Forgejo
(Gitea-compatible) API instead of
akkuman/gitea-release-action, with hybrid behavior:
v* tag push -> permanent versioned release; mainline
branch push -> tear down + recreate a rolling 'latest'
prerelease pointed at the new SHA.
- Both release paths attach the firmware UF2 plus Linux
.deb / .AppImage / .rpm bundles.
- jq added to the apt install line for the JSON payload.
Forgejo CI (check.yml):
- Added mainline to push.branches + pull_request.branches
so merges into the new default branch actually trigger
CI.
Build Orchestrator (build.sh):
- New populate_release_links mirrors .app / .dmg / .deb /
.AppImage / .msi bundles from
configurator/src-tauri/target/release/bundle/ into
release/ at the repo root via symlinks, giving a stable
cross-platform path to the latest build.
- Called at the end of build_configurator so every path
(all / app / clean) refreshes the symlinks.
- clean wipes release/ alongside build/ and
firmware-tests/build/.
- .gitignore: exclude release/.
Firmware Config:
- src/generated_config.h refreshed with expanded profile
fields.
- build_uf2/Pico_Game_Controller.uf2 rebuilt to match.
|
||
|---|---|---|
| .forgejo/workflows | ||
| build_uf2 | ||
| configurator | ||
| documentation | ||
| firmware-tests | ||
| src | ||
| .gitignore | ||
| build.sh | ||
| CMakeLists.txt | ||
| demo.gif | ||
| LICENSE | ||
| pico_sdk_import.cmake | ||
| README.md | ||
Pico-Game-Controller
This branch was developed with SDVX in mind. Capable of handling 7 LEDs, 1 WS2812B RGB strip, and 2 encoders.
Demo of this firmware running on Pocket SDVX Pico v4, purchasable at https://discord.gg/MmuKd73XbY
Quick Build
The fastest path on a fresh machine is the all-in-one script at the repo root:
./build.sh # firmware + desktop configurator app (default)
./build.sh firmware # just the firmware UF2
./build.sh app # just the desktop app
That script detects your OS, installs cmake / ninja / git / arm-none-eabi-gcc / node / pnpm if any are missing (via brew, apt, pacman, or dnf), then builds whichever stage you asked for. The output .uf2 ends up at build_uf2/Pico_Game_Controller.uf2; the desktop app bundle lands at configurator/src-tauri/target/release/bundle/macos/SDVX Pico Configurator.app (or the platform equivalent under bundle/). First firmware run takes a few minutes for the Pico SDK fetch; subsequent runs are seconds.
Rust toolchain prerequisite for the desktop app.
./build.sh appbuilds the configurator's Tauri shell, which requiresrustup+cargo. The script doesn't auto-install Rust (it's a ~500 MB download you should opt into explicitly):curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source $HOME/.cargo/env
./build.sh firmwaredoesn't need Rust.
If you'd rather copy-paste manually:
macOS
brew install cmake ninja git
brew install --cask gcc-arm-embedded # or download from https://developer.arm.com/downloads if cask is gone
cmake -B build -S . -G Ninja -DPICO_SDK_FETCH_FROM_GIT=on
cmake --build build
Ubuntu / Debian
sudo apt-get install -y cmake ninja-build gcc-arm-none-eabi git
cmake -B build -S . -G Ninja -DPICO_SDK_FETCH_FROM_GIT=on
cmake --build build
Arch
sudo pacman -S --needed cmake ninja arm-none-eabi-gcc arm-none-eabi-newlib arm-none-eabi-binutils git
cmake -B build -S . -G Ninja -DPICO_SDK_FETCH_FROM_GIT=on
cmake --build build
Fedora
sudo dnf install -y cmake ninja-build arm-none-eabi-gcc-cs arm-none-eabi-newlib git
cmake -B build -S . -G Ninja -DPICO_SDK_FETCH_FROM_GIT=on
cmake --build build
Windows
Use the Raspberry Pi Pico VS Code extension — it bundles the SDK + toolchain and Just Works. Install from the marketplace, open this folder, and the build/flash buttons appear in the status bar. Manual setup via the pico-setup-windows installer also works.
Skip the dep install
If your toolchain is already set up and you just want the build:
SKIP_INSTALL=1 ./build.sh firmware
Pin the Pico SDK to a known location
-DPICO_SDK_FETCH_FROM_GIT=on re-fetches the SDK every time you wipe build/. To pin it once:
git clone --depth 1 --branch 2.1.0 https://github.com/raspberrypi/pico-sdk.git ~/pico-sdk
cd ~/pico-sdk && git submodule update --init
# Then build from the repo root:
PICO_SDK_PATH=~/pico-sdk cmake -B build -S . -G Ninja
cmake --build build
Testing
The firmware's pure-logic modules (boot dispatch, NKRO report builder, palette math) have host-runnable unit tests under firmware-tests/. These build with the system C compiler — no ARM cross-compiler or Pico SDK required — and run in milliseconds.
pnpm --dir configurator test:firmware
Or build firmware AND run tests in one shot:
./build.sh firmware --test
Expected output ends with ✓ All firmware tests passed and 32+ tests across three suites (boot_dispatch_suite, keyboard_report_suite, palette_math_suite).
Flashing
Hold the FF (firmware-flash) button on the controller while plugging in via USB-C. The Pico mounts as the RPI-RP2 drive. Drag build_uf2/Pico_Game_Controller.uf2 onto it. The controller auto-reboots into the new firmware as soon as the file write finishes.
Features
- Gamepad mode — default boot mode
- NKRO Keyboard & Mouse Mode — hold first button (GPIO 4 / BT-A) to enter kb mode
- HID LEDs with Reactive LED fallback
- WS2812B RGB on second core, 2 zones
- SDVX/IIDX spoof — tested on EAC; see branches
release/pocket-sdvx-picoorrelease/pocket-iidx - 1000 Hz polling
- Reversible encoders with debouncing
- Switch debouncing
- Switch and LED pins are staggered for easier wiring
- Profile-based config: keymaps, LED behavior, and boot triggers all read from
src/generated_config.h(seedocumentation/agentic-context/)
Boot triggers (default config)
Hold one of these while plugging in:
| Hold | Result |
|---|---|
| (none) | Joystick mode + color-cycle LEDs |
| BT-A | Keyboard + mouse mode |
| BT-B | Turbocharger LED mode |
| START | LEDs off (overrides any other LED mode) |
These mappings are configurable per build — see documentation/agentic-context/agent-designs/2026-05-10-configurable-profiles-design.md for the full profile model.
Customizing keybinds & lighting
Tweakable parameters live in src/controller_config.h (compile-time limits and physical layout) and src/generated_config.h (the actual profile data — keymaps, palettes, triggers, overrides). The configurator app under configurator/ provides a GUI for editing the latter; see configurator/README.md.
Walkthrough video covering the pre-refactor (single-keymap) flow: Pocket SDVX Pico v5 Firmware Keybind & Lighting Walkthrough.
Documentation
Full project reference lives under documentation/:
| Section | Description |
|---|---|
| Architecture | System context, IPC bridge, build & flash flow |
| Models | Profile / ProfileSet schema, Layout, encoder modes |
| Configuration | generated_config.h + ~/.sdvx-pico/config.json schemas |
| Development | Setup, running, testing |
| Firmware | Pico v5 hardware, LED model, encoder dispatch |
| Agentic context | Design docs + implementation plans |
Repository layout
src/ # Firmware source (compiles to .uf2)
configurator/ # Svelte + TS frontend + Tauri (Rust) desktop shell
build.sh # Top-level build orchestrator (firmware / app / all)
documentation/ # Project reference docs (start at documentation/README.md)
build_uf2/ # Latest .uf2 lands here after a build
.forgejo/workflows/ # CI: firmware + per-OS desktop bundles
Thanks to
- Everyone in the Cons & Stuff Discord for providing near-instant support
- hathach/tinyusb
- mdxtinkernick/pico_encoders — encoders that perform better than both interrupts and polling
- My SE buddies who helped solve the encoder rollover edge case
- Drewol/rp2040-gamecon — USB gamepad descriptor info
- veroxzik/arduino-konami-spoof — Konami spoof USB descriptor info
- veroxzik/roxy-firmware — NKRO descriptor and logic info
- KyubiFox for
clkdivfor encoder debouncing - 4yn for turbocharger lighting
- SushiRemover for alternate debounce mode
