[PR #199] [MERGED] Relay OUTPUT report 0x81 HAPTIC_PULSE (fixes #163, #166) #238

Closed
opened 2026-10-22 05:08:04 +00:00 by roman · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/safijari/openpuck/pull/199
Author: @safijari
Created: 7/29/2026
Status: ✅ Merged
Merged: 8/2/2026
Merged by: @safijari

Base: main ← Head: fix-haptic-pulse-0x81


📝 Commits (1)

📊 Changes

6 files changed (+68 additions, -17 deletions)

View changed files

📝 OpenPuck/puck_hid.cpp (+26 -13)
📝 OpenPuck/serial_console.cpp (+3 -2)
📝 ReversePuckFirmware/ctrl_link.cpp (+1 -1)
📝 ReversePuckFirmware/deck_input.cpp (+9 -0)
📝 ReversePuckFirmware/deck_input.h (+2 -1)
📝 docs/PROTOCOL.md (+27 -0)

📄 Description

Both issues are one bug: OpenPuck was discarding Steam's haptic pulse reports.

Root cause

Two different Valve ID spaces both use 0x81:

channel 0x81 means
feature report 0x01 (command channel) ID_CLEAR_DIGITAL_MAPPINGS — the connect-time amp-clicker, correctly dropped in Steam mode
OUTPUT report (Triton actuator channel) ID_OUT_REPORT_HAPTIC_PULSE — MsgHapticPulse [side][on_us u16][off_us u16][repeat_count u16]

668bda6 ("Support latest beta, fix buzzing, fix stability problems", #102) extended the feature-channel drop to the OUTPUT report as well, describing it as "Steam's haptic-reset action, a 7-byte report id 0x81 with payloads 00../01..". Those leading 00/01 bytes are side = left/right pad — that was Steam firing legitimate click pulses at the pads, mistaken for spurious clicks.

So every pulse-based cue disappeared: trackpad "Regular Press" click (#166), trigger full-pull click (#163), GripSense. "Soft Press" kept working because it rides 0x82 ID_OUT_REPORT_HAPTIC_COMMAND, which is still relayed — exactly the symptom split reported in #166. Timeline matches: the drop landed 2026-07-03, the issues were filed Jul 10 and Jul 13.

Why the ID mapping is certain

Ground truth is SDL src/joystick/hidapi/steam/controller_structs.h (ValveTritonOutReportMessageIDs), whose payload sizes match our cloned report descriptor byte-for-byte:

0x80 rumble(9) · 0x81 pulse(7) · 0x82 command(3) · 0x83 lfo_tone(9) · 0x84 log_sweep(8) · 0x85 script(3)

SDL also notes "Triton and derivatives utilize output reports for haptic commands", and the controller's feature-command table has no 0x8F/0xEA (legacy pulse) handler — so every Steam haptic must arrive in 0x80–0x86, and 0x81 was the only one being dropped.

Changes

  • OpenPuck/puck_hid.cpp — relay OUTPUT 0x81 again; g_drop81 applies only to the feature-channel command it was written for. Both ID spaces documented at the relay gate so they can't be conflated again.
  • OpenPuck/serial_console.cpp — S81 help text says feature-only.
  • docs/PROTOCOL.md — two-ID-space table with payload layouts and the 0x81 trap.
  • ReversePuckFirmware/* — mirror-image bug in the Deck forwarder: it rendered 0x82/0x8F but not 0x81 (and 0x8F is a feature-channel id Triton doesn't implement), so it now buzzes on real pulses.

Pulses carry their own repeat_count, so unlike 0x80 rumble or the 0x82 on/off pair there's no latch to strand and no stop frame that can be lost — no stuck-rumble risk from re-enabling this. The feature-channel drop is untouched, so the mechanism blamed for the buzz in #102 stays suppressed.

Testing

Both firmwares compile clean (make build 187268 B flash / 42524 B RAM; make reversepuck OK). Not hardware-verified — confirmation is code + protocol-level only.

On-hardware notes:

  • S81 is no longer the lever for OUTPUT-side clicking; those pulses are now unconditional.
  • If pad-click haptics are still missing, the fallback suspect is the 0x87 SET_SETTINGS landing whitelist in rfConnFlushRelay discarding Steam's haptic config — console L87 lands all 0x87 verbatim for an A/B. Shouldn't be needed given Steam demonstrably sends the pulses we were dropping.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/safijari/openpuck/pull/199 **Author:** [@safijari](https://github.com/safijari) **Created:** 7/29/2026 **Status:** ✅ Merged **Merged:** 8/2/2026 **Merged by:** [@safijari](https://github.com/safijari) **Base:** `main` ← **Head:** `fix-haptic-pulse-0x81` --- ### 📝 Commits (1) - [`84ebce8`](https://github.com/safijari/openpuck/commit/84ebce8957d1e3b5388f1889fdbfb46d9b44949b) Relay OUTPUT report 0x81 HAPTIC_PULSE (fixes #163, #166) ### 📊 Changes **6 files changed** (+68 additions, -17 deletions) <details> <summary>View changed files</summary> 📝 `OpenPuck/puck_hid.cpp` (+26 -13) 📝 `OpenPuck/serial_console.cpp` (+3 -2) 📝 `ReversePuckFirmware/ctrl_link.cpp` (+1 -1) 📝 `ReversePuckFirmware/deck_input.cpp` (+9 -0) 📝 `ReversePuckFirmware/deck_input.h` (+2 -1) 📝 `docs/PROTOCOL.md` (+27 -0) </details> ### 📄 Description Both issues are one bug: OpenPuck was discarding Steam's haptic **pulse** reports. ## Root cause Two different Valve ID spaces both use `0x81`: | channel | `0x81` means | |---|---| | feature report `0x01` (command channel) | `ID_CLEAR_DIGITAL_MAPPINGS` — the connect-time amp-clicker, correctly dropped in Steam mode | | **OUTPUT report** (Triton actuator channel) | **`ID_OUT_REPORT_HAPTIC_PULSE`** — `MsgHapticPulse [side][on_us u16][off_us u16][repeat_count u16]` | `668bda6` ("Support latest beta, fix buzzing, fix stability problems", #102) extended the feature-channel drop to the OUTPUT report as well, describing it as *"Steam's haptic-reset action, a 7-byte report id 0x81 with payloads `00..`/`01..`"*. Those leading `00`/`01` bytes are `side = left/right pad` — that was Steam firing legitimate click pulses at the pads, mistaken for spurious clicks. So every pulse-based cue disappeared: trackpad **"Regular Press"** click (#166), **trigger full-pull** click (#163), GripSense. "Soft Press" kept working because it rides `0x82 ID_OUT_REPORT_HAPTIC_COMMAND`, which is still relayed — exactly the symptom split reported in #166. Timeline matches: the drop landed 2026-07-03, the issues were filed Jul 10 and Jul 13. ## Why the ID mapping is certain Ground truth is SDL `src/joystick/hidapi/steam/controller_structs.h` (`ValveTritonOutReportMessageIDs`), whose payload sizes match our cloned report descriptor byte-for-byte: `0x80` rumble(9) · `0x81` pulse(7) · `0x82` command(3) · `0x83` lfo_tone(9) · `0x84` log_sweep(8) · `0x85` script(3) SDL also notes *"Triton and derivatives utilize output reports for haptic commands"*, and the controller's feature-command table has no `0x8F`/`0xEA` (legacy pulse) handler — so every Steam haptic must arrive in `0x80`–`0x86`, and `0x81` was the only one being dropped. ## Changes - `OpenPuck/puck_hid.cpp` — relay OUTPUT `0x81` again; `g_drop81` applies only to the feature-channel command it was written for. Both ID spaces documented at the relay gate so they can't be conflated again. - `OpenPuck/serial_console.cpp` — `S81` help text says feature-only. - `docs/PROTOCOL.md` — two-ID-space table with payload layouts and the `0x81` trap. - `ReversePuckFirmware/*` — mirror-image bug in the Deck forwarder: it rendered `0x82`/`0x8F` but not `0x81` (and `0x8F` is a feature-channel id Triton doesn't implement), so it now buzzes on real pulses. Pulses carry their own `repeat_count`, so unlike `0x80` rumble or the `0x82` on/off pair there's no latch to strand and no stop frame that can be lost — no stuck-rumble risk from re-enabling this. The feature-channel drop is untouched, so the mechanism blamed for the buzz in #102 stays suppressed. ## Testing Both firmwares compile clean (`make build` 187268 B flash / 42524 B RAM; `make reversepuck` OK). **Not hardware-verified** — confirmation is code + protocol-level only. On-hardware notes: - `S81` is no longer the lever for OUTPUT-side clicking; those pulses are now unconditional. - If pad-click haptics are *still* missing, the fallback suspect is the `0x87` SET_SETTINGS landing whitelist in `rfConnFlushRelay` discarding Steam's haptic config — console `L87` lands all `0x87` verbatim for an A/B. Shouldn't be needed given Steam demonstrably sends the pulses we were dropping. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
roman 2026-10-22 05:08:04 +00:00
Sign in to join this conversation.
No labels
pull-request
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
safijari/openpuck#238
No description provided.