Development

July 30, 2026 ยท View on GitHub

This page covers local builds for the Pico 2 W firmware and the Windows companion app.

Prerequisites

Install these tools before building:

  • Git with submodule support.
  • CMake and Ninja.
  • Arm GNU Toolchain 15.2.Rel1 (arm-none-eabi-gcc 15.2.1).
  • Raspberry Pi Pico SDK 2.3.0.
  • Node.js 22.
  • .NET SDK 9.0.
  • Windows, for building and running the companion app.

The firmware CI currently builds with Pico SDK 2.3.0 and TinyUSB commit 2d56dc533e45e4e91b15e93fdab5e22e964f328d. For the closest local match, use the same versions.

For debug presets and runtime diagnostic flags, see docs/diagnostics.md.

Clone

Initialize the bundled third-party source submodules:

git submodule update --init --recursive

Firmware

Build the companion firmware with the Pico SDK toolchain:

cmake -S . -B build/companion -G Ninja `
  -DCMAKE_BUILD_TYPE=Release `
  -DPICO_SDK_PATH=/path/to/pico-sdk `
  -DENABLE_COMPANION=ON
cmake --build build/companion --target ds5-bridge

The resulting firmware is:

build/companion/ds5-bridge.uf2

Waveshare RP2350B-Plus-W

The Waveshare RP2350B-Plus-W (USB-C, 16MB flash, RM2 wireless) wires the CYW43/RM2 module to different RP2350 GPIOs than the Pico 2 W, so it needs its own build. Add -DWAVESHARE_RP2350B_PLUS_W_BUILD=ON to the cmake configure above, or use the convenience script:

PICO_SDK_PATH=/path/to/pico-sdk ./boards/build_waveshare_rp2350b_plus_w.sh

The board header lives in boards/headers/waveshare_rp2350b_plus_w.h; it is not part of upstream pico-sdk.

Companion App

Install dependencies from the lockfile and run the checks:

cd companion
npm ci
npm run typecheck
npm test

The companion app also keeps a repo-local .npmrc that blocks git dependencies and avoids newly published packages younger than three days.

For a stricter supply-chain check, install without lifecycle scripts and then explicitly rebuild the packages that are expected to need native or tool binary setup:

npm ci --ignore-scripts
npm rebuild electron esbuild node-hid electron-winstaller --ignore-scripts=false
npm run build:audio-helper
npm run typecheck
npm test

Build the companion app:

npm run build

npm run build publishes the audio helper from:

companion/native/AudioHelper

The helper output is written to:

companion/native/AudioHelper/bin/publish/win-x64

Packaging also builds the repo-owned Pico Universal Flash Nuke utility from tools/pico-universal-flash-nuke, then writes the bundled UF2 and manifest used by the Bridge Settings nuke action to:

companion/firmware/pico-universal-flash-nuke.uf2
companion/firmware/pico-universal-flash-nuke.uf2.sha256

For local development:

npm run dev

Packaging

Build an unpacked Windows package:

npm run package:win

Build the Windows installer:

npm run installer:win

The installer build includes the published audio helper and Pico Universal Flash Nuke UF2/manifest as Electron extra resources.

Installer Upgrades

The Windows installer is an NSIS installer generated by electron-builder. A newer DS5-Bridge-Companion-Setup-*.exe upgrades an existing install in place when the installer identity matches:

  • build.appId must remain io.github.sundaymoments.ds5bridge.
  • build.nsis.guid must remain 40464839-1bb3-5f24-b04b-13b55106e88b.
  • build.nsis.perMachine must remain compatible with the existing per-user install path unless the release explicitly migrates users.

On upgrade, electron-builder reads the existing install registry key, skips the fresh-install directory page, closes the running companion if needed, runs the old uninstaller with the --updated flag so user data is preserved, writes the new app files, refreshes shortcuts, and records the new version in Windows' Apps & features entry. Users should be able to run the new installer directly without uninstalling first.

Do not change the NSIS GUID to a newly generated value. That would make Windows and the installer treat the build as a different app, which can leave users with side-by-side installs instead of an upgrade.

Release Candidate Bundle

Create a timestamped release candidate folder in Documents with the firmware UF2, Windows installer, portable companion folder, portable ZIP, and a manifest:

.\tools\create-release-candidate.ps1

Useful options:

.\tools\create-release-candidate.ps1 -Label rc1
.\tools\create-release-candidate.ps1 -OutputRoot "$env:USERPROFILE\Desktop"
.\tools\create-release-candidate.ps1 -SkipBuild
.\tools\create-release-candidate.ps1 -NoZip

Beta Releases

Run the Publish beta release workflow manually and enter the next positive beta number. The workflow always tags the current port-dev head and derives the version from firmware-version.txt; for example, base version 1.7.0 and beta number 2 produce v1.7.0-beta.2.

The workflow creates a draft GitHub prerelease, calls the shared release build for both firmware targets plus the Windows installer and portable app, and only publishes the prerelease after every artifact and the release metadata upload succeeds. A failed build remains a draft for inspection. Beta tags are immutable inputs: rerunning the same beta is allowed only while its tag still points at the same port-dev commit and its release remains a draft.

The workflow definition must be present on the repository's default branch for GitHub's Run workflow button to expose it, but all beta source and submodules are checked out from port-dev.

Audio Helper Runtime

The audio helper is published as a self-contained Windows x64 build so end users do not need to install a separate .NET runtime. Developer machines still need the .NET SDK to build or publish the helper locally. The helper is used for audio session discovery, audio-reactive haptics mirroring, default endpoint setup, speaker test playback, mic keepalive, media metadata, game discovery, and icon extraction.

Enable helper diagnostics while developing companion-side audio integrations:

$env:DS5_BRIDGE_AUDIO_HELPER_DIAGNOSTICS="1"
cd companion
npm run dev

Project Layout

PathPurpose
src/main.cppPico startup, watchdog handling, USB task loop, and HID report bridge.
src/bt.cppBluetooth inquiry, pairing, L2CAP HID channels, and report queueing.
src/audio.cppUSB audio ingestion, haptic resampling, Opus speaker encoding, and audio packet assembly.
src/companion.cppVendor HID companion protocol, status reports, command ACKs, and runtime setting dispatch.
src/usb.cppTinyUSB audio control callbacks and runtime settings fallback.
src/usb_descriptors.cUSB device, configuration, HID report, audio, and string descriptors.
companion/Electron companion app source, protocol parser, HID service, assets, and UI.
companion/native/AudioHelper/Native audio helper source.
.github/workflowsCI and release builds.