# QT Py + SteamVR Haptics Project Postmortem

## Executive Summary

This project started as a full-stack haptics bring-up effort and ended as a real-time interaction tuning problem.
We successfully built a working pipeline from SteamVR haptic events all the way down to a DRV2605L-driven ERM motor on a QT Py SAMD21, then iteratively removed latency, queueing artifacts, and pulse-shaping mismatches until hover interactions felt immediate and reliable.

At a high level, success required coordinated progress across:

- Embedded firmware (USB HID + DRV2605L command handling)
- Host transport (Rust HID bridge and packet semantics)
- OpenVR driver integration (device registration, input profile, pose, bindings)
- SteamVR behavior tuning (Identify pulses, hover pulses, dashboard pointer)
- Real-time systems behavior (preemption, overlapping pulses, non-blocking execution)

---

## Timeline and Major Hurdles We Overcame

## 1) Foundational bring-up: firmware and hardware control

### What we were trying to do
Get the QT Py SAMD21 firmware to reliably receive commands over USB HID and drive the DRV2605L haptics chip.

### Major hurdles

1. **Reliable USB HID output report handling**  
   We needed robust parsing for the expected report format and strict command decoding.

2. **DRV2605L mode management**  
   The driver had to switch correctly into realtime playback mode and stop cleanly.

3. **Safe command dispatch path**  
   Unknown/malformed commands could not destabilize runtime behavior.

### Outcome
A stable embedded command path was established: parse HID report -> map to driver command -> apply to DRV2605L.

---

## 2) Host-to-firmware transport correctness

### What we were trying to do
Make the host reliably deliver haptic packets to the device with predictable timing.

### Major hurdles

4. **Packet framing and report ID conventions**  
   HID writes needed correct report layout (including report ID behavior).

5. **Error handling and retries**  
   Intermittent HID failures needed bounded retries and useful logs.

6. **Early hidden latency from repeated device open**  
   Reopening HID handles per packet introduced avoidable overhead.

### Outcome
Transport became both resilient and lower-latency, with improved send behavior and diagnostics.

---

## 3) OpenVR runtime integration and event routing

### What we were trying to do
Turn SteamVR/OpenVR haptic vibration events into firmware commands while maintaining driver lifecycle stability.

### Major hurdles

7. **Manual OpenVR ABI/vtable wiring in Rust**  
   Interface declarations and call signatures needed to match OpenVR expectations exactly.

8. **Lifecycle correctness**  
   Device activation/deactivation/cleanup had to avoid stale handles and undefined state.

9. **Event filtering and component matching**  
   We had to ensure only relevant haptic events reached the output path.

### Outcome
A dependable event routing loop was built, allowing OpenVR haptics to flow into the HID layer.

---

## 4) Input profile, bindings, and pointer visibility

### What we were trying to do
Enable dashboard laser pointer interactions and hover-triggered haptics for testing.

### Major hurdles

10. **No visible pointer despite controller registration**  
    SteamVR requires correct profile + compositor binding semantics, not just pose updates.

11. **Mismatch with known-good reference implementation**  
    We aligned against mic-map behavior (right-hand role, `/pose/raw`, compositor bindings).

12. **Missing/insufficient input components for compositor actions**  
    Pose alone was not enough; right-hand click/trigger/system inputs needed explicit components.

### Outcome
Pointer visibility and dashboard interaction plumbing were restored through profile/binding parity and component completeness.

---

## 5) Pose behavior and interaction feel

### What we were trying to do
Make controller/laser behavior practical for hover haptic testing and eliminate awkward offsets.

### Major hurdles

13. **Controller offset and orientation mismatch**  
    Initial transforms felt wrong for practical dashboard use.

14. **Tracking consistency while haptics were active**  
    Pose updates needed to stay smooth even during high-frequency haptic events.

### Outcome
Pose updates were stabilized and aligned with the intended HMD-follow behavior.

---

## 6) Identify and hover pulse interpretation

### What we were trying to do
Translate SteamVR pulse patterns into meaningful ERM output while preserving responsiveness.

### Major hurdles

15. **LRA-oriented SteamVR events on ERM hardware**  
    Raw amplitudes were often too weak for ERM detectability.

16. **Minimum detectability vs. overdriving**  
    We introduced ERM amplitude shaping (gain + non-zero floor), then tuned for perceptibility.

17. **Zero-duration pulse semantics**  
    We clarified OpenVR behavior and adjusted pulse construction to respect one-pulse semantics.

18. **Amplitude-dependent pulse width**  
    We adapted pulse timing logic to align with OpenVR guidance (pulse width interpolation by amplitude).

### Outcome
Single pulses became consistently detectable while preserving protocol intent.

---

## 7) Real-time responsiveness: overlapping pulses and preemption

### What we were trying to do
Ensure new pulses interrupt immediately instead of forming delayed tails during fast hover sweeps.

### Major hurdles

19. **FIFO queue buildup under rapid hover**  
    Backlog caused delayed “ghost” pulses long after the user moved on.

20. **Dropped vs. delayed pulses tradeoff**  
    Simple coalescing could skip too much; strict queueing delayed too much.

21. **Worker preemption model design**  
    We needed latest-intent behavior with immediate interruptibility.

22. **Firmware mode side effects**  
    Internal library-triggered effects can feel queued/non-interruptible compared to realtime actuation.

### Outcome
We moved toward interruptible, realtime-oriented pulse execution and eliminated the worst queue-tail behaviors that blocked responsiveness.

---

## 8) Cross-layer debugging and deployment operations

### What we were trying to do
Maintain fast iteration while touching firmware, host, and OpenVR driver layers.

### Major hurdles

23. **Multi-target build noise and unrelated workspace errors**  
    Some workspace diagnostics were unrelated to the active haptics path and had to be isolated.

24. **Ensuring deployed artifacts matched source changes**  
    Frequent build/deploy/retest loops were required to validate each hypothesis.

25. **Configuration caching in SteamVR**  
    Binding and driver changes sometimes required restart-level refreshes to take effect.

### Outcome
A repeatable edit-build-deploy-test cycle was established and used effectively through the final tuning stages.

---

## Final State

By the end of this effort, we achieved:

- End-to-end haptics from OpenVR events to QT Py firmware actuation
- Stable pose and pointer behavior for hover testing
- Detectable ERM pulse shaping for real hardware constraints
- Significantly improved real-time responsiveness under dense hover interactions

The biggest lesson: this was not one bug, but a **stacked systems problem**. We had to fix semantics and timing at every layer (SteamVR bindings, OpenVR runtime integration, host HID transport, firmware driver mode, and motor-specific pulse shaping) before the interaction felt correct.
