Rework the Gyro Tilt screen (and the tilt->RGB lightbar) using the DS5's
own factory IMU calibration, and fix two long-standing tilt-dot quirks.
All three are display-only — the host input report (every gyro + accel
axis) is still forwarded byte-for-byte, so in-game motion is unaffected.
- Calibration: parse feature report 0x05 (already cached by bt.cpp) into
per-axis bias + sensitivity and apply (raw-bias)*sens to the accel the
tilt visuals use, keeping the +-8192 == 1g scale. Re-read per connection
so it tracks across the 4 pairing slots; sanity-gated with raw fallback.
New side-effect-free bt_peek_feature() accessor (never issues an L2CAP
request, safe to poll). Parse/apply mirror SDL's SDL_hidapi_ps5.c (zlib).
- Centred when flat: drive the dot from X (roll) + Z (pitch) instead of
X + Y. Gravity rests on Y when flat, so the old Y mapping pegged the dot
to the bottom edge at rest; it now sits centred.
- Direction: negate both axes so the dot follows the tilt (tilt left -> dot
left, tilt forward -> dot up) instead of mirroring it.
Also fold in this session's charge-ETA robustness fix: cap each timed 10%
step at 30 min and take the median over the last 5 steps, so one slow step
can't balloon the estimate (was reading ~222m at 70% off a single ~47-min
step). Shares src/oled.cpp with the gyro work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BT microphone over Bluetooth: the DS5 mic now works over the dongle's BT
pairing — decoded from the controller's Opus stream to the USB capture
endpoint. Hinges on pkt[4] bit 0 (mic-enable) in the outbound 0x36 audio
report; credit to awalol (upstream) for identifying it. Mic-tagged 0x31
frames ((data[2]>>1)&1) are ALWAYS diverted out of the input path (decoded
when on, dropped when off) so Opus payload can never corrupt sticks/buttons.
Always-on via a sticky-latch keep-alive that only runs post-enumeration
(tud_mounted) so it never floods the fresh-pair handshake (which otherwise
delayed controller detection past the watchdog and tore the link down).
Toggle: bt_mic_enable config field (default on) — OLED Settings + web config.
README gains a "DualSense Microphone over Bluetooth" section;
BLUETOOTH_AUDIO_NOTES.md rewritten from "dead end" to the working mechanism.
USB 3.0 connection watchdog: auto-recovers a stalled connection (re-inquiry)
instead of hanging on the amber lightbar, for USB 3.0 ~2.4 GHz RF interference
that desensitizes the CYW43 BT radio. Re-enabled the ACL-fail / auth-fail /
create-connection-reject recovery paths. README "USB 3.0 ports & Bluetooth
interference" section with mitigations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bt.h: add OLED accessors (bt_is_connected, bt_get_addr, bt_hci_err_count)
and 6 multi-slot accessors (bt_get_slot, bt_set_slot, bt_forget_slot,
bt_wipe_all_slots, bt_slot_occupied, bt_slot_get_addr).
bt.cpp:
- #include "slots.h"; add g_current_slot + update_discoverable()
helper that gates gap_discoverable_control on slots_any_empty().
- 3 OLED accessor impls + 6 slot accessor impls.
- bt_init: slots_load() + g_current_slot from Config_body.current_slot,
then update_discoverable() instead of bare gap_discoverable_control(1).
- HCI_EVENT_INQUIRY_RESULT: slot-ownership filter (skip devices owned
by other slots; if our slot is occupied, only accept matching addr).
- L2CAP_EVENT_CHANNEL_OPENED HID_CONTROL: slot_assign on empty slot
(Phase G auto-bond pattern).
- L2CAP_EVENT_CHANNEL_OPENED HID_INTERRUPT: replace upstream's
gap_*_control(false) pair with update_discoverable() — they wanted
"non-discoverable once connected"; we want "non-discoverable once
all 4 slots are full". Strictly looser, which is correct since
empty slots need to accept new pairings.
- Inquiry-complete + disconnection-complete handlers similarly
swapped to update_discoverable().
Upstream's state_mgr hooks (state_init / state_set / state_update
calls in L2CAP_EVENT_CHANNEL_OPENED and elsewhere) are preserved
as-is — they're the upstream rumble-fix infrastructure.
Build still fails on oled.cpp references to audio_peak_* / counter
accessors that Phase E adds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>