The speaker-crackle root cause -- USB-audio OUT endpoint software buffer overflow at 3x the max packet size -- was fixed on the audio branch (de37e00) but never on this branch. Bumping CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ from 3x to 16x removes the crackle; verified by A/B tone test (440 Hz) through the DS5 speaker. A faint residual cadence "warble" remains (a separate root cause, not the buffer) and is left for a later pass. Net: one firmware with BOTH native adaptive triggers and a crackle-free speaker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pico2W DualSense 5 Bridge — OLED Edition
Turn a Raspberry Pi Pico2W into a wireless adapter for the DualSense (DS5) controller — with an optional on-board status display.
OLED Edition is a fork of awalol/DS5Dongle (upstream) that adds an optional Pico-OLED-1.3 128×64 display add-on with 11 screens (status, 4-slot multi-controller pairing, lightbar color picker with favorites and effect presets, trigger test, gyro tilt, touchpad, diagnostics, CPU/clock, BT signal strength, audio VU meters, and a persistent settings menu), plus a DS5 button-combo soft-reboot. Upstream is the authoritative source for the core bridge firmware; this fork tracks it and layers add-on features on top.
🛠️ Web Config Tool
→ Open the OLED Edition Web Config
The web tool is a one-stop shop — no installs, no command line, no picotool. A brand-new Pico 2 W can go from "just out of the box" to fully flashed and configured without ever leaving the browser:
-
Flash Firmware tab — put the Pico in BOOTSEL mode, then click Connect to Pico in the browser and Flash now. The site bundles the latest release UF2, or you can load a local
.uf2you've built yourself. Powered by WebUSB.What is BOOTSEL mode? It's the Pico's built-in flashing mode. To enter it: press and hold the small white button labeled BOOTSEL on the Pico, then plug the USB cable in (or, if it's already plugged in, briefly disconnect and reconnect while holding BOOTSEL). The Pico will appear to your computer as a removable drive — that's how you know it's in BOOTSEL mode. After the web tool flashes the firmware, the Pico auto-reboots into normal mode and is ready to use.
-
Config tab — once the dongle is flashed and reconnected, edit haptics gain, speaker volume, polling rate, audio auto-haptics mode, and the rest of the persistent settings; save to the dongle's flash with one click. Powered by WebHID.
-
Remap tab — visual button remapper: click a button on a live DualSense diagram to reassign it to any other (the shoulders/triggers float to the corners as labeled glyphs). The map is stored on the dongle and applied before the host sees the report, so it works in every game and on every OS. Powered by WebHID.
-
OLED Preview tab — pixel-perfect emulation of all 11 OLED screens. Use the in-page KEY0/KEY1 buttons (or the controller's △ / R1 / D-pad when a DualSense is paired) to navigate. Adaptive triggers actually fire on the controller when you cycle the Trigger Test preset.
Works in any Chromium-based browser (Chrome, Edge, Brave, Opera). Firefox + Safari don't expose WebHID or WebUSB, so flashing and live config aren't available there — the OLED Preview still renders with mock data.
⚠️ Native-trigger firmware (Unreleased) and the WebHID tabs. To make games on Linux/Proton recognise the dongle as a genuine DualSense and fire native adaptive triggers (see CHANGELOG.md), the firmware's HID descriptor is now byte-identical to a real DS5 — which meant un-declaring the
0xF6/0xF7config reports the browser Config and Remap tabs depend on. On that firmware those two tabs can't reach the dongle (WebHID refuses undeclared report IDs). Flash Firmware and OLED Preview still work, on-dongle OLED config still works, and the same settings can be driven over Linuxhidraw. Earlier releases are unaffected.
Source for the web tool: MarcelineVPQ/DS5Dongle-OLED-Config-Web (fork of awalol/ds5dongle-config-web).
Overview
This project enables the Raspberry Pi Pico2W to function as a Bluetooth bridge for the DualSense controller, allowing wireless connectivity with enhanced haptics support.
Features
Core bridge (from upstream):
- Full DualSense connectivity via Pico2W
- HD haptics (advanced vibration feedback)
- Wireless Bluetooth bridging
- Adjustable haptic gain via microphone volume
- Configurable LED and disconnection behaviors
OLED Edition additions:
- Native DualSense adaptive triggers in PC games on Linux/Proton — through the dongle. Games with native DualSense support drive the controller's adaptive triggers wirelessly via the dongle, 1:1 with a directly-wired DualSense. Requires a Proton carrying the Wine
winebus.sys#9034 fix (Wine 11 / current Proton-GE),PROTON_ENABLE_HIDRAW=0x054c/0x0ce6, and Steam Input disabled; the firmware makes the dongle byte-for-byte indistinguishable from a real DS5 so the game accepts it (full write-up in CHANGELOG.md). Trade-off: the browser Config/Remap tabs are disabled on this firmware (see the note above). - Optional Pico-OLED-1.3 status display with 11 screens (status, slots, lightbar, trigger test, gyro tilt, touchpad, diagnostics, CPU/clock, RSSI, VU meters, settings)
- Button remapping — reassign any of the 16 digital controls (face buttons, D-pad, shoulders/triggers, stick clicks, Create/Options) to any other. Stored on the dongle and applied before the host sees the report, so it works in every game and OS with no host-side software; identity (no remap) is the default. Edit it visually in the web config tool's Remap tab, or headlessly via
scripts/remap_test.py. Persisted in its own flash sector, survives reboot. - 4-slot persistent multi-controller pairing — bond up to four DualSenses, switch between them from the OLED, slot 0 reconnects automatically on boot
- Lightbar color picker with 4 user favorite slots + breathing / rainbow / fade effect presets
- Persistent settings menu for the 8 firmware config fields (haptics gain, speaker volume, polling rate, etc.) with hold-to-confirm Reset and Wipe-all-slots actions
- OLED idle power ladder — manual brightness cycle (KEY1 long-press), automatic deep-dim with a small breathing dot at 2 min idle, full display-off at 15 min idle. Wakes instantly on button, controller pair, or input. Real burn-in protection, not just a contrast tweak.
- Soft-reboot without unplugging USB via DS5
PS + Mutehold (works headless) or KEY0 + KEY1 held together for 1 s on the OLED add-on (replaces the older KEY0 double-click gesture, which was easy to fire by accident while paging quickly) - Audit pass on the core bridge — critical stack-overflow fix in the audio path (resolves long-standing "audio stuttering"), security hardening, watchdog, length validation across HID/L2CAP boundaries (see CHANGELOG.md)
🐧 Linux: native adaptive triggers (Proton)
Getting a game to drive the controller's adaptive triggers through the dongle on Linux needs a Proton that carries the Wine winebus.sys #9034 fix. As of this writing no official Proton-GE release includes it yet — it's merged upstream but unreleased, and GE-Proton10-34 (the current release) and earlier do not have it. So a build that does is provided: GE-Proton-DualSense (see Releases). Once an official GE-Proton ships the fix, that will work too.
Why: the #9034 bug suppresses the SDL gamepad device when a hidraw device exists for the same VID/PID, so the pad is seen everywhere except in-game. The fix lets the native (trigger-capable) path and a working gamepad coexist.
One-time setup
-
Install the Proton build. Download
GE-Proton-DualSense.tar.gzand extract it to:- Steam:
~/.steam/root/compatibilitytools.d/ - Heroic:
~/.config/heroic/tools/proton/
Then fully restart Steam so it detects the new compatibility tool.
- Steam:
-
Disable Steam Input so the game talks to the controller natively — Steam → game Properties → Controller → Disable Steam Input (or globally: Settings → Controller → turn off PlayStation Controller support).
Per game
-
Force the Proton: Steam → right-click the game → Properties → Compatibility → Force the use of a specific Steam Play compatibility tool →
GE-Proton-DualSense. (Heroic: set the game's Wine version to it.) -
Set the launch option (Steam: Properties → General → Launch Options; Heroic: per-game Environment Variables):
PROTON_ENABLE_HIDRAW=0x054c/0x0ce6 %command%
Launch — native adaptive triggers fire through the dongle, 1:1 with a wired DualSense. The game must have native DualSense support (e.g. Cyberpunk 2077). First launch under a new Proton does locale/prefix setup and can sit "Not Responding" for a minute — that's setup, not a crash; let it finish.
Launch-option reference
| Variable | Value | Purpose |
|---|---|---|
PROTON_ENABLE_HIDRAW |
0x054c/0x0ce6 |
Required for triggers. Exposes the dongle's raw HID to the game so it uses the native DualSense path (adaptive triggers + haptics). 054c/0ce6 is the dongle's USB VID/PID — identical to a real DualSense. |
PROTON_PREFER_SDL |
1 |
Alternative, input-only. Forces the SDL gamepad path — gives working input + rumble but as a generic/Xbox pad (no triggers). Use only when a game lacks native DualSense support and ENABLE_HIDRAW doesn't help. Do not combine with ENABLE_HIDRAW for trigger games. |
PROTON_LOG |
+hid,+setupapi,+plugplay |
Diagnostic only (not for normal play). Writes a HID enumeration trace to ~/steam-<appid>.log — useful for debugging recognition (Steam honors it; Heroic overrides PROTON_LOG_DIR/WINEDEBUG). |
Heroic vs Steam: Heroic games launch Proton without Steam's controller layer, so they're simpler — just set the runner to
GE-Proton-DualSenseand thePROTON_ENABLE_HIDRAWenv var. Steam games additionally need Steam Input disabled (above).
Hardware
Required
| Item | Notes | Approx. price |
|---|---|---|
| Raspberry Pi Pico 2 W | RP2350 MCU with on-board CYW43 Bluetooth/WiFi. Official product page | ~$7 USD |
| Sony DualSense Controller | Any standard PS5 DualSense (VID 054C:0CE6). |
— |
| USB-C cable | Connects the Pico 2 W to the host PC. | — |
Optional (strongly recommended)
| Item | Notes | Approx. price |
|---|---|---|
| Waveshare Pico-OLED-1.3 | 128×64 SH1107 OLED add-on board (SKU HIPI1798). Plugs directly onto the Pico 2 W headers. Firmware drives it automatically when present and gracefully no-ops when absent. Product page · Wiki | ~$6 USD |
| Small heatsink for the RP2350 | The firmware overclocks the MCU to 320 MHz at 1.20 V (see Performance / Overclocking). A small heatsink or thermal pad helps under sustained gameplay. | $1–3 USD |
Where to buy
Both the Pico 2 W and the Waveshare Pico-OLED-1.3 are widely available worldwide:
- Adafruit, Pimoroni, The Pi Hut, DigiKey, Mouser — major electronics distributors (US / EU)
- Waveshare's own store for the OLED add-on
- Regional Amazon storefronts — search
Raspberry Pi Pico 2 WandWaveshare Pico-OLED-1.3(or the SKUHIPI1798) - AliExpress — original Waveshare and Pico stock plus clones; check seller ratings
Getting Started
Flashing Firmware
- Hold the BOOTSEL button on the Pico2W
- Connect the Pico2W to your computer via USB
- The device will mount as a USB storage device
- Drag and drop the .uf2 firmware file onto the device
Pairing the Controller
- Put the DualSense controller into Bluetooth pairing mode
- Wait for the Pico2W to detect and connect
- Once connected, the device will appear on the host system
Configuration
There are four ways to configure the firmware:
Web config (recommended, any Chromium-based browser): open DS5 Bridge Config — OLED Edition in Chrome, Edge, Vivaldi, Brave, or Opera (Firefox isn't supported — Mozilla declined WebHID). Click Connect, pick the DualSense from the browser dialog, and edit any field with a familiar form UI. The page talks directly to the Pico over WebHID — no driver, no install, no data leaves your machine. Source at MarcelineVPQ/DS5Dongle-OLED-Config-Web.
On-device (OLED add-on present): use the on-screen Settings menu (10th screen). D-pad ▲▼ moves selection, ▶◀ adjusts values, △ saves to flash.
Terminal CLI (any OS, any browser): install hidapi, then use scripts/set_ds5.py:
pip install hidapi
scripts/set_ds5.py # show current config
scripts/set_ds5.py --auto-haptics fallback # change a field, persist to flash
scripts/set_ds5.py --speaker-volume -10 --haptics-gain 1.5
scripts/set_ds5.py --slot 2 # switch active multi-slot pairing
scripts/set_ds5.py --version # firmware version
scripts/set_ds5.py --rssi # live BT RSSI in dBm
scripts/set_ds5.py --help # full flag list
The script talks to the firmware over USB HID feature reports 0xF6/0xF7/0xF8/0xF9 — works on Linux, macOS, and Windows in any terminal regardless of which browser you use. Ported from loteran/DS5Dongle and extended for this fork's current_slot field.
DualSense controller buttons (legacy fallback, no OLED, no CLI):
Microphone volume
Controls haptic gain multiplier. Range: [1.0 – 2.0].
Speaker mute
Disables LED connection indicator. Takes effect after controller reconnects.
Microphone mute
Disables silent disconnection behavior.
Notes
The Pico device will only be visible to the system after the controller is connected
Some behaviors depend on reconnection cycles to take effect
Low-battery LED indicator
When the connected DualSense reports its battery at or below 10% (and it is not charging), the Pico onboard LED switches from solid-on to a 1 Hz blink so you can see the warning at a glance. The LED returns to solid-on as soon as the controller is plugged in or its reported level rises again. The blink also fires when disable_pico_led is set — the warning is treated as critical and overrides the LED-off preference; the LED returns to its disabled (off) state once the battery recovers or the controller starts charging.
To opt out at build time, configure with -DENABLE_BATT_LED=OFF. Default is ON.
DualSense Microphone over Bluetooth
The DualSense's built-in microphone works over the dongle's Bluetooth pairing (since v0.6.8). The controller streams its mic as Opus audio; the dongle decodes it and presents it to the host as the standard DualSense USB capture device, so any app (Discord, OBS, in-game voice) can use it like a normal microphone.
This was long believed impossible — earlier versions of this fork documented it as a hard Sony-firmware limitation (and the Linux hid-playstation kernel driver still doesn't support it). It turned out to hinge on a single enable bit in the dongle's outbound audio report. Full credit to awalol (upstream) for identifying it. The corrected investigation log lives in BLUETOOTH_AUDIO_NOTES.md.
Using it:
- On by default. Pair the controller and the mic begins streaming within a second or two — no game audio required (the dongle keeps the stream alive on its own).
- Raise the capture volume on the host — it defaults low. On Linux, find the card with
arecord -l, then e.g.amixer -c <DualSense card> sset 'Headset' 90%. Verify capture withscripts/mic_diag.sh capture. - Toggle it off to save controller battery — OLED Settings → BT Mic, or the BT microphone switch in the web config tool. Always-on mic keeps the DS5's audio subsystem awake, which drains its battery noticeably faster, so disable it if you don't use voice.
Caveats:
- Mic audio is mono (decoded mono, duplicated across the stereo capture endpoint).
- Toggling off mid-session stops the host feed immediately, but the controller keeps streaming until it next reconnects (there's no known "stop" command); connecting fresh with the toggle off never enables it.
- The OLED Diagnostics screen's
Mic in:counter reads ~100/s while the mic is streaming — a quick way to confirm it's live. - Packet-loss concealment: dropped mic frames (weak BT link, distance, interference) are concealed with Opus PLC so voice stays continuous instead of clicking/cutting out, at a small jitter-buffer latency (~30 ms). The Diag screen's
Mic PLC:counter climbs only when frames are being concealed — effectively a live link-quality gauge.
Known Issues
- Overclocking to 320 MHz @ 1.20 V is required for stable BT pairing. Dropping voltage to 1.10 V or clock to stock breaks the CYW43 PIO SPI bus and BT stops working. A small heatsink on the RP2350 is recommended for sustained gameplay.
- HD haptics may not fire in every game on Linux + Steam; this is game-side (some titles only send HD-haptic audio under Windows-specific APIs). Tested working in Spider-Man Remastered; not delivered in Ghost of Tsushima — same firmware, same controller.
- USB 3.0 ports can disrupt pairing — the controller may get stuck on a solid amber/yellow lightbar and never connect, while the same dongle works fine on a USB 2.0 port. This is RF interference, not a firmware bug; see USB 3.0 ports & Bluetooth interference below. (As of v0.6.8 the firmware auto-retries a stalled connection instead of hanging, which recovers many — but not all — marginal cases.)
USB 3.0 ports & Bluetooth interference
If the dongle works on one port but not another, try a USB 2.0 port first. USB 3.0 ports and (especially) USB 3.0 extension cables emit broadband RF noise centered near 2.4 GHz — the same band the dongle's Bluetooth radio uses to talk to the controller. This is a well-documented industry issue (Intel, "USB 3.0 Radio Frequency Interference Impact on 2.4 GHz Wireless Devices"), not specific to this firmware. The noise desensitizes the dongle's BT receiver, so the controller can start connecting (amber lightbar) but the link is too noisy to complete — it hangs on yellow.
Mitigations, roughly in order of effectiveness:
- Plug the dongle into a USB 2.0 port (often the simplest fix — many motherboards/cases have both).
- Use a short USB 2.0 extension cable to get the dongle a few inches away from the USB 3.0 ports / metal chassis, improving line-of-sight to the controller. Avoid USB 3.0 extension cables specifically.
- Use a powered USB 2.0 hub plugged into the USB 3.0 port — the hub downshifts the link and adds distance.
- Clip a ferrite bead onto the cable near the dongle.
- Keep the controller closer / in line of sight of the dongle during pairing.
The firmware will keep retrying a stalled connection on its own, so leaving it plugged in for ~10–20 s after the lightbar goes amber may let it recover without a replug.
Performance / Overclocking
You don't need to do anything for this — the overclock is baked into the firmware. When you flash a UF2 from this repo, the Pico 2 W boots at the settings below automatically. There is no separate tool to run, no config file to edit, no fuses to blow.
Baked-in settings:
- Voltage: 1.20 V (
vreg_set_voltage(VREG_VOLTAGE_1_20)) - Clock: 320 MHz (
set_sys_clock_khz(SYS_CLOCK_KHZ, true))
Why it's required: at stock clock/voltage the CYW43 PIO SPI bus (the path the firmware uses to talk to the on-board Bluetooth chip) is unreliable and pairing fails. 320 MHz @ 1.20 V is the lowest combination we've verified to produce a stable BT link on this board.
If your device fails to boot a build you compiled yourself (unusual — only relevant when you've changed source), try increasing voltage slightly or lowering the clock in src/main.cpp. End users running official UF2 releases should not need to touch this.
A small heatsink on the RP2350 is recommended for sustained gameplay but not required for pairing or short sessions.
Build Instructions
To build the project from source:
- Install Pico SDK 2.2.0 (or later). The build uses
pico_sdk_import.cmake. - Pin TinyUSB to 0.20.0 inside the Pico SDK (
$PICO_SDK_PATH/lib/tinyusb). This project'stusb_config.huses the 4-argument form ofTUD_AUDIO_EP_SIZE, which is not present in the 0.18.0 version that ships bundled with Pico SDK 2.2.0:cd "$PICO_SDK_PATH/lib/tinyusb" git fetch --tags git checkout 0.20.0 - Configure and build with CMake + Ninja (or Make):
cd /path/to/DS5Dongle cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DPICO_SDK_PATH="$PICO_SDK_PATH" cmake --build build --target ds5-bridge - The UF2 lands at
build/ds5-bridge-oled.uf2. Flash with BOOTSEL as usual.
Build flags worth knowing:
-DENABLE_BATT_LED=ON(default) — blink Pico LED on low DS5 battery.-DENABLE_SERIAL=ON— route printf to USB CDC for debugging (default OFF; releases UART for production builds).-DPICO_W_BUILD=ON— build for the original Pico W (drops audio, lowers clock). Default targets Pico 2 W.
Diagnostics & debug tooling
Two ways to triage bridge issues — on-device via the OLED Diagnostics screen, and host-side via scripts/mic_diag.sh (Linux). The host-side path is faster: no screen-switching, no flash cycle, runs while the controller is in active use.
# One-shot snapshot — is the dongle on USB? Did ALSA enumerate it? Is the
# capture stream live? Is a controller currently paired?
scripts/mic_diag.sh status
# 3-second arecord on the mic IN endpoint — reports peak / RMS / non-zero
# count so we can tell "stream is silent" from "stream is producing audio".
scripts/mic_diag.sh capture 3
# Same as `status` but in a loop, prints only on state change. Useful for
# catching the exact second pairing completes or audio streams open / close.
scripts/mic_diag.sh watch
# Live read of the firmware's 0xFD vendor feature report (via /dev/hidraw):
# BT input counts + rates, last seen non-0x31 IDs, byte prefixes, AND the
# trigger-flow counters (host 0x02 received / with AllowTriggerFFB set /
# forwarded to BT). bt-trace prints a verdict — "host driver isn't sending
# trigger Allow bits" vs "forwarded but controller didn't actuate" — which
# is what would otherwise need a USB protocol analyzer.
scripts/mic_diag.sh bt-trace
Originally written to triage the parked DS5 BT-microphone investigation (see BLUETOOTH_AUDIO_NOTES.md). The 0xFD feature report and bt-trace decoder now also carry the trigger-flow counters added for issue #3 (missing adaptive trigger tension in some games).
OLED Display Add-on (optional)
If you plug a Waveshare Pico-OLED-1.3 onto the Pico2W's headers, the firmware drives it automatically as a live status display. No configuration needed — the firmware no-ops gracefully when no OLED is present.
Boot splash (1.5 s on power-on)
Centered firmware version on a blank screen for 1.5 seconds, then jumps to the Status screen.
Eleven screens, cycled with KEY0 on the add-on
Cycle order: Status → Slots → Lightbar → Trigger Test → Gyro Tilt → Touchpad → Diagnostics → CPU/Clock → BT Signal → VU Meters → Settings → wrap. KEY0 short-press steps forward; KEY1 short-press steps backward — on every screen. Per-screen interactions (cycling the trigger preset, cycling the lightbar mode, navigating the Settings cursor, switching slots) live on the DualSense controller buttons, never on KEY0/KEY1, so the two physical buttons on the OLED add-on always mean the same thing.
Every screen also paints > at the top-left edge (next to KEY0) and < at the bottom-left edge (next to KEY1) so the on-screen labels physically pair with the buttons.
1. Status
Connection state, paired DualSense BD address, battery % with bar (+ charging / * complete / ! error), live analog stick positions, D-pad, face buttons (△ ◯ ✕ □), L1/R1, and L2/R2 analog trigger fill bars. The link indicator and battery use small pixel icons.
2. Slots
Persistent 4-slot multi-controller pairing. Browse stored controllers, switch active slot, or wipe a single slot. > is the cursor, * marks the currently active slot.
- D-pad ▲▼ — move cursor across slots 0–3
- △ — switch to the cursor slot (disconnect current, reconnect to slot's stored controller)
- □ hold 1.5 s — wipe the cursor slot (drops bd_addr + BTstack link key)
- Active slot is persisted; the dongle reconnects to it on next boot
3. Lightbar Color Picker
Tilt the controller on each axis to dial in R / G / B; the firmware sends the resulting color to the DualSense's actual lightbar at 10 Hz, so the lightbar IS the visual preview (the OLED is monochrome).
- Press △ ◯ ✕ □ on the controller to save the current color into favorite slot 0 / 1 / 2 / 3
- Press R1 on the controller to cycle the mode tag:
[LIVE]→[FAV0]→[FAV1]→[FAV2]→[FAV3]→ effects (Breathing / Rainbow / Fade) → back to[LIVE] - Default favorites: Red, Green, Blue, White
4. Trigger Test
Press △ on the controller to cycle seven adaptive trigger effects applied to both L2 and R2. Pull each trigger to feel the effect.
Cycle order: Off → Feedback → Weapon → Vibration → Bow → Gallop → Machine Gun → Off … Effect parameters bitpacked per dualsensectl's reverse-engineering, all at max strength.
5. Gyro Tilt
Live X/Y/Z accelerometer values with a 40×40 crosshair box. Tilt the controller and the dot tracks in real time.
6. Touchpad
Live render of the touchpad surface. Dots appear at current finger positions; the count updates as fingers touch / leave.
7. Diagnostics
Scrollable list of live counters — uptime, BT state, host → BT trigger flow (host02 / trig / tx), BT 0x31 input rate, USB audio frames/sec, BT 0x32 packets/sec, and parked mic-investigation counters at the bottom. Controller D-pad ▲/▼ scrolls; tiny ^ / v glyphs at the right edge mark "more above/below." Read-only, so no cursor. Useful for verifying the bridge is moving bytes without needing a UART cable.
The same counters are also exported on HID feature report 0xFD for host-side tooling — see scripts/mic_diag.sh bt-trace below.
8. CPU / Clock
Live RP2350 vitals: configured (Set) and actually-running (Real) system clock measured against the crystal reference, core voltage read back from the regulator, and on-die temperature (256-sample average + slow EMA so the value tracks the real die temp rather than ADC noise).
The same telemetry is also exported on HID feature report 0xFC for tooling.
9. BT Signal
Live Bluetooth signal strength of the active link, in dBm with a bar. Closer to 0 dBm is stronger; −90 dBm is weak. Includes a qualitative label (Poor / Fair / Good / Excellent).
10. VU Meters
Live peak meters for the speaker and haptic audio paths. Useful for verifying audio routing without the controller being plugged in to a host.
11. Settings
Persistent config editor. D-pad ▲▼ moves the selection, ▶◀ adjusts values, △ saves to flash. Includes the firmware-config fields (haptics gain, speaker volume, inactive timeout, polling rate), the Audio Auto Haptics controls, and two hold-to-confirm actions:
- AutoHap Off / Fallback / Mix / Replace — selects the Audio Auto Haptics mode. Default
Fallbackfires derived rumble only when the game sends no native haptic data (e.g. Ghost of Tsushima on Linux); games that do send native haptics (Spider-Man Remastered) pass through unchanged.Mixadds derived on top of native,Replaceignores native entirely,Offdisables. - AH Gain N% — derived-signal gain, 0–200 % in 10 % steps. Default 100 %.
- AH LP 80/160/250/400 Hz — low-pass cutoff applied to the speaker audio before envelope follow. Lower = more sub-bass, higher = more presence. Default 160 Hz.
- Reset to defaults — hold △ for 2 s to revert all config fields
- Wipe all slots — hold △ for 2 s to drop all 4 paired controllers + all BTstack link keys
Button reference
The two physical buttons on the OLED add-on are strictly navigation:
| Button | Action |
|---|---|
| KEY0 short-press | Next screen (forward) |
| KEY1 short-press | Previous screen (backward) |
| KEY1 long-press (≥ 1.5 s) | Cycle OLED brightness level |
| KEY0 + KEY1 held together ≥ 1 s | watchdog_reboot — soft-reboot without unplugging USB |
Per-screen state changes (cycling the trigger preset, cycling the lightbar mode, navigating Settings, switching slots, saving colors to favorite slots) all happen on the DualSense controller buttons — never on KEY0 / KEY1 — so the two physical buttons always mean the same thing across every screen. See each screen's section above for which controller button does what.
Pinout (standard Waveshare Pico HAT layout)
| Function | GPIO |
|---|---|
| MOSI | 11 |
| SCK | 10 |
| CS | 9 |
| DC | 8 |
| RST | 12 |
| KEY0 | 15 |
| KEY1 | 17 |
Soft-reboot recovery
Two ways to reboot the dongle without unplugging USB — handy if pairing gets stuck or you want a clean state:
- OLED KEY0 + KEY1 held simultaneously for ≥ 1 s →
watchdog_reboot. Replaces the older "KEY0 double-click" gesture from earlier versions, since rapid forward-navigation kept tripping the double-click timer by accident. - DualSense
PS + Muteheld for 2 seconds →watchdog_reboot(works whether or not the OLED is attached — headless backup).
Acknowledgements
Some features and design ideas in this fork are borrowed from other forks of upstream, with credit:
- zurce/DS5Dongle-OLED — pixel-art icons in the OLED status header (visual approach), the "hold for factory reset" UX pattern used by the Settings screen's "Reset to defaults" item (hold △ for 2 s to confirm), and the multi-slot persistent BT pairing system on the new Slots screen (4 bonded controllers, D-pad to navigate, △ to switch slots, □ hold to wipe a slot, plus "Wipe all slots" in the Settings menu).
- loteran/DS5Dongle — independent rediscovery of the upstream
3a31bd7regression that broke speaker / HD haptic output (commitc7a8d3c); the fix in oursrc/audio.cpprestores the same SetStateData sub-report. Also the source of the Audio Auto Haptics DSP (1-pole LP + envelope follower, see Settings → Auto Haptics) and the "don't sync USB-side UAC1 volume to the persistent config" fix. - awalol/ds5dongle-config-web — base for our forked web config app at MarcelineVPQ/DS5Dongle-OLED-Config-Web. The fork adapts upstream's 13-byte Config_body layout to our 19-byte one and adds UI for our additions (multi-slot pairing, Auto Haptics).
Roadmap
- Please check out DS5Dongle plan
Community
- Join the Discord server: Discord Server
- If you have a bug, please open an issue instead.
References
- rafaelvaloto/Pico_W-Dualsense — Project inspiration
- egormanga/SAxense — Bluetooth Haptics POC
- https://controllers.fandom.com/wiki/Sony_DualSense - DualSense data report structure documentation
- Paliverse/DualSenseX — Speaker report packet