Merge branch 'master' into master

This commit is contained in:
awalol
2026-05-13 18:17:25 +08:00
committed by GitHub
6 changed files with 129 additions and 2 deletions
+8
View File
@@ -94,6 +94,14 @@ jobs:
cmake --build build/debug --target ds5-bridge cmake --build build/debug --target ds5-bridge
cp build/debug/ds5-bridge.uf2 artifacts/ds5-bridge-debug.uf2 cp build/debug/ds5-bridge.uf2 artifacts/ds5-bridge-debug.uf2
- name: Build no-batt-led firmware (compile check only)
run: |
cmake -S . -B build/no-batt-led -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DPICO_SDK_PATH="$PICO_SDK_PATH" \
-DENABLE_BATT_LED=OFF
cmake --build build/no-batt-led --target ds5-bridge
- name: Upload standard UF2 artifact - name: Upload standard UF2 artifact
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
+6
View File
@@ -57,6 +57,12 @@ The Pico device will only be visible to the system after the controller is conne
Some behaviors depend on reconnection cycles to take effect Some behaviors depend on reconnection cycles to take effect
### Low-battery LED indicator
When the connected DualSense reports its battery at or below 10% (and it is not charging), the Pico onboard LED switches from solid-on to a 1 Hz blink so you can see the warning at a glance. The LED returns to solid-on as soon as the controller is plugged in or its reported level rises again. The blink also fires when `disable_pico_led` is set — the warning is treated as critical and overrides the LED-off preference; the LED returns to its disabled (off) state once the battery recovers or the controller starts charging.
To opt out at build time, configure with `-DENABLE_BATT_LED=OFF`. Default is ON.
## Known Issues ## Known Issues
- ⚠️ Audio may experience slight stuttering - ⚠️ Audio may experience slight stuttering
+73
View File
@@ -0,0 +1,73 @@
//
// Low-battery LED indicator. See battery_led.h.
//
#include "battery_led.h"
#include <cstdint>
#include "config.h"
#include "pico/cyw43_arch.h"
#include "pico/time.h"
extern uint8_t interrupt_in_data[63];
namespace {
constexpr uint64_t REPORT_STALE_US = 2'000'000; // assume disconnected if no report for 2 s
constexpr uint64_t BLINK_PERIOD_US = 500'000; // 1 Hz, 50% duty
constexpr uint8_t THRESHOLD_LEVEL = 1; // PowerPercent <= 1 (i.e. <= 10%)
constexpr uint8_t POWER_STATE_DISCHARGING = 0x0;
uint64_t last_report_us = 0;
uint64_t last_toggle_us = 0;
bool blinking = false;
bool led_state = false;
} // namespace
void battery_led_init(void) {
last_report_us = 0;
last_toggle_us = 0;
blinking = false;
led_state = false;
}
void battery_led_note_report(void) {
last_report_us = time_us_64();
}
void battery_led_tick(void) {
const uint64_t now = time_us_64();
if (last_report_us == 0 || (now - last_report_us) >= REPORT_STALE_US) {
// No fresh data — bt.cpp owns the LED while disconnected.
blinking = false;
return;
}
const uint8_t b = interrupt_in_data[52];
const uint8_t pct = b & 0x0F;
const uint8_t st = (b >> 4) & 0x0F;
const bool low = (st == POWER_STATE_DISCHARGING) && (pct <= THRESHOLD_LEVEL);
if (low) {
// Critical warning: override disable_pico_led so the user always sees it.
if (!blinking) {
blinking = true;
led_state = true;
last_toggle_us = now;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);
return;
}
if ((now - last_toggle_us) >= BLINK_PERIOD_US) {
led_state = !led_state;
last_toggle_us = now;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_state);
}
} else if (blinking) {
blinking = false;
// Battery recovered or now charging — restore steady-state LED per the user
// preference flag (LED off when disabled, otherwise the bt.cpp connected = on state).
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, !get_config().disable_pico_led);
}
}
+18
View File
@@ -0,0 +1,18 @@
//
// Low-battery LED indicator for the Pico onboard LED.
// Reads PowerPercent / PowerState from interrupt_in_data[52]
// (DualSense BT 0x31 report, see USBGetStateData in utils.h).
//
#pragma once
void battery_led_init(void);
// Call once per main-loop iteration. Drives the LED blink while the
// battery is low and the controller is connected; otherwise no-op.
void battery_led_tick(void);
// Call from the BT input-report callback whenever a fresh 0x31 report
// has been copied into interrupt_in_data. Used to detect disconnection
// via stale-report timeout.
void battery_led_note_report(void);
+8 -2
View File
@@ -17,8 +17,8 @@
#include "config.h" #include "config.h"
#include "pico/util/queue.h" #include "pico/util/queue.h"
#define MTU_CONTROL 256 #define MTU_CONTROL 672
#define MTU_INTERRUPT 1691 #define MTU_INTERRUPT 672
using std::unordered_map; using std::unordered_map;
using std::vector; using std::vector;
@@ -395,6 +395,9 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
if (psm == PSM_HID_CONTROL) { if (psm == PSM_HID_CONTROL) {
printf("[L2CAP] HID Control opened cid=0x%04X\n", local_cid); printf("[L2CAP] HID Control opened cid=0x%04X\n", local_cid);
hid_control_cid = local_cid; hid_control_cid = local_cid;
const auto mtu = l2cap_get_remote_mtu_for_local_cid(hid_control_cid);
printf("[L2CAP] Remote Control MTU: %d\n",mtu);
} else if (psm == PSM_HID_INTERRUPT) { } else if (psm == PSM_HID_INTERRUPT) {
printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid); printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid);
hid_interrupt_cid = local_cid; hid_interrupt_cid = local_cid;
@@ -429,6 +432,9 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
memcpy(report32 + 2, packet_0x10, sizeof(packet_0x10)); memcpy(report32 + 2, packet_0x10, sizeof(packet_0x10));
bt_write(report32, sizeof(report32)); bt_write(report32, sizeof(report32));
const auto mtu = l2cap_get_remote_mtu_for_local_cid(hid_interrupt_cid);
printf("[L2CAP] Remote Interrupt MTU: %d\n",mtu);
// tud_connect(); // tud_connect();
} else { } else {
printf("[L2CAP] Unknown Channel psm: 0x%02X", psm); printf("[L2CAP] Unknown Channel psm: 0x%02X", psm);
+16
View File
@@ -17,6 +17,9 @@
#endif #endif
#include "config.h" #include "config.h"
#include "cmd.h" #include "cmd.h"
#if ENABLE_BATT_LED
#include "battery_led.h"
#endif
// Pico SDK speciifically for waiting on conditions // Pico SDK speciifically for waiting on conditions
#include "pico/critical_section.h" #include "pico/critical_section.h"
@@ -85,6 +88,9 @@ void on_bt_data(CHANNEL_TYPE channel, uint8_t *data, uint16_t len) {
if (get_config().polling_rate_mode != 2) { if (get_config().polling_rate_mode != 2) {
memcpy(interrupt_in_data, data + 3, 63); memcpy(interrupt_in_data, data + 3, 63);
#if ENABLE_BATT_LED
battery_led_note_report();
#endif
return; return;
} }
@@ -98,6 +104,9 @@ void on_bt_data(CHANNEL_TYPE channel, uint8_t *data, uint16_t len) {
memcpy(interrupt_in_data, data + 3, 63); memcpy(interrupt_in_data, data + 3, 63);
report_dirty = true; report_dirty = true;
critical_section_exit(&report_cs); critical_section_exit(&report_cs);
#if ENABLE_BATT_LED
battery_led_note_report();
#endif
} }
} }
@@ -204,6 +213,10 @@ int main() {
} }
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, false); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, false);
#if ENABLE_BATT_LED
battery_led_init();
#endif
#if !ENABLE_SERIAL #if !ENABLE_SERIAL
if (watchdog_caused_reboot()) { if (watchdog_caused_reboot()) {
printf("Rebooted by Watchdog!\n"); printf("Rebooted by Watchdog!\n");
@@ -243,5 +256,8 @@ int main() {
tud_task(); tud_task();
audio_loop(); audio_loop();
interrupt_loop(); interrupt_loop();
#if ENABLE_BATT_LED
battery_led_tick();
#endif
} }
} }