Closes the three non-audio user issues (audio path untouched):
- #6: adaptive trigger FFB was silently dropped. state_update() copied the
RightTriggerFFB/LeftTriggerFFB params into the outgoing state but never set
the Allow{Right,Left}TriggerFFB apply-bits in byte 0, so the DS5 discarded
them on BOTH the standalone 0x31 path and the 0x36 audio-frame fold, while
direct USB worked. Mirror the host's two allow-flags in, like the rumble
flags already were (matches what the on-device Trigger Test screen does).
- #8/#9: new CtrlWake setting (default on = unchanged behavior). Set off and
controller input no longer keeps the OLED awake, so the dim/off timeouts run
during gameplay and the panel can sleep while the controller is in use; only
KEY0/KEY1 wake it.
- #9: OLED brightness (screen_brightness) now persists across a power cycle
instead of resetting to full on every boot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
README: add a "Button remapping" feature bullet and a "Remap tab" entry
under the Web Config Tool section. CHANGELOG [Unreleased]: add a Companion
web tool note documenting the visual Remap editor + Zacksly CC BY 3.0
asset attribution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
16-entry source→target table in its own flash sector (-3, magic DS5\x03),
identity default, 0xFF = disabled. Applied to the OUTGOING host report copy
only — raw interrupt_in_data (OLED screens + PS+Mute reboot combo) stays
physical. Edited via the existing 0xF6/0xF7 vendor reports with a hardened
RM+version frame (no HID-descriptor change, so Windows enumeration is
unaffected) plus a revision counter the host polls to confirm a write.
Apply logic + button set ported from SundayMoments/DS5_Bridge (credit).
scripts/remap_test.py exercises the path over /dev/hidraw without the web
tool. Verified on hardware: swap applied, read back, survived reboot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The BT mic decode path gained a decoded-frame jitter buffer (8 frames) drained
at a steady 10 ms playout cadence. Bursty BT delivery is smoothed; a dropped
mic frame during an active session is concealed with an Opus PLC frame
(opus_decode(decoder, NULL, 0, ...)) instead of leaving a hole the host hears
as a click/dropout. Playout pre-buffers 3 frames and stops after 300 ms of no
real frames so it never emits comfort noise when the mic is idle. New "Mic PLC:"
Diagnostics counter climbs only when concealment fires (a live link-quality
gauge). Verified on hardware: forced BT loss kept captured audio gap-free
(longest zero-run ~0 ms) while the counter climbed; clean link leaves it idle.
Adds ~30 ms mic latency (the pre-buffer). Design ported from
SundayMoments/DS5_Bridge (credit). Unreleased — batching with the next feature.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Issue #3: screen-stays-on fixed by the idle power ladder; trigger-flow diagnostics (host02/trig/tx counters + mic_diag.sh bt-trace) added to triage the missing adaptive-trigger tension.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the firmware's 0xFD vendor feature report from 32 to 44 bytes
to carry the three host -> dongle -> BT trigger counters added in
5da1be4 (host_out02_total / host_out02_trig_allow / host_out02_to_bt).
mic_diag.sh's bt-trace decoder reads them and prints a one-line
verdict so the user can triage issue #3 in one terminal command
instead of switching to the OLED Diagnostics screen mid-game.
Verdicts encoded:
host02 > 0, trig == 0 -> host driver never sets the trigger
Allow bits; not a firmware problem.
trig > 0, tx < trig -> trigger Allow bits arrive but the
speaker-active gate in main.cpp
swallowed them.
full chain populated -> dongle delivered, controller didn't
actuate; Sony BT-side limit.
The 0xFD report ID stays undeclared in the HID descriptor — Linux
hidraw ioctls don't enforce that and WebHID never sees these reports
(config goes through 0xF6). bt-trace's IOCTL_SIZE bumped to 45
(1 byte for the kernel-prepended report ID + 44 bytes payload).
Docs:
README.md - new "Diagnostics & debug tooling" section walks
through the four mic_diag.sh subcommands; previously
the script was only mentioned inside
BLUETOOTH_AUDIO_NOTES.md.
README.md - Diagnostics-screen description updated for the
scrolling row-list + cross-link to bt-trace.
CLAUDE.md - "Where features live" gets a host-side-diagnostics
entry explaining how to extend the 0xFD payload and
wire it into bt-trace's decoder.
CHANGELOG - records both the firmware extension and the README
discoverability fix.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CHANGELOG [Unreleased]:
- Added: OLED idle power ladder (breathing dot @ 2 min, full off @ 15 min).
- Added: host02 / trig / tx trigger-flow counters for issue #3 triage.
- Added: D-pad scrolling Diagnostics screen.
- Changed: flush_fb split into flush_fb_raw + chrome wrapper.
- Changed: Diagnostics row order re-prioritized for trigger triage.
README — replace stale "auto-dim after 5 min idle" line with the
new three-stage power ladder description.
CLAUDE.md — new "Idle power ladder" paragraph in the Architecture
section (state machine + wake events + why contrast-register dim
doesn't work on this panel), and a new entry in "Where features
live" for adding diagnostic rows.
There's a stray older `[Unreleased]` heading at CHANGELOG.md:112
that looks like leftover release content (speaker/haptic regression
fix + Audio Auto Haptics) which actually shipped in an earlier tag.
Not touched here; flagging for a future CHANGELOG hygiene pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Small follow-up to v0.6.2. The version-centralization landed on
master after the v0.6.2 tag, so v0.6.2 UF2s still ship the stale
hardcoded "v0.6.0" on the Status header. v0.6.3 rebuilds from the
current master so the new FIRMWARE_VERSION macro path is exercised
and the Status header reflects the actual release tag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OLED button-model + arrow-chrome refactor on top of v0.6.1. Strict
KEY0/KEY1 nav on every screen, KEY0+KEY1 chord reboot, per-screen
cycling moved to controller buttons (Triangle on Trigger Test, R1 on
Lightbar). Visual chrome strip with > / < arrows on the left edge.
Web preview mirrored in lockstep + orange-tint indicator for screens
whose firmware feature report Chrome WebHID can't expose.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CPU/Clock OLED diagnostics screen (PR #2) + battery_led disconnect
fix, on top of v0.6.0-oled-edition. Promotes the [Unreleased] section
to [0.6.1-oled-edition] with today's date.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New CPU/Clock OLED screen (kScreenCpu), between Diagnostics and BT
Signal in the K0 cycle. Shows:
- Set - configured system clock (SYS_CLOCK_KHZ overclock target)
- Real - clk_sys from the RP2350 frequency counter, measured once on
screen entry and cached (it is fixed at boot; avoids a per-frame
multi-ms stall)
- Vcore - core voltage read back via vreg_get_voltage()
- Temp - on-die sensor via cpu_temp_raw_smoothed()
render_screen_cpu() is noinline like the other render functions (Thumb
literal-pool reach). Hardware-in-the-loop verified on Pico 2 W +
Waveshare Pico-OLED-1.3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an [Unreleased] block for the fb68ea5 disconnect fix (cross-references
the upstream PR awalol/DS5Dongle#101), and a [0.6.0-oled-edition] entry
covering the GitHub release / tag that wraps the existing 0.6.0-rebase code.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README: checkout from our master since our content (10-screen
docs, web-config link, multi-slot docs, Auto Haptics docs,
Acknowledgements, etc.) is the source of truth for the fork; upstream's
v0.6.0 README updates are mostly about their new web config tool which
we don't surface (we link our own fork's web config instead).
CHANGELOG: new [0.6.0-rebase] entry on top documenting (a) the
state_mgr adoption + speaker-fix hack drop, (b) the discoverable-rule
loosening for multi-slot, (c) the iSerialNumber restore that fixes
#100, and (d) verification results from the mid-rebase flash.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build-system, config, usb, and CI wiring on top of upstream's
v0.6.0-hotfix tree. Build will still fail until Phase C+D land bt.h
accessors that oled.cpp references; that's expected mid-rebase.
CMakeLists.txt: add src/oled.cpp + src/slots.cpp to add_executable,
link hardware_spi, restore OUTPUT_NAME ds5-bridge-oled.
src/config.h: append the 4 OLED Edition Config_body fields
(current_slot + auto_haptics_enable/gain/lowpass). Preserves
upstream's field order for the first 8 fields.
src/config.cpp:
- speaker_volume validity default -100 dB → 0 dB (footgun fix)
- 4 new validity guards for the appended fields with sensible
defaults (Fallback / 100 % / 160 Hz / slot 0)
- config_default() impl (was declared in upstream's config.h since
v0.5.4 but never defined; the OLED Edition's "Reset to defaults"
Settings item calls it)
src/usb.cpp: drop the UAC1 SET_CUR Volume → flash-config sync that
let PipeWire/Pulse silently override the user's saved Spk Vol on
every device reconnect. Borrowed from loteran/DS5Dongle commit
03fa1e4.
src/usb_descriptors.cpp: restore iSerialNumber = STRID_SERIAL.
Upstream's commit e79c762 zeroed it to work around SpecialK (#32),
but that broke Windows device identity (#100) — users lost
per-device volume / app preferences when moving USB ports. The
OLED Edition prioritizes Windows device-identity stability for
the broader user base over SpecialK compat for a narrower one.
.github/workflows/{build,release}.yml: artifact filenames use
ds5-bridge-oled.uf2 (and -debug / -picow variants) per the
OUTPUT_NAME rebrand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>