The original DS5 Bridge — the Windows companion app and the Pico 2 W firmware — was created by SundayMoments. This fork adds native Linux support (PipeWire audio + audio-driven haptics, libusb device access,
uinputchord injection, and CachyOS/Arch packaging), including BC-250 Steam machines.
- Linux version support / issues → here: github.com/djanice1980/DS5_Bridge
- Windows version → the upstream project: github.com/SundayMoments/DS5_Bridge
Please don't file Linux-port issues on the upstream repo, and don't ask SundayMoments for support on this port — they didn't build it.
DS5 Bridge lets you use a real Sony DualSense or DualSense Edge controller wirelessly through a Raspberry Pi Pico 2 W. The controller pairs to the Pico over Bluetooth, and the Pico plugs into your PC over USB.
The companion app gives you a clean place to adjust audio, haptics, trigger strength, lighting, button remaps, shortcuts, firmware tools, and other controller behavior without rebuilding firmware.
The complete walkthrough — dependencies, install, the WirePlumber audio rule that exposes the controller's speaker + haptic channels, and testing — is in docs/cachyos-install.md. In short:
- Download the firmware
.uf2and the Linux companion —.pacmanfor Arch/CachyOS (or the.AppImage) — from Releases. - With the Pico 2 W unplugged, hold
BOOTSEL, connect it, and copy the.uf2onto the Pico drive that appears. - Put the DualSense into pairing mode by holding
CreateandPSuntil the lightbar rapidly blinks blue, then single-click the Pico'sBOOTSELbutton to open the pairing window (the Pico's LED blinks). The controller pairs to the Pico — not directly to your PC. See Syncing a Controller. - Install the companion (
sudo pacman -U DS5-Bridge-Companion-*.pacman) and open DS5 Bridge. The Overview page should show the connected bridge and firmware version.
Once the controller connects to the Pico, Linux sees it as a normal DualSense-compatible USB controller.
Pairing a controller to the bridge — "syncing" — uses the BOOTSEL button on the Pico 2 W
board.
Sync a new controller:
- Put the DualSense into pairing mode: hold
Create+PSuntil the lightbar rapidly blinks blue. - Single-click the Pico's
BOOTSELbutton. Its onboard LED blinks while a 60-second pairing window is open. - The controller pairs to the Pico (not to your PC). The LED stops blinking once it connects.
You only sync each controller once. The bridge remembers paired controllers and reconnects them automatically whenever you power them on — no button press needed. Only a brand-new controller needs the sync button. And because the bridge advertises for new controllers only during the sync window, a stray controller in pairing mode nearby can't latch onto your dongle on its own.
Waking a sleeping PC. Connecting a controller can wake the PC from sleep — an already-paired controller as it reconnects, or a new one as soon as its Bluetooth link comes up during a sync. On Windows, enable the bridge's "Allow this device to wake the computer" power setting; on Linux the companion sets USB wakeup up for you.
BOOTSEL gesture |
Action |
|---|---|
| Single-click | Open a 60-second window to sync a new controller (LED blinks). |
| Triple-click | Reboot the Pico into firmware-flashing (BOOTSEL) mode. |
The sync button requires firmware 1.6.16 or newer; earlier firmware pairs any controller automatically without a button press.
- Use a DualSense or DualSense Edge wirelessly through a Pico 2 W.
- Sync a new controller on demand with a single button press; connecting one can wake a sleeping PC.
- Use the controller speaker, headset jack, microphone, and audio-driven haptics.
- Tune audio, haptics, adaptive triggers, and lighting from the app.
- Use Audio Haptics to turn system or app audio into controller feedback.
- Test every input on a live diagram — buttons, sticks, triggers, touchpad, gyro, and accelerometer.
- Measure stick drift and set a deadzone from the measurement instead of by guesswork.
- Calibrate stick centre and range, temporarily or permanently.
- Save controller setups as profiles.
- Remap buttons and assign chord shortcuts.
- Switch the host persona between DualSense, DualShock 4, and Xbox modes.
- See Bluetooth signal quality at a glance.
- Mount, flash, or nuke Pico firmware from Bridge Settings.
One Pico 2 W drives one controller with all of its features — audio, haptics, adaptive triggers — because each bridge has its own dedicated Bluetooth link. Want four-player couch co-op with full DualSense features on every pad? Use four Pico 2 Ws. Nothing is shared or degraded: four bridges, four controllers, all first-class.
Set a controller up once — trigger feel, haptics strength, speaker volume, lighting — and that profile is bound to the controller itself. Move that controller to a different Pico and its profile comes with it, automatically, the moment it connects. Your settings live with your controller; the person, not the port.
Each Pico is recognized by its own hardware id (the RP2350's factory-unique board id), so you can name them — “Living Room”, “Desk” — and the names stick across reboots and USB ports. Each controller is tracked by its Bluetooth address. Because the app keys everything to real device identity rather than to a name string, a DualSense plugged straight into the PC over USB never gets confused with a bridge — tests, audio, and haptics always target the exact device you selected, even when several identically-named “DualSense Wireless Controller” audio endpoints exist side by side.
A DualSense connected to a bridge over Bluetooth can be plugged into the PC with a USB cable at the same time — it keeps its wireless data link and simply charges over the cable, exactly like it does on a PS5. (The controller picks one transport for data; see the note below on what to expect when you unplug things.)
The companion app is where you check the bridge, adjust the controller, and save the setup you actually want to play with.
See connection health, firmware version, battery, audio route, Bluetooth signal quality, host persona, and the settings most likely to matter during play.
Every bridge and controller the app has seen, each identified by its own hardware id rather than by a name that could collide. Name a bridge, pair or forget a controller, and open the tester from here.
Control the controller speaker, headphone-jack route, microphone level, speaker gain, and buffer length.
Adjust HD haptics, classic rumble, feedback boost, and audio buffer length, then test the feel before opening a game.
Turn system audio or an app session into controller haptic feedback.
Set adaptive trigger strength, try effects, or open Trigger Lab for per-trigger profiles.
Build and preview adaptive trigger effects before applying them to the controller.
Choose lightbar brightness and color, or let the app manage lighting behavior for you.
Change what each controller button does, then save the remap when you are happy with it.
Manage profiles, mute button behavior, polling rate, host persona, diagnostics, and device repair.
Create reusable keyboard, media, and controller actions, then assign them to starter chords.
Set theme, UI scale, tray and startup behavior, firmware maintenance, power saving, LEDs, shortcuts, idle disconnect, and PC sleep disconnect.
Open the Tester from the Devices page. It is a separate window showing everything the controller reports, live, so you can tell a worn stick from a bad cable from a game that simply is not reading the input.
Digital buttons snap on and off. Analogue inputs — the triggers and sticks — fade in proportion to their value, so a sticky trigger or a drifting stick shows up as colour that never fully clears. Gyro reads as three dials, acceleration as a needle and a lift bar, and the raw report sits underneath as the ground truth to check any of it against.
While the tester is open, input to the PC is paused, so pressing PS to check it does not open Steam and sweeping a stick does not move the game behind. That hold is a short lease the window renews rather than a flag it sets: if the app is closed or killed, the controller comes back on its own.
A deadzone hides drift rather than fixing it, so it starts at zero — and setting it used to be guesswork, because the bridge applies the deadzone before anything reaches the app. With one set, the stick reads exactly centre and there is nothing left to see.
Measure drift turns the deadzone off, zooms each stick until a two-count wobble is visible, and draws the deadzone you are about to set as a disc at the same magnification. Push a stick right out and let go, three times over. Each release is measured on its own once the stick has settled, and a release the stick was still moving through is thrown away rather than counted. The middle of the three readings is used, and readings that disagree say so — a thumb left resting on a "released" stick sits perfectly still and reads exactly like drift, which is why it takes three goes that agree.
If a stick will not settle at centre, calibrate it instead of masking it. Centre and range calibration are written to the controller rather than the bridge, and every step is checked against the controller's own reply rather than assumed to have worked.
These changes are temporary by default — the controller reverts them on reset. Making one permanent writes to the controller's flash, cannot be undone, and is deliberately awkward: the sequence is reverse-engineered, nobody can guarantee it works on your unit, and confirming it requires typing the words out. Try the temporary path first; it does the same thing and reverts.
A DualSense remembers exactly one host: pairing it to a different bridge
makes it forget the previous one (same as moving between PS5 consoles). A
plain PS press always reconnects to the last bridge it paired with — so
moving a controller back is always a fresh pairing, never just a PS press:
- Single-click the target Pico's
BOOTSELbutton (60 s pairing window, onboard LED blinks). - Hold
Create+PSon the controller until the lightbar rapidly blinks.
The bridge clears its own stale bond automatically during this (firmware 1.6.13+), and with profile bindings (companion 1.6.26+) the controller's settings follow it to the new bridge on their own.
A DualSense that is connected to the bridge over Bluetooth treats a USB cable plugged in afterwards as charge-only: Windows enumerates audio endpoints for the wired controller, but the controller never activates them (its data session stays on Bluetooth), so the wired speaker/mic stay silent. This is the controller's own transport policy, not a bridge fault.
If the bridge is then unplugged while the cable is still in, the controller
loses its Bluetooth host, blinks orange, powers off — and will not power on
again until the USB cable is removed. Unplug the cable, press PS, and it
reconnects to the bridge normally.
To use a controller wired-only: disconnect it from the bridge first (or power it off), then plug in the USB cable from a fresh start.
- Use the companion app and firmware from the same release when possible.
- For first-time flashing, hold
BOOTSELbefore plugging the Pico 2 W into the PC. The Pico should appear as a USB drive. - Pair the controller to the Pico, not your PC. Hold
CreateandPSuntil the lightbar rapidly blinks blue, then single-click the Pico'sBOOTSELbutton to open the pairing window (firmware 1.6.16+). Already-paired controllers reconnect on their own — see Syncing a Controller. - If the bridge isn't detected, use a direct USB port and a data-capable micro-USB cable (not a charge-only cable), then plug the bridge in before powering the controller on.
- If the controller speaker is quiet or the grips don't buzz, the ALSA UCM profile is likely hiding the 4-channel device. The companion ships a WirePlumber rule that fixes this; see docs/cachyos-install.md.
- Adaptive triggers hold through audio as of firmware 1.6.12. A one-shot trigger test still eases off while audio plays (the controller decays a single command), but in games — which re-assert the effect every frame — the triggers stay firm. If audio ever stutters, ease the Interleave page toward Smooth.
- Battery level may be inaccurate while the controller is charging.
If something misbehaves and you want to gather diagnostics (or you've been asked to), launch the
companion from a terminal with the DS5_DEBUG environment variable set:
DS5_DEBUG=1 ds5-bridge # pacman install
DS5_DEBUG=1 ./DS5-Bridge-Companion-*.AppImage # AppImageThis turns on two things — both completely off without the variable, so normal use is unaffected:
- Developer Tools. A DevTools window opens next to the app. DS5 Bridge is built on Electron, so
this is the same DevTools you'd find in a web browser; its Console tab can read the app's live
state, e.g.
window.bridge.getStatus().then(s => console.log(s.settings)). - Extra terminal logging. The app prints diagnostic lines to the terminal it was launched from (for example, which settings and controller profiles it loaded on startup).
Copy whatever it prints — or a DevTools console result — into your bug report.
- Raspberry Pi Pico 2 W.
- Sony DualSense or DualSense Edge controller.
- Data-capable USB cable with a micro-USB end for the Pico 2 W.
- Linux: CachyOS/Arch with PipeWire + WirePlumber, including BC-250 Steam machines. See docs/cachyos-install.md. (Windows users: use the upstream project.)
See docs/development.md for build requirements, firmware build commands,
companion app setup, audio helper notes, and packaging. Linux-port internals (PipeWire audio,
libusb transport, uinput, packaging) are covered in docs/linux-port.md.
Backporting to the Windows / upstream build: docs/upstream-backport-notes.md is a per-fix technical changelog of everything this port changed — firmware and companion — each item with its symptom, root cause, exact files/functions, and a flag for whether it applies to Windows. Written so the upstream author can adopt applicable fixes without reverse-engineering the diffs. The three firmware fixes (adaptive-triggers-die-under-audio, the fair-interleave output scheduler, and the stale-link-key / dongle-switch fix) apply to the Windows build directly.
| Path | Purpose |
|---|---|
src/main.cpp |
Pico startup, watchdog handling, USB task loop, and HID report bridge. |
src/bt.cpp |
Bluetooth inquiry, pairing, L2CAP HID channels, and report queueing. |
src/audio.cpp |
USB audio ingestion, haptic resampling, Opus speaker encoding, and audio packet assembly. |
src/companion.cpp |
Vendor HID companion protocol, status reports, command ACKs, and runtime setting dispatch. |
src/usb.cpp |
TinyUSB audio control callbacks and runtime settings fallback. |
src/usb_descriptors.c |
USB device, configuration, HID report, audio, and string descriptors. |
companion/ |
Electron companion app source, protocol parser, HID service, assets, and UI. |
companion/native/AudioHelper/ |
Audio helper used by the companion app. Windows (WASAPI/WinUSB) and Linux (Linux/ — PipeWire, libusb, uinput) backends. |
.github/workflows |
CI and release builds. |
This repository is a Linux port of SundayMoments/DS5_Bridge — the original Windows companion app and Pico 2 W firmware. All credit for the original project goes to SundayMoments. This fork adds the Linux companion backends and CachyOS/Arch packaging.
Distributed under AGPL-3.0-only. See LICENSE.
SundayMoments' DS5 Bridge is itself derived from awalol/DS5Dongle, credited in NOTICE. Third-party submodules and package dependencies retain their own license terms. DualSense controller overlay artwork is adapted from AL2009man/Gamepad-Asset-Pack and credited in NOTICE.
- awalol/DS5Dongle, the foundation for the original project.
- rafaelvaloto/Pico_W-Dualsense for project inspiration.
- egormanga/SAxense for Bluetooth haptics proof-of-concept work.
- Sony DualSense controller documentation for report structure notes.
- Paliverse/DualSenseX for speaker report packet references.
- Alex Smith of The Cynic Project for the speaker test sound, "Crystal Cave" (
song18).















