Status screen: show an estimated time-to-full ("~43m") while charging,
self-calibrating from each 10% battery notch with Li-ion taper correction
(discard the partial plug-in step; 3-sample moving average; per-step weight
1.0/1.5/2.2 for bulk/80-90/90-100%). Shows "~--m" until the first full step.
Lightbar: the chosen mode + 4 favorites now persist to config flash and
stick across every screen (and through gameplay/audio). A single
lightbar_service() owns the LED via the persistent state[] block (new
state_set_led/state_get_led) with a host-override gate (g_lightbar_override)
so the host's AllowLedColor can't stomp a firmware-chosen mode. New HOST
passthrough mode (default) keeps the game in control of the LED out of the
box. The charging amber pulse (255,100,0) is folded in as top priority.
Idle ladder: keep the panel at the dim/dot tier (never full-off) while
charging so users stop unplugging to wake it (which reset the charge ETA).
Fix idle detection to deadzone stick jitter [120,140] + skip the counter
byte (idata[6]) so the dot tier engages with a controller connected
(mirrors bt.cpp's inactivity heuristic).
New Config_body fields: lightbar_mode + lb_fav_{r,g,b}[4].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In-progress work on DualSense microphone capture over BT. Mic-add tap
itself is disabled (was decoding standard input bytes as Opus and
producing INT16_MIN garbage on the USB IN endpoint) but everything
around it is wired and ready to re-enable once we identify the actual
mic transport.
Firmware:
- src/audio.cpp: Opus decoder on core0, mic_fifo queue, audio_loop
mic-in path with decode + mono->stereo + tud_audio_write. Decoder
init in audio_init() (creates 48kHz mono OpusDecoder).
- src/audio.h: exports mic_add_queue() + per-frame diagnostic
accessors (audio_mic_frames, last_decoded, last_want, last_wrote,
last_toc).
- src/main.cpp on_bt_data(): BT-side instrumentation — counts every
INTERRUPT input report, tracks min/max length, OR mask of byte[2],
most recent non-0x31 report ID, hex prefix of last 0x31/other/any
frame, full content of the longest 0x31 frame seen. Mic-tap call
itself stubbed behind `if (false)` pending the real detector.
- src/state_mgr.cpp: state_init_data byte 6 (VolumeMic) 0xFF→0x40
(was out of range), byte 9 (MuteControl) 0x0F→0x00 (clear all
PowerSave bits — AudioPowerSave was muting DSP).
- src/cmd.cpp: two new vendor feature reports — 0xFD returns 32-byte
diagnostic state (counters + prefixes), 0xFE returns the longest
0x31 frame in full (up to 80 bytes). Both queryable via
/dev/hidraw on Linux from the host script.
- src/oled.cpp: Diagnostics screen shows TOC + decode result + USB
wrote/want bytes for live BT-side visibility.
Host-side:
- scripts/mic_diag.sh: subcommands `status`, `capture [secs]`,
`watch`, `bt-trace`. The bt-trace subcommand reads the 0xFD
feature report via hidraw ioctl, decodes counters + recent
prefixes, computes per-second rates. Drastically cuts iteration
time — no OLED relay or per-test flash cycle needed.
Findings to date:
- Upstream/mic's mic-flag bit ((data[2] >> 1) & 1) does NOT match
this DS5 firmware; bit 1 of byte[2] is NEVER set. Bit 0 is the
standard input report type indicator, not a mic tag — confirmed
by stick-bytes appearing as our supposed "Opus prefix".
- DS5 sends both report ID 0x01 and 0x31 over BT; the longest frame
is a standard 79-byte 0x31 input report with sticks/IMU/touchpad
but no audio bytes appended.
- Conclusion in progress: the DS5 firmware on this controller is
not currently streaming mic over BT at all, even with
AllowAudioControl=1, VolumeMic=0x40, AudioPowerSave=0,
MicMute=0. Next investigation step: compare against a USB-mode
DS5 to see what a real mic stream looks like at the UAC1 layer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>