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>
28 KiB
Changelog
All notable changes to Pico2W DualSense 5 Bridge — OLED Edition are documented here. This fork tracks awalol/DS5Dongle (upstream) and adds an optional OLED status display plus a security/correctness audit pass on the core bridge.
Format follows Keep a Changelog. Versioning approximates SemVer with the upstream version stream — the fork shares a major.minor with whatever upstream tag it is rebased on.
[Unreleased]
Added
- OLED idle power ladder. Replaces the single-tier 5-min auto-dim with a three-stage state machine: at 2 min idle the panel wipes black and a 2×2 "breathing dot" (1 s on / 1 s off) walks through 8 evenly-spaced positions every 30 s; at 15 min idle the SH1107 is sent
cmd(0xAE)(display off) entirely. Wakes instantly on KEY0/KEY1, controller pair (BT-connect rising edge), or any input-report change. Why this shape: on the Waveshare panel, bench-testingkDimContrast = 0x10and0x02both produced only ~10 % perceptual reduction (SH1107's contrast register vs apparent brightness is heavily non-linear on this hardware), so the only reliable per-pixel dim available is rendering fewer pixels. The breathing dot lights ~4 of 8 192 pixels half the time — roughly a 1 000× drop in cumulative current — while still indicating "the dongle is alive," and the rotating position spreads OLED wear across the panel. - Trigger-flow diagnostic counters on the Diagnostics screen.
host02(total0x02HID OUT reports from host) /trig(those where the host setAllowRight|LeftTriggerFFBinvalid_flag0) /tx(forwarded as BT0x31sub-0x10). Added in response to issue #3 ("trigger tension missing in Death Stranding 2"). Lets the user triage in one game session whether the dongle, the host driver, or the controller is the source of the missing adaptive-trigger effect — without a UART or BT sniffer. - Diagnostics screen now scrolls with the controller D-pad. Refactored to a row-list (10 rows currently: Uptime / BT state / host02 / trig+tx / BT31 in/s / USB aud/s / BT32 out/s / Mic in/s / Mic dec=&w= / Mic prefix). 5 rows visible at a time; ▲/▼ glyphs at the right edge mark "more above/below." Read-only — no cursor, unlike Settings, since there's nothing to select.
Changed
flush_fb()split. Internal refactor:flush_fb_raw()writes just the framebuffer;flush_fb()is nowdraw_button_chrome() + flush_fb_raw(). Lets the dim-tier renderer push the breathing dot without the K0/K1 chrome arrows (no navigation target while the panel is asleep).- Diagnostics row order re-prioritized. The first 5 rows (always visible without scrolling) cover the most common triage path: Uptime / BT state /
host02/trig+tx/BT31 in/s. Audio + parked-mic-investigation counters live below the fold.
[0.6.3-oled-edition] — 2026-05-18
Small follow-up to v0.6.2. UF2s attached to the GitHub release (built by .github/workflows/release.yml).
Fixed
- OLED Status header was stuck on
"DS5 Bridge v0.6.0". The string was hardcoded insrc/oled.cppand never got bumped per release, so v0.6.1 and v0.6.2 both shipped with stale text on the Status screen. Now driven by a compile-timeFIRMWARE_VERSIONmacro set fromCMakeLists.txt's${VERSION}(whichrelease.ymlalready passes as-DVERSION="$FIRMWARE_VERSION"). Single source of truth: the release tag. Local builds without-DVERSIONshow"dev"so an untagged build is obvious at a glance. - Web preview's Status header had the same bug.
src/oled/screens.tshardcoded"v0.5.4". Now readsfirmware-latest.json(already CI-bundled from the GitHub API) at runtime inOledEmulator.tsxand writes the short tag (suffix-oled-editionstripped) intostate.firmwareVersionLabel, whichrenderStatus()consumes.
Documentation
- New
CLAUDE.md"Versioning — single source of truth" section documents the release ritual (CHANGELOG bump → tag → push →gh release create) and the single-source-of-truth flow from tag → CMake → C++ macro → webfirmware-latest.json. Includes a note about the still-pendingWEB_REPO_DISPATCH_PATsecret on the firmware repo.
[0.6.2-oled-edition] — 2026-05-18
OLED button-model + visual chrome refactor on top of v0.6.1. UF2s attached to the GitHub release (built by .github/workflows/release.yml).
Changed (button model)
- KEY0 / KEY1 are now strictly navigation on every screen. KEY0 short-press = next screen, KEY1 short-press = previous screen. KEY1 long-press still cycles OLED brightness (unchanged). The old contextual K1=cycle behavior on Trigger Test (cycle trigger preset) and Lightbar (cycle lightbar mode) moved to DualSense controller buttons — Triangle on Trigger Test, R1 on Lightbar. Source of "the Mode label didn't change when I clicked K1" confusion eliminated.
- KEY0 double-click reboot → KEY0 + KEY1 simultaneous hold (≥ 1 s). Rapid forward-navigation kept tripping the double-click timer by accident, soft-rebooting the dongle mid-session. The new two-button chord can't be fat-fingered.
kDoubleClickUs+key0_pending_singlestate removed; newchord_held_since_us+kChordHoldUs = 1 s. DS5 PS+Mute hold-2 s remains the headless backup. - Per-screen contextual actions on the controller mirror the existing Slots / Settings conventions (where Triangle has always meant "commit / switch / save"):
- Trigger Test — △ rising edge cycles
trigger_presetand re-applies viasend_trigger_effect(). - Lightbar — R1 rising edge cycles
lb_mode. (Triangle stays as "save current RGB to favorite slot 0" — the existing favorite-save UX.)
- Trigger Test — △ rising edge cycles
Changed (visual chrome)
- Arrow chrome on the left edge of every screen.
flush_fb()now paints>at(0, 8)and<at(0, 49)so the on-screen labels physically pair with the KEY0 (top) and KEY1 (bottom) buttons. The horizontal"K0=next K1=back"footer at y=56 is removed from all 11 screens. Trigger Test footer ="Tri=cycle"; Lightbar footer ="R1=mode"; Slots and Settings keep their existing contextual hints ("Tri=switch Sq hold=wipe","DP nav/adj Tri=save"). NewkContentX = 6shifts every screen's content right by 6 px to clear the chrome strip; rectangles, sticks, and the L1/L2 column on Status all repositioned to avoid the chrome<glyph painting inside the live left-stick area.
Added (web preview parity)
src/protocol/ds5BridgeHid.tssendTriggerPreset(preset)— builds the DS5 SetStateData payload byte-for-byte fromsrc/oled.cpp send_trigger_effect()and pushes viadevice.sendReport(0x02, ...). The dongle relays it over BT to the paired controller, so cycling Trigger Test in the web preview actually drives the real adaptive triggers.- Web preview mirrors the firmware refactor.
key1Action()collapsed to back-nav-only. New rising-edge handlers inOledEmulator.tsxdetect Triangle / R1 / D-pad from the live controller's input report and dispatch to the appropriate per-screen action.drawButtonChrome(fb)paints the>/<arrows after every render.flush()accepts an optional tint color: Slots / Diagnostics / CPU/Clock render in orange (#f59e0b) when a controller is connected — Chrome WebHID can't expose those reports on a stock DualSense descriptor, so the orange tint + an explanatory paragraph below the canvas flag the values as mock. KEY0/KEY1 buttons in the UI moved to sit visually next to the rendered Pico-OLED-1.3, mirroring the physical board. - Settings cursor on the web — new
settingsSelstate,>cursor mark on the selected row; D-pad up/down on the connected controller moves the cursor (web-preview-only — actual edits + save happen via the dedicated Config tab on the website). - Mock-data temperature tweak — web Preview's CPU/Clock screen no longer drifts 41–47 °C; jitter is now ±0.4 °C around 33.6 °C (realistic Pico 2 W idle).
Documentation
- README "Web Config Tool" section added near the top, linking https://marcelinevpq.github.io/DS5Dongle-OLED-Config-Web/#config and explaining the three tabs (Flash / Config / OLED Preview). Includes a BOOTSEL mode primer for first-time flashers.
- OLED Display Add-on section rewritten. Screen count 10 → 11 (CPU/Clock added). Cycle order updated. New "Button reference" table covers the strict K0/K1 nav, K1 long-press brightness, and K0+K1 chord reboot. ASCII mockups dropped in favor of consistent web-preview screenshots under
assets/oled/. - Performance / Overclocking section reworded to lead with "you don't need to do anything — the overclock is baked into the firmware". The "raise voltage / lower clock if it fails to boot" line is scoped to users compiling from source.
[0.6.1-oled-edition] — 2026-05-18
Tagged release of the v0.6.0-oled-edition follow-up. UF2s attached to the GitHub release (built by .github/workflows/release.yml).
Added
- CPU / Clock diagnostics screen (
kScreenCpu, inserted between Diagnostics and BT Signal in the K0 cycle). Shows the configured system clock (SYS_CLOCK_KHZ— the overclock target), the actually runningclk_sysmeasured live by the RP2350 on-chip frequency counter against the crystal reference, the core voltage read back from the regulator (vreg_get_voltage(), not the compile-time constant), and the RP2350 on-die temperature (ADC input 4). Pure read-only instrumentation; one-time ADC bring-up and no other code path uses the ADC, so it is conflict-free.render_screen_cpu()isnoinlinelike the other render functions (Thumb literal-pool reach). Addshardware_adctotarget_link_libraries. The frequency-counter measurement (a multi-ms busy-wait) runs once on screen entry and is cached —clk_sysis fixed at boot, so only the temperature refreshes per frame, avoiding a per-frame BT/audio hitch while the screen is visible.oled_loop()gained a genericscreen_enteredflag for this. Hardware-verified on Pico 2 W + OLED. Also exported over a new HID feature report0xfc(src/cmd.cpp) — 11 bytes: set_khz, cached real_khz, vreg code, ADC ch4 raw — so the web config emulator can show live CPU telemetry (volts/temp math done web-side to keep the firmware HID path float-free).
Fixed (web telemetry — latent since the slots/diag reports landed)
- CPU/Clock temperature was a single noisy ADC sample. The RP2350 temp sensor has a shallow slope (−1.721 mV/°C, ~1 LSB ≈ 0.47 °C) so a lone 12-bit reading swings several tenths of a degree per frame — the displayed value just mirrored the latest noisy sample instead of the true die temperature. New
cpu_temp_raw_smoothed()insrc/cmd.cppaverages a 256-sample block then runs a slow EMA (α=0.15, seeded on first call). It is the single source of truth: bothrender_screen_cpu()and the0xfcweb telemetry call it, and the duplicated per-site ADC bring-up was removed (ADC now initialised in exactly one place).oled.cppno longer touches the ADC directly (dropshardware/adc.h, addscmd.h). - Live web telemetry over WebHID: not feasible on the target setup; abandoned. A browser-side read-only diagnostic proved Chrome WebHID returns
NotAllowedErrorfor any report ID not declared in the parsed HID report descriptor (declared0xF7/0xF8/0xF9read fine; undeclared0xFA/0xFB/0xFCfail). Declaring them is therefore mandatory for the web read — but doing so (even applied atomically with a matchingwDescriptorLength, correct bytes identical in shape to the working0xF6–0xF9, and abcdDevicecache-bust bump) made the device fail to enumerate as a usable HID device on the user's real Windows machine in two independent attempts (Device Manager showed it; WebHID and the PlayStation Accessories app did not). The cloned DualSense HID report descriptor cannot be safely extended on this environment. Reverted to the original descriptor (0x0141/0x01B5, no vendor feature reports,bcdDevice 0x0100). Retained with no USB impact: the0xfcfirmware handler and the temperature smoothing/cpu_temp_raw_smoothed(). The on-device CPU/Clock OLED screen is fully working and hardware-verified; the web preview's CPU screen stays on representative mock values (the slots/diagnostics web screens were never readable for the same root cause and are likewise mock-only when connected).
Fixed
- Low-battery LED keeps blinking after controller disconnect (
fb68ea5). When the DualSense's battery dropped low enough to triggerbattery_led_tick's blink and the controller subsequently disconnected (typically: battery fully depletes and the BT link drops), the Pico's onboard LED stayed frozen in whichever half-cycle it was in at the moment of disconnect; reconnect-retry windows could even briefly resume blinking. Newbattery_led_on_disconnect()clears blink state, forces LED off, and zeroslast_report_usso the stale-check early-return blocks any new blink until a fresh 0x31 report arrives on the next connection. Stale-check in the tick also now forces LED off when it fires mid-blink (defense in depth for ungraceful disconnects). Reported by Sura Academy on Discord. Same bug present in upstream — sent back as awalol/DS5Dongle#101.
[0.6.0-oled-edition] — 2026-05-17
Tagged release of the rebase below. UF2s attached to the GitHub release (built by .github/workflows/release.yml). No code changes vs the rebase; tag exists so users can install from a stable artifact.
[0.6.0-rebase] — 2026-05-17
Rebased onto upstream awalol/DS5Dongle v0.6.0-hotfix. All OLED Edition features preserved with no user-visible regression.
Changed
- Adopted upstream's
state_mgrrefactor (awalol/DS5Dongle#93) as the new base for controller-state and audio-packet construction. The local speaker / HD-haptic regression fix we shipped inbff65d6(re-introducing a 63-bytestate_datablock into every audio packet) is dropped — upstream'sstate_mgr.cppis the proper architectural fix and supersedes the hack. Speaker, HD haptics, and basic rumble all verified working on the rebased firmware. Same fix loteran/DS5Dongle shipped asc7a8d3c~6 h before ours. - All upstream commits between our prior base (
a2e3a33) andv0.6.0-hotfixare now in our tree:0a33aaePR#93 merge,b1019fbREADME update,54a4b69config struct comment typo,f882ff1adjust default state-init volume,77bbee5rumble transfer fix,c2e0d84state init after connected,7bbe37bstate_mgr refactor itself,9a2c2b4discoverable / connectable off when connected,508a841DISABLE_SPEAKER_PROC option,b308545audio.cpp comment,0ed05d3rumble hotfix. - Our
update_discoverable()helper (gatesgap_discoverable_controlonslots_any_empty()) replaces upstream's strictergap_*_control(false)pair on L2CAP connect. Effect: dongle stays discoverable while at least one slot is empty (needed for slot-1/2/3 pairing); only goes dark when all 4 are full. Strictly looser than upstream's rule, but correct for the multi-slot use case.
Fixed
awalol/DS5Dongle#100"(v0.6.0) Dongle is detected as a new device on Windows when using different USB ports" — upstream'se79c762 remove usb serialnumber #32zeroed the device descriptor'siSerialNumberto work around a SpecialK compat issue, but that broke Windows device identity: users lost per-device volume / app settings every time they moved the dongle to a different USB port. The OLED Edition restoresiSerialNumber = STRID_SERIAL(per-board unique serial from flash chip ID viaboard_usb_get_serial). Trade-off documented insrc/usb_descriptors.cpp: re-introduces SpecialK incompatibility (#32) for the narrower set of Windows users with that specific tool, in exchange for stable device identity for the broader Windows population.
Verification
End-to-end on user's hardware after the rebase:
- DS5 pairs cleanly.
- Speaker audio via
scripts/test_speaker.sh --tone 440 3— audible (load-bearing check that upstream's state_mgr does what we expect). - Basic rumble works in games (validates we didn't disturb upstream's
0ed05d3hotfix). - All 10 OLED screens render correctly with live data.
- Multi-slot pairing: switch between slots, slot_assign on empty, wipe-from-Settings all work.
- PS + Mute hold-2s combo reboot triggers
watchdog_reboot(validates ourinterrupt_loopaddition coexists with upstream'sstate_updateflow).
Unchanged
The full prior CHANGELOG history for [0.5.4] and earlier sessions remains accurate and below.
[Unreleased]
Fixed
- DualSense speaker + HD haptic actuator regression — upstream commit
3a31bd7(2026-05-12, "refactor: add SetStateData and audio send priority") moved the0x10SetStateData sub-report out of every0x36audio packet and into a one-time L2CAP-open setup. The DS5 hardware requires that sub-report to be re-asserted on every audio frame (the0x7f 0x7fHeadphones+Speaker volume bytes specifically) or the speaker and HD haptic actuators silently stop producing output. Restored insrc/audio.cppwith the pre-3a31bd7 packet layout (state_data atpkt[11..75], haptic atpkt[76..141], speaker atpkt[142..343]). Same fix shipped independently by loteran/DS5Dongle as commitc7a8d3c~6 h before ours; credit to loteran for the clearer hardware-side explanation in their commit message. - USB UAC1 SET_CUR Volume request no longer overrides flash-persisted speaker_volume. PipeWire / PulseAudio re-apply their last-known UAC1 volume on every device reconnect, which had silently overridden whatever the user had saved in the OLED Settings menu. The in-memory
volume[]array still tracks live host volume; only the flash sync was removed. Fix borrowed from loteran/DS5Dongle commit03fa1e4.
Added
- Audio Auto Haptics — derive haptic feedback from the speaker audio (UAC channels 0/1) for games that send sound but no per-frame haptic data, e.g. Ghost of Tsushima on Linux + Steam. DSP is a 1-pole low-pass + envelope follower + modulation + soft-clip (
x / (1 + |x|), avoidstanhfon Cortex-M33), borrowed from loteran/DS5Dongle commit5d6bc2f. Four modes selectable from the OLED Settings menu: Off / Fallback / Mix / Replace. Default is Fallback — derived rumble fires only after the game has been silent on the native haptic path (channels 2/3) for ~1 s, so games that send native HD haptics (Spider-Man Remastered) are not overridden, while games that don't (Ghost of Tsushima) get derived haptics out of the box. Gain (0–200 %) and LP cutoff (80 / 160 / 250 / 400 Hz) are also tunable from the Settings menu. - Audio Diagnostics counters on the OLED Diagnostics screen:
USB aud N/s(UAC1 frames per second arriving from the host) andBT 0x32 N/s(audio packets emitted to the DS5 per second). Lets the user verify the speaker/haptic path is actually moving bytes without needing a UART cable. Used to triage the speaker regression above.
0.5.4 — 2026-05-16
First full OLED Edition release. Includes upstream's v0.5.4 base plus the audit pass and the entire OLED add-on feature set.
Added — OLED display add-on
Requires a Waveshare Pico-OLED-1.3 (128×64 SH1107). Firmware drives it automatically when present and no-ops gracefully when absent.
- Boot splash (1.5 s) showing firmware version on power-on.
- 10 screens, cycled with KEY0 (forward) / KEY1 (back, except where contextual):
- Status — connection state, paired BD address, battery % with pixel-icon battery, live stick / D-pad / face-button / L1-R1 / L2-R2 trigger visualization.
- Slots — persistent 4-slot multi-controller pairing (see below).
- Lightbar Color Picker — tilt-to-RGB live preview on the controller's lightbar, with 4 user-savable favorite slots (△ ○ ✕ □) and three effect presets (Breathing, Rainbow, Fade).
- Trigger Test — cycles 7 DS5 adaptive trigger effects (Off / Feedback / Weapon / Vibration / Bow / Galloping / Machine Gun) on both L2 and R2, bitpacked per dualsensectl's reverse-engineering.
- Gyro Tilt — live X/Y/Z accelerometer values + 40×40 crosshair box that tracks tilt in real time.
- Touchpad — live render of finger positions on the touchpad surface, with a finger count.
- Diagnostics — uptime, BT state, HCI / audio-FIFO / opus-FIFO counter stubs (kept for future wiring).
- RSSI — live BT signal strength of the active link, dBm + bar.
- VU Meters — live peak meters for the speaker + haptic audio paths.
- Settings — persistent on-device editor for the 8 firmware config fields, plus "Reset to defaults" (hold △ 2 s) and "Wipe all slots" (hold △ 2 s).
- OLED brightness control — KEY1 long-press cycles brightness levels.
- Auto-dim after 5 minutes of input idle (lifespan / burn-in protection).
- Soft-reboot recovery without unplugging USB:
- OLED KEY0 double-click (~400 ms window) →
watchdog_reboot. - DS5
PS + Muteheld for 2 seconds →watchdog_reboot(works without the OLED).
- OLED KEY0 double-click (~400 ms window) →
- Pixel-art icons in the Status screen header (link indicator + battery icon).
Added — 4-slot persistent multi-controller pairing
- Bond up to 4 DualSenses; switch between them from the Slots OLED screen.
- Active slot persisted in the existing flash-backed config; dongle reconnects to the last-used controller on boot.
- D-pad ▲▼ to move cursor, △ to switch to the cursor slot (disconnect current ACL, restart inquiry filtered to the new slot's bd_addr), □ hold 1.5 s to wipe a single slot.
- "Wipe all slots" item in the Settings menu drops all 4 stored bd_addrs + all BTstack link keys in one shot.
- Inquiry filter on
HCI_EVENT_INQUIRY_RESULTenforces slot ownership: devices stored in other slots are skipped; empty slots auto-assign on the first L2CAP HID_CONTROL channel open. - Dongle stops being BT-discoverable once all 4 slots are full (security tightening; was permanently discoverable in upstream).
- Storage: BTstack's TLV link-key DB holds the keys (
NVM_NUM_LINK_KEYS=4, unchanged from upstream); a new dedicated flash sector holds the 4 bd_addrs + occupancy bits + magic word. - Multi-slot UX modeled on zurce/DS5Dongle-OLED. Credit to zurce.
Added — security / correctness audit pass
Critical and high-severity fixes on the core bridge code. Many of these have since landed upstream independently; this changelog captures what this fork shipped.
- C1: 4.8× stack-overflow in
core1_entry'sout_buf(200 floats vs. 960 floats the resampler/encoder actually writes/reads). Root cause of the long-standing "audio may experience slight stuttering" known issue. Buffer resized and Opus return value now checked. - C2 + H5: Variable-length stack array in
set_feature_datasized by host-controlled length (potential stack blowup). Replaced with a bounded fixed buffer + length validation; CRC bounds tightened solen < 4no longer wraps to a hugesize_t. - C3: Unbounded
memcpyinto a 78-byte stack buffer intud_hid_set_report_cbfor HID report 0x02 (overflow ifbufsize > 76). Bounded. - H1:
tud_hid_get_report_cbwrotefeature_data.size() - 1bytes into the host-supplied buffer without clamping toreqlen(host-buffer overflow). Clamped. - H2: OOB read in
on_bt_data(data[56]/memcpy(.., data+3, 63)with no length check on the incoming BT frame). Bounded. - H3: Same pattern in
l2cap_packet_handler(packet[3]read with no size check). Bounded. - H4: UAC1 volume parsed as
uint16instead of signedint16— non-conformant hosts could crank haptic gain to 256× and saturate everything. Fixed sign extension. - H7: BT report sequence counter in
main.cppcycled with period 16 (incremented asint, written as(c<<4)). Replaced with the correct(c+1) & 0x0Fwrap used inaudio.cpp. - N1: Pairing-failure recovery: three
gap_inquiry_start(30)calls were commented out inbt.cpp's error paths (HCI command status, connection complete, authentication complete). Dongle would soft-brick after any pairing failure that didn't end in a disconnect. Uncommented. - N2: Removed printf of the bonding link-key to UART on every reconnect.
- N3: CRC-helper bounds:
fill_output_report_checksum/fill_feature_report_checksumunderflowedsize_tand wrote at negative offsets onlen < 4. Guarded. - N4: HCI command-send return values logged so a future stuck-dongle report has observability.
- N5: Watchdog enabled (8 s). Hangs now recover automatically instead of needing a power-cycle.
Added — pairing posture hardening
- Tightened
gap_discoverable_control(1)— the dongle now only advertises as pairable when at least one slot is empty. Once all 4 slots are full, it goes non-discoverable (still connectable to bonded controllers). - (Carried over from upstream's own hardening; documented here for completeness.)
Changed
- Project rebranded as OLED Edition to differentiate from upstream. UF2 artifact renamed:
ds5-bridge.uf2→ds5-bridge-oled.uf2. - README rewritten with full hardware section (Pico 2 W + Pico-OLED-1.3 SKUs, vendor links, prices), all 10 OLED screen mockups in the new cycle order, and explicit
TINYUSB 0.20.0pin requirement (the 0.18.0 bundled with Pico SDK 2.2.0 lacks the 4-arg form ofTUD_AUDIO_EP_SIZEused by this project'stusb_config.h). - KEY1 short-press: was a 250 ms test-rumble burst on most screens; now cycles backward through screens (mirror of KEY0). Still cycles trigger preset on Trigger Test and lightbar mode on Lightbar (their primary in-screen interactions).
- Screen cycle order reorganized: Status → Slots → Lightbar → Trigger Test → Gyro Tilt → Touchpad → Diagnostics → RSSI → VU Meters → Settings. Settings last; the three "test" stimulus screens grouped together; diagnostic screens grouped together. Screen indices are symbolic constants (
kScreenStatus,kScreenSlots, …) so future reorders are a one-block edit.
Fixed
config_default()was declared inconfig.hbut never defined upstream (latent "undefined symbol" → linker "dangerous relocation" if anything called it). Implemented (fills body with0xFF, runsconfig_valid()— same path as a freshly-erased flash sector).- GitHub Actions workflow
cppaths corrected to match theds5-bridge-oled.uf2produced after the CMakeOUTPUT_NAMErename. (Workflows had failed on every push since the rebrand commit.)
Build
- TinyUSB pinned to
0.20.0(required; see Build Instructions in README). - Pico SDK 2.2.0 + toolchain
14_2_Rel1validated. Pico 2 W (RP2350) is the primary target. CMakeLists.txtaddssrc/oled.cpp+src/slots.cppto the executable target, linkshardware_spi.OUTPUT_NAMEset tods5-bridge-oledso the UF2 reflects the project name.
Acknowledgements
- awalol/DS5Dongle — upstream base. This fork is a strict superset that tracks upstream and layers add-on features.
- zurce/DS5Dongle-OLED — pixel-art icon approach, the "hold for factory reset" UX pattern, and the multi-slot persistent pairing model.