diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 00c09fd..5e8eac0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,9 @@ jobs: with: submodules: recursive + - name: Set firmware version + run: printf 'FIRMWARE_VERSION=%s\n' "${GITHUB_SHA::7}" >> "$GITHUB_ENV" + - name: Prepare build dependency cache id: apt-cache run: | @@ -79,7 +82,8 @@ jobs: run: | cmake -S . -B build/standard -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ - -DPICO_SDK_PATH="$PICO_SDK_PATH" + -DPICO_SDK_PATH="$PICO_SDK_PATH" \ + -DVERSION="$FIRMWARE_VERSION" cmake --build build/standard --target ds5-bridge mkdir -p artifacts cp build/standard/ds5-bridge.uf2 artifacts/ds5-bridge.uf2 @@ -90,7 +94,8 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \ -DENABLE_SERIAL=ON \ - -DENABLE_VERBOSE=ON + -DENABLE_VERBOSE=ON \ + -DVERSION="$FIRMWARE_VERSION" cmake --build build/debug --target ds5-bridge cp build/debug/ds5-bridge.uf2 artifacts/ds5-bridge-debug.uf2 @@ -99,7 +104,8 @@ jobs: cmake -S . -B build/no-batt-led -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \ - -DENABLE_BATT_LED=OFF + -DENABLE_BATT_LED=OFF \ + -DVERSION="$FIRMWARE_VERSION" cmake --build build/no-batt-led --target ds5-bridge - name: Build Pico W firmware @@ -107,7 +113,8 @@ jobs: cmake -S . -B build/picow -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \ - -DPICO_W_BUILD=ON + -DPICO_W_BUILD=ON \ + -DVERSION="$FIRMWARE_VERSION" cmake --build build/picow --target ds5-bridge cp build/picow/ds5-bridge.uf2 artifacts/ds5-bridge-picow.uf2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90652f8..8eb8133 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,11 @@ jobs: ref: ${{ github.event.release.tag_name }} submodules: recursive + - name: Set firmware version + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: printf 'FIRMWARE_VERSION=%s\n' "$RELEASE_TAG" >> "$GITHUB_ENV" + - name: Prepare build dependency cache id: apt-cache run: | @@ -82,7 +87,8 @@ jobs: run: | cmake -S . -B build/standard -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ - -DPICO_SDK_PATH="$PICO_SDK_PATH" + -DPICO_SDK_PATH="$PICO_SDK_PATH" \ + -DVERSION="$FIRMWARE_VERSION" cmake --build build/standard --target ds5-bridge mkdir -p artifacts cp build/standard/ds5-bridge.uf2 "artifacts/ds5-bridge-${{ github.event.release.tag_name }}.uf2" @@ -93,7 +99,8 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \ -DENABLE_SERIAL=ON \ - -DENABLE_VERBOSE=ON + -DENABLE_VERBOSE=ON \ + -DVERSION="$FIRMWARE_VERSION" cmake --build build/debug --target ds5-bridge cp build/debug/ds5-bridge.uf2 "artifacts/ds5-bridge-debug-${{ github.event.release.tag_name }}.uf2" diff --git a/CMakeLists.txt b/CMakeLists.txt index e660178..fcec7fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,9 +141,10 @@ if(WAKE_DEBUG) WAKE_DEBUG ) endif() +set(VERSION "dev" CACHE STRING "Program version string") pico_set_program_name(ds5-bridge "ds5-bridge") -pico_set_program_version(ds5-bridge "0.5.3") +pico_set_program_version(ds5-bridge "${VERSION}") # Modify the below lines to enable/disable output over UART/USB pico_enable_stdio_uart(ds5-bridge 1) diff --git a/src/bt.cpp b/src/bt.cpp index b4572b7..527b5cb 100644 --- a/src/bt.cpp +++ b/src/bt.cpp @@ -9,6 +9,7 @@ #include #include #include "btstack_event.h" +#include "gap.h" #include "l2cap.h" #include "pico/cyw43_arch.h" #include "utils.h" @@ -37,6 +38,7 @@ static uint16_t hid_control_cid; static uint16_t hid_interrupt_cid; static bt_data_callback_t bt_data_callback = nullptr; static bool check_dse = false; +static int8_t bt_rssi = 0; unordered_map > feature_data; queue_t send_fifo; queue_t priority_send_fifo; @@ -74,6 +76,18 @@ bool bt_disconnect() { return true; } +void bt_get_signal_strength(int8_t *rssi) { + // gap_read_rssi() completes asynchronously, so this function can only + // return the last cached RSSI value. Trigger a refresh afterwards so a + // subsequent call can observe the updated value once the RSSI event arrives. + if (rssi != nullptr) { + *rssi = bt_rssi; + } + if (acl_handle != HCI_CON_HANDLE_INVALID) { + gap_read_rssi(acl_handle); + } +} + void bt_l2cap_init() { l2cap_event_callback_registration.callback = &l2cap_packet_handler; l2cap_add_event_handler(&l2cap_event_callback_registration); @@ -198,7 +212,14 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p case HCI_EVENT_COMMAND_COMPLETE: { const uint8_t status = hci_event_command_complete_get_return_parameters(packet)[0]; const uint16_t opcode = hci_event_command_complete_get_command_opcode(packet); - printf("[HCI] CmdComplete %s(0x%04X) status=0x%02X\n", opcode_to_str(opcode), opcode, status); + if (opcode != HCI_OPCODE_HCI_READ_RSSI) { + printf("[HCI] CmdComplete %s(0x%04X) status=0x%02X\n", opcode_to_str(opcode), opcode, status); + } + if (opcode == HCI_OPCODE_HCI_READ_RSSI) { + if (status != ERROR_CODE_SUCCESS || packet[1] < 7) { + printf("[HCI] RSSI complete failed status=0x%02X param_len=%u\n", status, packet[1]); + } + } break; } @@ -207,6 +228,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p if (status == 0) { const hci_con_handle_t handle = hci_event_connection_complete_get_connection_handle(packet); acl_handle = handle; + bt_rssi = 0; hci_event_connection_complete_get_bd_addr(packet, current_device_addr); printf("[HCI] ACL connected handle=0x%04X\n", handle); printf("[HCI] Request authentication on handle=0x%04X\n", handle); @@ -316,6 +338,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p device_found = false; new_pair = false; acl_handle = HCI_CON_HANDLE_INVALID; + bt_rssi = 0; hid_control_cid = 0; hid_interrupt_cid = 0; feature_data.clear(); @@ -324,6 +347,14 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p gap_inquiry_start(30); break; } + + case GAP_EVENT_RSSI_MEASUREMENT: { + const hci_con_handle_t handle = gap_event_rssi_measurement_get_con_handle(packet); + if (handle == acl_handle) { + bt_rssi = static_cast(gap_event_rssi_measurement_get_rssi(packet)); + } + break; + } } } diff --git a/src/bt.h b/src/bt.h index 6404f9b..8d426c6 100644 --- a/src/bt.h +++ b/src/bt.h @@ -20,6 +20,7 @@ void bt_register_data_callback(bt_data_callback_t callback); void bt_send_packet(uint8_t *data, uint16_t len); void bt_send_control(uint8_t *data, uint16_t len); void bt_write(const uint8_t *data, uint16_t len, bool priority = false); +void bt_get_signal_strength(int8_t *rssi); std::vector get_feature_data(uint8_t reportId,uint16_t len); void init_feature(); void set_feature_data(uint8_t reportId, uint8_t* data,uint16_t len); diff --git a/src/cmd.cpp b/src/cmd.cpp index caea2da..8a2add8 100644 --- a/src/cmd.cpp +++ b/src/cmd.cpp @@ -8,13 +8,16 @@ #include #include +#include "bt.h" #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 + report_id == 0xf7 || + report_id == 0xf8 || + report_id == 0xf9 ) { return true; } @@ -31,6 +34,25 @@ uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) { memcpy(buffer,&get_config(),len); return len; } + if (report_id == 0xf8) { + printf("[HID] Receive 0xf8 getting firmware version\n"); + const auto len = std::min(strlen(PICO_PROGRAM_VERSION_STRING), static_cast(reqlen)); + memcpy(buffer, PICO_PROGRAM_VERSION_STRING, len); + return len; + } + if (report_id == 0xf9) { + // [-128,0] + int8_t rssi = 0; + bt_get_signal_strength(&rssi); + if (reqlen == 0) { + return 0; + } + buffer[0] = rssi; +#if ENABLE_VERBOSE + printf("[HID] 0xf9 RSSI=%d raw=0x%02X\n", rssi, buffer[0]); +#endif + return 1; + } return 0; } diff --git a/src/config.cpp b/src/config.cpp index 3c1b46b..3bcc72b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -79,6 +79,10 @@ void config_valid() { body->controller_mode = 2; printf("[Config] controller_mode is invalid\n"); } + if (body->config_version != CONFIG_VERSION) { + body->config_version = CONFIG_VERSION; + printf("[Config] Warning: Config may breaking change\n"); + } } void config_load() { diff --git a/src/config.h b/src/config.h index 69c8a6b..92a015a 100644 --- a/src/config.h +++ b/src/config.h @@ -8,6 +8,7 @@ #include struct __attribute__((packed)) Config_body { + uint8_t config_version; // Config Version float haptics_gain; // [1.0,2.0] float speaker_volume; // [-100,0] uint8_t inactive_time; // [10,60] min diff --git a/src/usb_descriptors.cpp b/src/usb_descriptors.cpp index 2d32ff4..0194ef2 100644 --- a/src/usb_descriptors.cpp +++ b/src/usb_descriptors.cpp @@ -362,8 +362,8 @@ uint8_t descriptor_configuration[] = { 0x00, // bCountryCode: Not localized 0x01, // bNumDescriptors: 1 report descriptor 0x22, // bDescriptorType: Report - 0x31, 0x01, // wDescriptorLength: 305 (0x0131) DS - // 0xA5, 0x01, // wDescriptorLength: 421 (0x01A5) DSE + 0x41, 0x01, // wDescriptorLength: 321 (0x0141) DS + // 0xB5, 0x01, // wDescriptorLength: 437 (0x01B5) DSE // Endpoint Descriptor (HID IN: EP4) 0x07, // bLength @@ -408,9 +408,9 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { descriptor_configuration[offset - 1] = bInterval; descriptor_configuration[offset - 8] = bInterval; if (ds_mode()) { - descriptor_configuration[offset - 16] = 0x31; + descriptor_configuration[offset - 16] = 0x41; }else { - descriptor_configuration[offset - 16] = 0xA5; + descriptor_configuration[offset - 16] = 0xB5; } return descriptor_configuration; } @@ -561,17 +561,26 @@ 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, + 0x85, 0xF6, // Report ID (-10) + 0x09, 0x37, // Usage (Vendor 0x37) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF7, // Report ID (-9) + 0x09, 0x38, // Usage (Vendor 0x38) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF8, // Report ID (-8) + 0x09, 0x39, // Usage (Vendor 0x39) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF9, // Report ID (-7) + 0x09, 0x3A, // Usage (Vendor 0x3A) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0, // End Collection - // 305 bytes + // 321 bytes }; +static_assert(sizeof(desc_hid_report_ds) == 0x0141); uint8_t const desc_hid_report_dse[] = { 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) @@ -773,17 +782,26 @@ 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, + 0x85, 0xF6, // Report ID (-10) + 0x09, 0x37, // Usage (Vendor 0x37) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF7, // Report ID (-9) + 0x09, 0x38, // Usage (Vendor 0x38) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF8, // Report ID (-8) + 0x09, 0x39, // Usage (Vendor 0x39) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x85, 0xF9, // Report ID (-7) + 0x09, 0x3A, // Usage (Vendor 0x3A) + 0x95, 0x3F, // Report Count (63) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0, // End Collection - // 421 bytes + // 437 bytes }; +static_assert(sizeof(desc_hid_report_dse) == 0x01B5); // Invoked when received GET HID REPORT DESCRIPTOR // Application return pointer to descriptor diff --git a/src/utils.h b/src/utils.h index 29db588..66eff31 100644 --- a/src/utils.h +++ b/src/utils.h @@ -83,6 +83,8 @@ inline const char *opcode_to_str(const uint16_t opcode) { return "HCI_READ_BD_ADDR"; case HCI_OPCODE_HCI_READ_ENCRYPTION_KEY_SIZE: return "HCI_READ_ENCRYPTION_KEY_SIZE"; + case HCI_OPCODE_HCI_READ_RSSI: + return "HCI_READ_RSSI"; case 0xFC01: return "HCI_VENDOR_0xFC01"; default: