From fb8624b61b1aba49a7ab0638896356b3e3eb2198 Mon Sep 17 00:00:00 2001 From: awalol Date: Tue, 5 May 2026 01:17:15 +0800 Subject: [PATCH] feat: config manager --- CMakeLists.txt | 5 +- src/audio.cpp | 11 ++--- src/bt.cpp | 8 ++- src/cmd.cpp | 60 +++++++++++++++++++++++ src/cmd.h | 14 ++++++ src/config.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++ src/config.h | 34 +++++++++++++ src/main.cpp | 14 ++++++ src/usb_descriptors.c | 22 +++++++-- 9 files changed, 263 insertions(+), 15 deletions(-) create mode 100644 src/cmd.cpp create mode 100644 src/cmd.h create mode 100644 src/config.cpp create mode 100644 src/config.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e9b4679..d39245d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,8 @@ add_executable(ds5-bridge src/usb.cpp src/audio.cpp src/bt.cpp + src/config.cpp + src/cmd.cpp ) # Cockos WDLResampler @@ -67,7 +69,7 @@ target_compile_definitions(ds5-bridge PRIVATE CYW43_LWIP=0 PICO_FLASH_ASSUME_CORE1_SAFE=1 WDL_RESAMPLE_TYPE=float - CYW43_PIO_CLOCK_DIV_INT=4 + CYW43_PIO_CLOCK_DIV_INT=5 ) option(ENABLE_DSE "Enable DSE" OFF) if(ENABLE_DSE) @@ -98,6 +100,7 @@ target_link_libraries(ds5-bridge pico_multicore hardware_watchdog hardware_timer + hardware_flash pico_btstack_classic # pico_cyw43_arch_threadsafe_background pico_cyw43_arch_poll diff --git a/src/audio.cpp b/src/audio.cpp index f5be2bc..5bad48e 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -6,14 +6,13 @@ #include "bt.h" #include "resample.h" #include "tusb.h" -#include "usb.h" #include #include - #include "opus.h" #include "utils.h" #include "pico/multicore.h" #include "pico/util/queue.h" +#include "config.h" #define INPUT_CHANNELS 4 #define OUTPUT_CHANNELS 2 @@ -72,8 +71,8 @@ void audio_loop() { int nframes = resampler.ResamplePrepare(frames, OUTPUT_CHANNELS, &in_buf); for (int i = 0; i < nframes; i++) { - audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS] / 32768.0f * (volume[0] - 1.0f); - audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS + 1] / 32768.0f * (volume[0] - 1.0f); + audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS] / 32768.0f * (get_config().speaker_volume - 1.0f); + audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS + 1] / 32768.0f * (get_config().speaker_volume - 1.0f); if (audio_buf_pos == 512 * 2) { static audio_raw_element element{}; memcpy(element.data,audio_buf,512 * 2 * 4); @@ -99,8 +98,8 @@ void audio_loop() { // 4. 转换为int8并缓冲,满64字节即组包发送 for (int i = 0; i < out_frames; i++) { - int val_l = (int) (out_buf[i * 2] * 127.0f * max(volume[1],1.0f)); - int val_r = (int) (out_buf[i * 2 + 1] * 127.0f * max(volume[1],1.0f)); + int val_l = (int) (out_buf[i * 2] * 127.0f * max(get_config().haptics_gain,1.0f)); + int val_r = (int) (out_buf[i * 2 + 1] * 127.0f * max(get_config().haptics_gain,1.0f)); haptic_buf[haptic_buf_pos++] = (int8_t) clamp(val_l, -128, 127); // 似乎clamp有点多余?还是以防万一吧 haptic_buf[haptic_buf_pos++] = (int8_t) clamp(val_r, -128, 127); diff --git a/src/bt.cpp b/src/bt.cpp index 1d90494..06ea162 100644 --- a/src/bt.cpp +++ b/src/bt.cpp @@ -4,13 +4,10 @@ #include #include - #include "bt.h" - #include #include #include - #include "btstack_event.h" #include "l2cap.h" #include "pico/cyw43_arch.h" @@ -20,6 +17,7 @@ #include "bsp/board_api.h" #include "pico/sync.h" #include "classic/sdp_server.h" +#include "config.h" #define MTU 672 @@ -321,7 +319,7 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t bt_data_callback(INTERRUPT, packet, size); // 静默检测 - if (mute[1]) { // 麦克风静音开启 + if (get_config().disable_inactive_disconnect) { return; } if (packet[3] < 120 || packet[3] > 140) { @@ -361,7 +359,7 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid); hid_interrupt_cid = local_cid; - if (!mute[0]) { + if (!get_config().disable_pico_led) { cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true); } inactive_time = time_us_32(); diff --git a/src/cmd.cpp b/src/cmd.cpp new file mode 100644 index 0000000..7ddc277 --- /dev/null +++ b/src/cmd.cpp @@ -0,0 +1,60 @@ +// +// Created by awalol on 2026/5/4. +// + +#include "cmd.h" + +#include +#include +#include + +#include "config.h" +#include "device/usbd.h" +#include "pico/time.h" + +bool is_pico_cmd(uint8_t report_id) { + if (report_id == 0xf6 || + report_id == 0xf7 + ) { + return true; + } + return false; +} + +uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) { + if (report_id == 0xf7) { + printf("[HID] Receive 0xf7 getting config\n"); + if (sizeof(Config_body) > reqlen) { + printf("[Config] Warning: Config_body overflow\n"); + } + const auto len = std::min(sizeof(Config_body),static_cast(reqlen)); + memcpy(buffer,&get_config(),len); + return len; + } + return 0; +} + +void pico_cmd_set(uint8_t report_id, uint8_t const *buffer, uint16_t bufsize) { + (void) report_id; + if (bufsize == 0) { + return; + } + + // 0x01 update config in variable + // 0x02 write config to flash + // 0x03 reconnect tinyusb device; + if (buffer[0] == 0x01) { + printf("[CMD] Enter config set func\n"); + set_config(buffer + 1, bufsize - 1); + } + if (buffer[0] == 0x02) { + printf("[CMD] Enter config save func\n"); + config_save(); + } + if (buffer[0] == 0x03) { + printf("[CMD] Enter tud reconnect func\n"); + tud_disconnect(); + sleep_ms(1000); + tud_connect(); + } +} diff --git a/src/cmd.h b/src/cmd.h new file mode 100644 index 0000000..13d075c --- /dev/null +++ b/src/cmd.h @@ -0,0 +1,14 @@ +// +// Created by awalol on 2026/5/4. +// + +#ifndef DS5_BRIDGE_CMD_H +#define DS5_BRIDGE_CMD_H + +#include + +bool is_pico_cmd(uint8_t report_id); +uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer,uint16_t reqlen); +void pico_cmd_set(uint8_t report_id, uint8_t const *buffer,uint16_t bufsize); + +#endif //DS5_BRIDGE_CMD_H diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..8853480 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,110 @@ +// +// Created by awalol on 2026/5/4. +// + +#include "config.h" + +#include +#include + +#include "utils.h" +#include "hardware/flash.h" +#include "hardware/sync.h" + +constexpr uint32_t CONFIG_MAGIC = 0x66ccff00; +constexpr uint16_t CONFIG_VERSION = 1; +constexpr uint32_t CONFIG_FLASH_OFFSET = PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE; +static Config config{}; + +// 编译期保护 +// 判断Config结构体是否能放进flash 256bytes +static_assert(sizeof(Config) <= FLASH_PAGE_SIZE); +// 配置区起始地址必须按 flash sector 对齐。 +static_assert(CONFIG_FLASH_OFFSET % FLASH_SECTOR_SIZE == 0); + +uint32_t calc_config_crc(const Config &con) { + return crc32(reinterpret_cast(&con.body), sizeof(Config_body)); +} + +const Config *flash_config() { + return reinterpret_cast(XIP_BASE + CONFIG_FLASH_OFFSET); +} + +void config_valid() { + // valid config and set default value + if (config.magic != CONFIG_MAGIC) { + config.magic = CONFIG_MAGIC; + printf("[Config] Config Magic Header is invalid\n"); + } + if (config.version != CONFIG_VERSION) { + config.version = CONFIG_VERSION; + printf("[Config] Config Version is invalid\n"); + } + if (config.size != sizeof(Config_body)) { + config.size = sizeof(Config_body); + printf("[Config] Config Body size is invalid\n"); + } + auto body = &config.body; + if (std::isnan(body->haptics_gain) || body->haptics_gain < 1.0f || body->haptics_gain > 2.0f) { + body->haptics_gain = 1.0f; + printf("[Config] Haptics Gain value is invalid\n"); + } + if (std::isnan(body->speaker_volume) || body->speaker_volume < 1.0f || body->speaker_volume > 2.0f) { + body->speaker_volume = 2.0f; + printf("[Config] Speaker Volume is invalid\n"); + } + if (body->disable_inactive_disconnect > 1) { + body->disable_inactive_disconnect = 0; + printf("[Config] disable_auto_disconnect is invalid\n"); + } + if (body->disable_pico_led > 1) { + body->disable_pico_led = 0; + printf("[Config] disable_pico_led is invalid\n"); + } + if (body->polling_rate_mode > 2) { + body->polling_rate_mode = 0; + printf("[Config] polling_rate_mode is invalid\n"); + } + if (body->haptics_buffer_length < 16 || body->haptics_buffer_length > 255) { + body->haptics_buffer_length = 48; + printf("[Config] haptics_buffer_length is invalid\n"); + } +} + +void config_load() { + memcpy(&config, flash_config(), sizeof(Config)); + + config_valid(); +} + +bool config_save() { + config.crc32 = calc_config_crc(config); + alignas(4) uint8_t page[FLASH_PAGE_SIZE]; + memset(page, 0xff, sizeof(page)); + memcpy(page, &config, sizeof(Config)); + + const uint32_t interrupts = save_and_disable_interrupts(); + flash_range_erase(CONFIG_FLASH_OFFSET, FLASH_SECTOR_SIZE); + flash_range_program(CONFIG_FLASH_OFFSET, page, sizeof(page)); + restore_interrupts(interrupts); + + Config verify{}; + memcpy(&verify, flash_config(), sizeof(verify)); + const auto verify_crc32 = calc_config_crc(verify); + if (verify_crc32 == config.crc32) { + printf("[Config] Config write flash verify success\n"); + return true; + } + printf("[Config] Config write flash verify failed\n"); + return false; +} + +const Config_body& get_config() { + return config.body; +} + +void set_config(const uint8_t *new_config, const uint16_t len) { + const auto copy_len = len < sizeof(Config_body) ? len : sizeof(Config_body); + memcpy(&config.body, new_config, copy_len); + config_valid(); +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..c3af2cd --- /dev/null +++ b/src/config.h @@ -0,0 +1,34 @@ +// +// Created by awalol on 2026/5/4. +// + +#ifndef DS5_BRIDGE_CONFIG_H +#define DS5_BRIDGE_CONFIG_H + +#include + +struct __attribute__((packed)) Config_body { + float haptics_gain; // [1.0,2.0] + float speaker_volume; // [1.0,2.0] + uint8_t disable_inactive_disconnect; // bool: 0 disable,1 enable + uint8_t disable_pico_led; // bool + uint8_t polling_rate_mode; // 0: 250Hz,1: 500Hz,2: instant + uint8_t haptics_buffer_length; // [16,255] +}; + +struct __attribute__((packed)) Config { + uint32_t magic; + uint16_t version; + uint32_t crc32; // Config_body crc32, only calc and verify when save + uint16_t size; // Config_body size + Config_body body; +}; + +void config_default(); +void config_load(); +bool config_save(); +const Config_body& get_config(); +void set_config(const uint8_t *new_config, const uint16_t len); +void config_valid(); + +#endif //DS5_BRIDGE_CONFIG_H diff --git a/src/main.cpp b/src/main.cpp index b19c028..086bcf1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,8 @@ #include "hardware/vreg.h" #include "hardware/watchdog.h" #include "pico/cyw43_arch.h" +#include "config.h" +#include "cmd.h" // Pico SDK speciifically for waiting on conditions #include "pico/critical_section.h" @@ -94,6 +96,10 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t (void) buffer; (void) reqlen; + if (is_pico_cmd(report_id)) { + return pico_cmd_get(report_id,buffer,reqlen); + } + std::vector feature_data = get_feature_data(report_id, reqlen); if (!feature_data.empty()) { memcpy(buffer, feature_data.data() + 1, feature_data.size() - 1); @@ -112,6 +118,12 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep (void) buffer; (void) bufsize; + if (is_pico_cmd(report_id)) { + printf("[HID] Receive 0xf6 setting config, funcid:0x%02X\n",buffer[0]); + pico_cmd_set(report_id,buffer,bufsize); + return; + } + // INTERRUPT OUT if (report_id == 0) { switch (buffer[0]) { @@ -173,6 +185,8 @@ int main() { // Initialize the critical section for the report buffer critical_section_init(&report_cs); + config_load(); + bt_init(); bt_register_data_callback(on_bt_data); diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index 8283d40..f2ac8c6 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -303,9 +303,9 @@ uint8_t const descriptor_configuration[] = { 0x01, // bNumDescriptors: 1 report descriptor 0x22, // bDescriptorType: Report #ifdef ENABLE_DSE - 0x95, 0x01, // wDescriptorLength: 405 (0x0121) + 0xA1, 0x01, // wDescriptorLength: 417 (0x01A1) #else - 0x21, 0x01, // wDescriptorLength: 289 (0x0121) + 0x31, 0x01, // wDescriptorLength: 289 (0x0121) #endif // Endpoint Descriptor (HID IN: EP4) @@ -480,6 +480,14 @@ uint8_t const desc_hid_report_ds[] = { 0x09, 0x36, // Usage (0x36) 0x95, 0x03, // Report Count (3) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF6, + 0x09, 0x37, + 0x95, 0x3F, + 0xB1, 0x02, + 0x85, 0xF7, + 0x09, 0x38, + 0x95, 0x3F, + 0xB1, 0x02, 0xC0, // End Collection // 289 bytes }; @@ -686,8 +694,16 @@ uint8_t const desc_hid_report_dse[] = { 0x85, 0x7B, // Report ID (123) 0x09, 0x53, // Usage (0x53) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF6, + 0x09, 0x37, + 0x95, 0x3F, + 0xB1, 0x02, + 0x85, 0xF7, + 0x09, 0x38, + 0x95, 0x3F, + 0xB1, 0x02, 0xC0, // End Collection - // 405 bytes + // 417 bytes }; #endif