Follow-up to #2 — the PR added a CPU/Clock OLED screen and bumped
kNumScreens from 10 to 11, but README still advertised 10 screens
in two places (header blurb + Features bullet).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Future releases get:
- A 'Checksums' section appended to the release body with sha256sum -c
-compatible 'HASH filename' lines for each .uf2 attached
- A SHA256SUMS.txt file uploaded alongside the .uf2 artifacts so users
can verify with 'sha256sum -c SHA256SUMS.txt' after downloading
Web-config flasher already compares this against its own SHA-256 on the
'Use latest release' path, but the hashes on the release page let people
manually verify the bundled site UF2 too.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a repository_dispatch step to the release workflow that pings the
DS5Dongle-OLED-Config-Web repo with event-type 'firmware-released' after
the UF2s are uploaded. The web repo's deploy.yml listens for that event
and re-runs its build, which re-fetches the latest release UF2 and
bundles it into the GH Pages site.
Requires a fine-grained PAT stored as WEB_REPO_DISPATCH_PAT secret on
this repo, scoped to:
- Repository: MarcelineVPQ/DS5Dongle-OLED-Config-Web
- Permissions: Actions: Write, Metadata: Read
If the secret isn't set, the step fails 401 and continue-on-error
swallows it — release upload still succeeds.
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>
The low-battery blink would stay frozen mid-cycle (or briefly resume
during reconnect retries) after a controller died and lost connection,
because the stale-report check stopped toggling but never turned the
LED off, and the cached interrupt_in_data[52] still read low.
New `battery_led_on_disconnect()` clears blink state, forces the LED
off, and zeros last_report_us so the stale-check early-return blocks
any new blink until a fresh 0x31 report arrives on the next connection.
Called from bt.cpp's HCI_EVENT_DISCONNECTION_COMPLETE handler.
Stale-check in the tick also now forces LED off when it fires while a
blink was in progress (defense in depth for unclean disconnects).
Same bug exists in upstream awalol/DS5Dongle (their battery_led.cpp is
byte-identical to ours pre-fix) — Sura Academy reported it in their
Discord. Plan to send this back as a PR after we validate it locally.
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 now compiles. audio.cpp on top of upstream's state_mgr-based
audio_loop adds three additive pieces:
1. VU peak meters for ch0/1 (spk_max) and ch2/3 (hap_max) tracked
per-sample in the existing loop; written back to g_peak_spk /
g_peak_hap after. Accessors decay 12.5% per read so the OLED
VU bars fall back naturally over a few frames.
2. Monotonic byte-flow counters: g_usb_frames++ after tud_audio_read
(the OLED Diagnostics screen + web emulator compute USB-aud/s
and BT-0x32/s as delta over time). g_bt_packets++ after each
0x36 bt_write.
3. Audio Auto Haptics DSP — 1-pole LP + envelope follower +
modulation + soft-clip per loteran/DS5Dongle 5d6bc2f, with our
added 4th "Fallback" mode that fires only after the game's
native haptic path has been silent for ~1 s (NATIVE_SILENT_TIMEOUT
= 100 audio_loop calls). Preserves native HD haptics (Spider-Man
Remastered) while filling in for games that send no haptic data
(Ghost of Tsushima). Modes: 0=Off, 1=Fallback (default), 2=Mix,
3=Replace.
Crucially the state_mgr-based state_set(pkt+13, 63) call is left
untouched — that's upstream's correct replacement for our prior
state_data restore hack, and it's load-bearing for speaker output.
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>
Three additions on top of upstream's v0.6.0-hotfix main.cpp:
- #include "oled.h" + oled_init() after audio_init/state_init,
oled_loop() in the main while(1).
- PS + Mute hold-2-seconds combo at the top of interrupt_loop()
for headless soft-reboot via watchdog_reboot(0,0,0). Bit pattern
(interrupt_in_data[9] & 0x05) is rare in normal gameplay so the
long hold prevents accidental triggers.
Upstream's state_init() call sits right between audio_init() and our
new oled_init() — preserved as-is. The state_mgr → 0x31 output report
flow in tud_hid_set_report_cb is upstream's rumble path; not touched.
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>
Upstream only checks left stick X axis, so pressing buttons, using
the d-pad, or moving the right stick won't prevent disconnection.
Extends the same dead zone pattern to cover both sticks (all 4 axes),
both button bytes, and d-pad/misc bytes.
The low-battery indicator is critical info — keep blinking even when
the user has the LED disabled (Speaker mute toggle). Steady-state
LED still respects the flag: when battery recovers or charging
starts, the LED returns to off if the flag is set, otherwise to the
existing connected = solid-on state.
(cherry picked from commit 775346cd29d9a3c566309ec7d29db80f0b6979f1)
Blink the Pico onboard LED at 1 Hz when the connected DualSense
reports PowerPercent <= 1 (i.e. <= 10%) and PowerState == Discharging.
Source data is byte 52 of the BT 0x31 input report, already copied
into interrupt_in_data; no new BT parsing required.
The new module owns the LED only while it is actively blinking; it
detects controller disconnection via stale-report timeout and steps
out, so bt.cpp's existing connect/disconnect LED handling stays in
charge in all other states. Honors disable_pico_led.
Gated by -DENABLE_BATT_LED=ON (default). With the option off, the
source file is not compiled and behavior is identical to upstream.
CI gains a compile-only check for the OFF flavor.
(cherry picked from commit 2f8ea73c9fb695e24e7cc3329db7cb925e82e1c9)
When the inactive_time configuration value exceeds 35, the expression
`get_config().inactive_time * 60 * 1000 * 1000`
overflows the maximum int value due to implicit integer conversion.