mirror of
https://github.com/safijari/openpuck.git
synced 2026-11-03 22:30:22 +00:00
[PR #238] feat: DualSense audio haptics, Switch Pro HD rumble and full-rate PCM haptic relay #251
Labels
No labels
pull-request
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
safijari/openpuck#251
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/safijari/openpuck/pull/238
Author: @ripps818
Created: 8/20/2026
Status: 🔄 Open
Base:
main← Head:dualsense-haptic📝 Commits (10+)
c9a4980feat(ps5): add UAC1 4-channel audio hapticsb8bcdb9feat(ps5): answer DualSense feature reports like a real pad2ab1472fix(haptics): buzz the trackpad that was clicked in PlayStation modes62f63c8feat(haptics): relay OUTPUT haptic sample streams 0x87-0x89 at full rateac4aad8feat(ps5): wave style streams audio haptics as PCM to the grips011a7bafeat(swpro): HD rumble on the trackpads and gripsf2279c5feat(panel): wave style, grip limiter and HD trackpad strength9881556docs: document wave style, PCM streams, HD rumble and new fields165abc9feat(haptics): reduce trackpad click latency9275d82fix(haptics): no click after HD rumble, retry lost PCM frames📊 Changes
32 files changed (+4098 additions, -196 deletions)
View changed files
📝
Makefile(+30 -2)📝
OpenPuck/OpenPuck.ino(+16 -5)📝
OpenPuck/config.cpp(+184 -1)📝
OpenPuck/config.h(+69 -0)📝
OpenPuck/gamepad_util.cpp(+11 -5)📝
OpenPuck/gamepad_util.h(+1 -1)📝
OpenPuck/haptics.cpp(+869 -54)📝
OpenPuck/haptics.h(+176 -5)📝
OpenPuck/mode_hidgyro.cpp(+1 -1)📝
OpenPuck/mode_ps5.cpp(+160 -28)➕
OpenPuck/mode_ps5_audio.cpp(+936 -0)➕
OpenPuck/mode_ps5_audio.h(+28 -0)📝
OpenPuck/mode_switch_pro.cpp(+116 -27)📝
OpenPuck/puck_hid.cpp(+10 -6)📝
OpenPuck/puck_hid.h(+1 -1)📝
OpenPuck/rf_link.cpp(+61 -15)📝
OpenPuck/rf_link.h(+3 -0)📝
OpenPuck/serial_console.cpp(+2 -2)📝
OpenPuck/usb_app_drivers.cpp(+1 -0)📝
OpenPuck/usb_app_drivers.h(+1 -0)...and 12 more files
📄 Description
Summary
Adds haptics that use the Steam Controller 2's actuators directly instead of motor-style rumble:
0x87-0x89), so audio-to-haptics apps work through the puck.Changes
Eight commits, each builds on its own:
UAC1 4-channel audio haptics (
mode_ps5_audio.*). 48 kHz 4-channel output plus a silent 2-channel mic, which PipeWire's stock DualSense profile requires. Channels 3 and 4 drive the actuators in one of four styles, set from the panel:0x83tones per actuator.0x80, merged with the game's normal rumble.DualSense identity:
bcdDevice1.00, no serial string in the single-HID mode;PS trackpad click fix: the click pulse now buzzes the pad that was clicked. It always used side 1, which is the right pad.
Steam mode relay of OUTPUT
0x87-0x89. These are raw haptic sample streams, not settings reports, and the real puck forwards them. Changes:PCM stream + wave style + grip limiter:
hapticPcmStart/hapticPcmSend/hapticUlaw;Switch Pro HD rumble. Decoder:
0x6012reports device type 3, which Eden's direct Pro Controller driver needs.Renderer (in the main loop, not the USB callback):
0x83tones, capped at -15 dB between 250 and 300 Hz;The decode and renderer are adapted from @Froggerdog's HD Rumble Emulation in #303, without its profiles, trackpad D-pad and shortcut changes.
Panel: wave in the DualSense style button. A grip limiter select and an HD trackpad strength select in the Rumble card, hidden on older firmware.
Docs:
docs/DUALSENSE_HAPTICS.md: setup, Proton notes, hardware measurements;0x86-0x89table, actuator routing, measured PCM behaviour, HD rumble (§9.5), new fields;Update
Four things were added on top of the eight commits, after testing and the reports in the comments:
-128 dBcut or0x86op 1 sent to an idle one plays as a click of its own. An unanswered0x86-0x89frame goes back to the front of the ring for up to two more poll cycles. This path is shared, so DualSense wave and Steam-mode0x87-0x89relay get the retry too.120-127). The Switch gets the four styles for its grips (wave, tone, split, rumble); both types start on wave. Fields88and115still work for older panels.108-111;22still edits the active type). The Switch starts at 100%, where its wave stops getting stronger; the other types start at 200%.128). Off plays plain motor rumble with its own strength (200%).In the panel, each controller's tab now has Rumble strength, and the Switch and DS5 tabs have Haptics style and Grip limiter; the Switch tab also has HD Rumble and HD trackpad strength. The Rumble card keeps Style and Test rumble.
The status blob grows to
p[202..214](limiter and style per type, HD Rumble, strength per type). A config from before these starts every type from the old shared limiter, style and strength.New tests:
tests/type_haptics/run.py, and more cases intests/hd_rumble/run.py. Hardware: Mario Kart in Eden feels fine.Update 2: upstream merge and haptics fixes
Merged current
main(no conflicts left) and brought over the fixes from testing in the comments (Jefemy's reports on HD rumble position and strength):0x86goes out once per stream. Re-sending it every second made the grips stutter once a second.144-147, blobp[215..218]): an upward compressor that raises quiet wave passages such as footsteps, Off / 1.5x (default) / 2x / 3x / 4x.0x36): a tone panned across the grips, in every mode. Op0x35reads PCM stream counters (debug).Behaviour changes worth a look
0x03/0x08, which SDL probes for capabilities.0x87-0x89. Steam's own traffic uses them only for haptic streams.Protocol / config
Status blob v23 (upstream is at 22, with the RF recovery marker at
p[196]) appendsp[197..201]: audio gain/2, audio haptics on/off, audio style, grip limiter knee, HD trackpad strength/2.WebUSB fields:
30audio gain (percent/2, 0 = auto)31audio haptics on/off88audio style (0 rumble, 1 tone, 2 split, 3 wave)115grip limiter knee (50-100, 100 = off)141HD trackpad strength (percent/2)None of these collide with #303. 141 is #303's own field for the same setting.
Later fields:
108-111,120-127,128and144-147, blobp[202..218](see the updates above).cfg.bin: new fields are appended to the tail, after upstream'sext[]block, so existing files keep loading. A file from an earlier build of this PR loses its saved haptics settings once and starts on the defaults.Related PRs
ps5GetCommon, which also stalls0x03.Testing
make check(clang-format 18) passes; every commit builds.python3 tests/hd_rumble/run.py: HD decode, rendering, stop, transition, pan test and soft boost tests, built with ASan/UBSan and-Werror;python3 tests/type_haptics/run.py: per-type defaults and config loading.docs/DUALSENSE_HAPTICS.md.Linux setup
No WirePlumber config is required. Set the DualSense Direct sink's haptic channels to 100% once:
make install-wireplumberinstalls an optional hook that does this automatically. libScePad games (Sony PC ports) need GE-Proton 11-6+ and Steam Input off.AI Disclosure
🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.