From 68dd7e95ab6870f9dee269e9c89a2e5174fd2d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= <2712495353@qq.com> Date: Thu, 2 Jul 2026 00:20:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20BLE=20HID=20?= =?UTF-8?q?=E9=94=AE=E7=9B=98=E6=94=AF=E6=8C=81=E4=B8=8E=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 sdkconfig.defaults 中更新 BLE 配置以支持 HID 键盘功能 - 新增 ble_hid.c 和 ble_hid.h 文件,实现 BLE HID 键盘的核心功能 - 更新 CMakeLists.txt,包含新的服务文件以支持 BLE HID - 修改 ui_handlers.c,响应启动按钮事件以发送 HID 按键 - 更新 ble_manager.c,初始化 BLE HID 功能并处理连接状态 - 新增 esp_hid_gap.c 和 esp_hid_gap.h 文件,管理 BLE HID 的 GAP 相关功能 --- main/CMakeLists.txt | 3 + main/app/ui_handlers.c | 19 +- main/services/ble_hid.c | 255 ++++++++ main/services/ble_hid.h | 25 + main/services/ble_manager.c | 227 +------ main/services/esp_hid_gap.c | 1227 +++++++++++++++++++++++++++++++++++ main/services/esp_hid_gap.h | 86 +++ sdkconfig.defaults | 12 +- 8 files changed, 1645 insertions(+), 209 deletions(-) create mode 100644 main/services/ble_hid.c create mode 100644 main/services/ble_hid.h create mode 100644 main/services/esp_hid_gap.c create mode 100644 main/services/esp_hid_gap.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 4418178..4de8a70 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -7,6 +7,8 @@ idf_component_register(SRCS "bsp/boot_button.c" "services/wifi_manager.c" "services/ble_manager.c" + "services/ble_hid.c" + "services/esp_hid_gap.c" "ui/ui.c" "ui/ui_helpers.c" "ui/screens/ui_Screen1.c" @@ -14,6 +16,7 @@ idf_component_register(SRCS "ui/widgets/ui_comp_hook.c" PRIV_REQUIRES bt + esp_hid nvs_flash esp_driver_ledc esp_driver_spi diff --git a/main/app/ui_handlers.c b/main/app/ui_handlers.c index 84fd8e0..02f5597 100644 --- a/main/app/ui_handlers.c +++ b/main/app/ui_handlers.c @@ -2,28 +2,25 @@ #include +#include "ble_hid.h" #include "boot_button.h" -#include "lvgl_port.h" -#include "ui.h" +#include "esp_log.h" #include "ui_events.h" -static bool s_showing_screen1; +static const char *TAG = "ui_handlers"; -static void ui_toggle_screen(void) +static void on_boot_press(void) { - lvgl_port_lock(); - if (s_showing_screen1) { - _ui_screen_change(&ui_Screen2, LV_SCR_LOAD_ANIM_FADE_ON, 200, 0, ui_Screen2_screen_init); + if (ble_hid_ready()) { + ble_hid_send_combo(0, HID_KEY_C); } else { - _ui_screen_change(&ui_Screen1, LV_SCR_LOAD_ANIM_FADE_ON, 200, 0, ui_Screen1_screen_init); + ESP_LOGW(TAG, "HID not ready"); } - s_showing_screen1 = !s_showing_screen1; - lvgl_port_unlock(); } void ui_handlers_init(void) { - boot_button_init(ui_toggle_screen); + boot_button_init(on_boot_press); } void update_year_action(lv_event_t * e) diff --git a/main/services/ble_hid.c b/main/services/ble_hid.c new file mode 100644 index 0000000..866aa6b --- /dev/null +++ b/main/services/ble_hid.c @@ -0,0 +1,255 @@ +#include "ble_hid.h" + +#include "board_config.h" + +#if BOARD_BLE_ENABLED + +#include "esp_hidd.h" +#include "esp_hid_common.h" +#include "esp_hid_gap.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include +#include + +static const char *TAG = "ble_hid"; + +#define HID_KEYBOARD_REPORT_ID 1 +#define HID_KEYBOARD_REPORT_LEN 7 + +static const unsigned char s_keyboard_report_map[] = { + 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x85, 0x01, 0x05, 0x07, 0x19, 0xE0, + 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, + 0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, + 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, + 0x95, 0x05, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00, + 0x29, 0x65, 0x81, 0x00, 0xC0, +}; + +static esp_hid_raw_report_map_t s_report_maps[] = { + { + .data = s_keyboard_report_map, + .len = sizeof(s_keyboard_report_map), + }, +}; + +static esp_hid_device_config_t s_hid_config = { + .vendor_id = 0x16C0, + .product_id = 0x05DF, + .version = 0x0100, + .device_name = CONFIG_ESP_BLE_DEVICE_NAME, + .manufacturer_name = "HeavenlyHeroStar", + .serial_number = "1234567890", + .report_maps = s_report_maps, + .report_maps_len = 1, +}; + +static esp_hidd_dev_t *s_hid_dev; +static ble_hid_status_cb_t s_status_cb; +static bool s_connected; + +static void ble_hid_log_buffer(const char *label, const uint8_t *data, size_t len) +{ + char hex[32]; + size_t pos = 0; + + for (size_t i = 0; i < len && pos + 3 < sizeof(hex); i++) { + pos += (size_t)snprintf(hex + pos, sizeof(hex) - pos, "%02x ", data[i]); + } + + ESP_LOGI(TAG, "%s len=%u: %s", label, (unsigned)len, hex); +} + +static void ble_hid_send_idle_report(void) +{ + uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0}; + + if (!ble_hid_ready()) { + return; + } + + ble_hid_log_buffer("idle", buffer, sizeof(buffer)); + (void)esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer, + sizeof(buffer)); +} + +static void ble_hid_idle_task(void *arg) +{ + (void)arg; + /* Windows often enables notify a few seconds after connect. */ + vTaskDelay(pdMS_TO_TICKS(3500)); + ble_hid_send_idle_report(); + vTaskDelete(NULL); +} + +static void ble_hid_hidd_event(void *handler_args, esp_event_base_t base, int32_t id, + void *event_data) +{ + esp_hidd_event_t event = (esp_hidd_event_t)id; + esp_hidd_event_data_t *param = (esp_hidd_event_data_t *)event_data; + + (void)handler_args; + (void)base; + + switch (event) { + case ESP_HIDD_START_EVENT: + ESP_LOGI(TAG, "HID stack started"); + esp_hid_ble_gap_adv_start(); + break; + + case ESP_HIDD_CONNECT_EVENT: + if (param->connect.status == 0) { + s_connected = true; + ESP_LOGI(TAG, "HID connected"); + if (s_status_cb != NULL) { + s_status_cb(true); + } + xTaskCreate(ble_hid_idle_task, "ble_hid_idle", 2048, NULL, 5, NULL); + } else { + ESP_LOGW(TAG, "HID connect failed status=%d", param->connect.status); + } + break; + + case ESP_HIDD_DISCONNECT_EVENT: + s_connected = false; + ESP_LOGI(TAG, "HID disconnected reason=%d", param->disconnect.reason); + if (s_status_cb != NULL) { + s_status_cb(false); + } + esp_hid_ble_gap_adv_start(); + break; + + case ESP_HIDD_PROTOCOL_MODE_EVENT: + ESP_LOGI(TAG, "protocol mode map=%u mode=%s", + param->protocol_mode.map_index, + param->protocol_mode.protocol_mode ? "REPORT" : "BOOT"); + break; + + case ESP_HIDD_CONTROL_EVENT: + ESP_LOGI(TAG, "control map=%u %sSUSPEND", + param->control.map_index, + param->control.control ? "EXIT_" : ""); + break; + + case ESP_HIDD_OUTPUT_EVENT: + ESP_LOGD(TAG, "output map=%u id=%u len=%d", + param->output.map_index, param->output.report_id, param->output.length); + break; + + default: + break; + } +} + +int ble_hid_init(ble_hid_status_cb_t status_cb) +{ + esp_err_t ret; + + s_status_cb = status_cb; + s_connected = false; + + ret = esp_hidd_dev_init(&s_hid_config, ESP_HID_TRANSPORT_BLE, ble_hid_hidd_event, + &s_hid_dev); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_hidd_dev_init failed: %d", ret); + return ret; + } + + ESP_LOGI(TAG, "esp_hidd keyboard ready (report payload=%u bytes)", + (unsigned)HID_KEYBOARD_REPORT_LEN); + return ESP_OK; +} + +void ble_hid_on_input_notify(bool enabled) +{ + if (enabled) { + ble_hid_send_idle_report(); + } +} + +bool ble_hid_ready(void) +{ + return s_connected && s_hid_dev != NULL; +} + +int ble_hid_key_press(uint8_t modifier, uint8_t keycode) +{ + uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0}; + esp_err_t ret; + + if (!ble_hid_ready()) { + return ESP_ERR_INVALID_STATE; + } + + buffer[0] = modifier; + buffer[2] = keycode; + ble_hid_log_buffer("press", buffer, sizeof(buffer)); + + ret = esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer, + sizeof(buffer)); + return (ret == ESP_OK) ? 0 : ret; +} + +int ble_hid_key_release(void) +{ + uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0}; + esp_err_t ret; + + if (!ble_hid_ready()) { + return ESP_ERR_INVALID_STATE; + } + + ble_hid_log_buffer("release", buffer, sizeof(buffer)); + ret = esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer, + sizeof(buffer)); + return (ret == ESP_OK) ? 0 : ret; +} + +int ble_hid_send_combo(uint8_t modifier, uint8_t keycode) +{ + int rc; + + rc = ble_hid_key_press(modifier, keycode); + if (rc != 0) { + return rc; + } + + vTaskDelay(pdMS_TO_TICKS(50)); + return ble_hid_key_release(); +} + +#else + +int ble_hid_init(ble_hid_status_cb_t status_cb) +{ + (void)status_cb; + return 0; +} + +bool ble_hid_ready(void) +{ + return false; +} + +int ble_hid_key_press(uint8_t modifier, uint8_t keycode) +{ + (void)modifier; + (void)keycode; + return -1; +} + +int ble_hid_key_release(void) +{ + return -1; +} + +int ble_hid_send_combo(uint8_t modifier, uint8_t keycode) +{ + (void)modifier; + (void)keycode; + return -1; +} + +#endif /* BOARD_BLE_ENABLED */ diff --git a/main/services/ble_hid.h b/main/services/ble_hid.h new file mode 100644 index 0000000..fe61819 --- /dev/null +++ b/main/services/ble_hid.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#define HID_MOD_LCTRL 0x01 +#define HID_MOD_LSHIFT 0x02 +#define HID_MOD_LALT 0x04 +#define HID_MOD_LGUI 0x08 + +#define HID_KEY_C 0x06 +#define HID_KEY_F5 0x3E + +typedef void (*ble_hid_status_cb_t)(bool connected); + +int ble_hid_init(ble_hid_status_cb_t status_cb); +bool ble_hid_ready(void); + +int ble_hid_key_press(uint8_t modifier, uint8_t keycode); +int ble_hid_key_release(void); +int ble_hid_send_combo(uint8_t modifier, uint8_t keycode); + +#if defined(BOARD_BLE_ENABLED) && BOARD_BLE_ENABLED +void ble_hid_on_input_notify(bool enabled); +#endif diff --git a/main/services/ble_manager.c b/main/services/ble_manager.c index c3a474c..b8d1fde 100644 --- a/main/services/ble_manager.c +++ b/main/services/ble_manager.c @@ -1,96 +1,28 @@ #include "ble_manager.h" #include "board_config.h" +#include "ble_hid.h" #include "esp_log.h" #include "sdkconfig.h" #if BOARD_BLE_ENABLED #include "driver/gpio.h" +#include "esp_hid_gap.h" +#include "esp_hid_common.h" #include "host/ble_hs.h" -#include "host/ble_uuid.h" -#include "host/util/util.h" #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" -#include "services/gap/ble_svc_gap.h" -#include "services/gatt/ble_svc_gatt.h" #include "store/config/ble_store_config.h" -#include - -#define BLE_DEVICE_NAME CONFIG_ESP_BLE_DEVICE_NAME - -#define HHS_SVC_UUID16 0xFFF0 -#define HHS_CHR_UUID16 0xFFF1 - static const char *TAG = "ble_manager"; +#define BLE_DEVICE_NAME CONFIG_ESP_BLE_DEVICE_NAME + static ble_status_cb_t s_status_cb; static bool s_connected; -static uint8_t s_own_addr_type; -static uint16_t s_conn_handle = BLE_HS_CONN_HANDLE_NONE; -static uint8_t s_chr_val[20] = "HeavenlyHeroStar"; -static uint16_t s_chr_val_handle; -static const ble_uuid16_t s_hhs_svc_uuid = BLE_UUID16_INIT(HHS_SVC_UUID16); -static const ble_uuid16_t s_hhs_chr_uuid = BLE_UUID16_INIT(HHS_CHR_UUID16); - -static void start_advertising(void); -static int gap_event_handler(struct ble_gap_event *event, void *arg); - -static int chr_access(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - (void)arg; - - if (attr_handle != s_chr_val_handle) { - return BLE_ATT_ERR_UNLIKELY; - } - - switch (ctxt->op) { - case BLE_GATT_ACCESS_OP_READ_CHR: - return os_mbuf_append(ctxt->om, s_chr_val, sizeof(s_chr_val)) == 0 - ? 0 - : BLE_ATT_ERR_INSUFFICIENT_RES; - - case BLE_GATT_ACCESS_OP_WRITE_CHR: { - uint16_t len = OS_MBUF_PKTLEN(ctxt->om); - if (len > sizeof(s_chr_val)) { - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - memset(s_chr_val, 0, sizeof(s_chr_val)); - int rc = ble_hs_mbuf_to_flat(ctxt->om, s_chr_val, sizeof(s_chr_val), NULL); - if (rc != 0) { - return BLE_ATT_ERR_UNLIKELY; - } - ESP_LOGI(TAG, "characteristic write from conn=%d, len=%u", conn_handle, len); - return 0; - } - - default: - return BLE_ATT_ERR_UNLIKELY; - } -} - -static const struct ble_gatt_svc_def s_gatt_svcs[] = { - { - .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = &s_hhs_svc_uuid.u, - .characteristics = - (struct ble_gatt_chr_def[]){ - { - .uuid = &s_hhs_chr_uuid.u, - .access_cb = chr_access, - .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, - .val_handle = &s_chr_val_handle, - }, - {0}, - }, - }, - {0}, -}; - -static void notify_status(bool connected) +static void on_hid_status(bool connected) { s_connected = connected; gpio_set_level(BOARD_PIN_LED, connected ? 1 : 0); @@ -99,119 +31,15 @@ static void notify_status(bool connected) } } -static void start_advertising(void) -{ - struct ble_hs_adv_fields fields = {0}; - struct ble_gap_adv_params adv_params = {0}; - const char *name = ble_svc_gap_device_name(); - int rc; - - fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; - fields.name = (uint8_t *)name; - fields.name_len = strlen(name); - fields.name_is_complete = 1; - - rc = ble_gap_adv_set_fields(&fields); - if (rc != 0) { - ESP_LOGE(TAG, "failed to set advertising data: %d", rc); - return; - } - - adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; - adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; - - rc = ble_gap_adv_start(s_own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, - gap_event_handler, NULL); - if (rc != 0) { - ESP_LOGE(TAG, "failed to start advertising: %d", rc); - return; - } - - ESP_LOGI(TAG, "advertising as \"%s\"", name); -} - -static int gap_event_handler(struct ble_gap_event *event, void *arg) -{ - (void)arg; - - switch (event->type) { - case BLE_GAP_EVENT_CONNECT: - if (event->connect.status == 0) { - s_conn_handle = event->connect.conn_handle; - ESP_LOGI(TAG, "connected, handle=%d", s_conn_handle); - notify_status(true); - } else { - ESP_LOGW(TAG, "connection failed, status=%d", event->connect.status); - start_advertising(); - } - return 0; - - case BLE_GAP_EVENT_DISCONNECT: - ESP_LOGI(TAG, "disconnected, reason=%d", event->disconnect.reason); - s_conn_handle = BLE_HS_CONN_HANDLE_NONE; - notify_status(false); - start_advertising(); - return 0; - - case BLE_GAP_EVENT_ADV_COMPLETE: - start_advertising(); - return 0; - - default: - return 0; - } -} - -static void on_stack_sync(void) -{ - int rc = ble_hs_util_ensure_addr(0); - if (rc != 0) { - ESP_LOGE(TAG, "no available BT address"); - return; - } - - rc = ble_hs_id_infer_auto(0, &s_own_addr_type); - if (rc != 0) { - ESP_LOGE(TAG, "failed to infer address type: %d", rc); - return; - } - - start_advertising(); -} - -static void on_stack_reset(int reason) -{ - ESP_LOGW(TAG, "nimble stack reset, reason=%d", reason); -} - -static void gatt_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) -{ - (void)ctxt; - (void)arg; -} - -static int gatt_svc_init(void) -{ - int rc; - - ble_svc_gatt_init(); - rc = ble_gatts_count_cfg(s_gatt_svcs); - if (rc != 0) { - return rc; - } - - return ble_gatts_add_svcs(s_gatt_svcs); -} - -void ble_store_config_init(void); - static void nimble_host_task(void *param) { (void)param; nimble_port_run(); - vTaskDelete(NULL); + nimble_port_freertos_deinit(); } +void ble_store_config_init(void); + #endif /* BOARD_BLE_ENABLED */ void ble_manager_set_status_cb(ble_status_cb_t cb) @@ -235,36 +63,41 @@ bool ble_manager_is_connected(void) void ble_manager_init(void) { #if BOARD_BLE_ENABLED - int rc; + esp_err_t ret; gpio_reset_pin(BOARD_PIN_LED); gpio_set_direction(BOARD_PIN_LED, GPIO_MODE_OUTPUT); gpio_set_level(BOARD_PIN_LED, 0); - ESP_ERROR_CHECK(nimble_port_init()); - - ble_svc_gap_init(); - rc = ble_svc_gap_device_name_set(BLE_DEVICE_NAME); - if (rc != 0) { - ESP_LOGE(TAG, "failed to set device name"); + ESP_LOGI(TAG, "init esp_hid_gap (NimBLE)"); + ret = esp_hid_gap_init(HIDD_BLE_MODE); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_hid_gap_init failed: %d", ret); return; } - rc = gatt_svc_init(); - if (rc != 0) { - ESP_LOGE(TAG, "failed to init GATT service: %d", rc); + ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_KEYBOARD, BLE_DEVICE_NAME); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_hid_ble_gap_adv_init failed: %d", ret); + return; + } + + ret = ble_hid_init(on_hid_status); + if (ret != 0) { + ESP_LOGE(TAG, "ble_hid_init failed: %d", ret); return; } - ble_hs_cfg.reset_cb = on_stack_reset; - ble_hs_cfg.sync_cb = on_stack_sync; - ble_hs_cfg.gatts_register_cb = gatt_register_cb; - ble_hs_cfg.store_status_cb = ble_store_util_status_rr; ble_store_config_init(); + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; - nimble_port_freertos_init(nimble_host_task); + ret = esp_nimble_enable(nimble_host_task); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_nimble_enable failed: %d", ret); + return; + } - ESP_LOGI(TAG, "BLE peripheral started"); + ESP_LOGI(TAG, "BLE HID keyboard started as \"%s\"", BLE_DEVICE_NAME); #else ESP_LOGI("ble_manager", "BLE disabled by board config"); #endif diff --git a/main/services/esp_hid_gap.c b/main/services/esp_hid_gap.c new file mode 100644 index 0000000..506eb06 --- /dev/null +++ b/main/services/esp_hid_gap.c @@ -0,0 +1,1227 @@ +/* + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "esp_hid_gap.h" + +#if CONFIG_BT_NIMBLE_ENABLED +#include "board_config.h" +#if BOARD_BLE_ENABLED +#include "ble_hid.h" +#endif +#include "host/ble_hs.h" +#include "nimble/nimble_port.h" +#include "host/ble_gap.h" +#include "host/ble_hs_adv.h" +#include "nimble/ble.h" +#include "host/ble_sm.h" +#else +#include "esp_bt_device.h" +#endif + +static const char *TAG = "ESP_HID_GAP"; + +// uncomment to print all devices that were seen during a scan +#define GAP_DBG_PRINTF(...) //printf(__VA_ARGS__) +//static const char * gap_bt_prop_type_names[5] = {"","BDNAME","COD","RSSI","EIR"}; + +#if !CONFIG_BT_NIMBLE_ENABLED +static esp_hid_scan_result_t *bt_scan_results = NULL; +static size_t num_bt_scan_results = 0; + +static esp_hid_scan_result_t *ble_scan_results = NULL; +static size_t num_ble_scan_results = 0; +#endif + +static SemaphoreHandle_t bt_hidh_cb_semaphore = NULL; +#define WAIT_BT_CB() xSemaphoreTake(bt_hidh_cb_semaphore, portMAX_DELAY) +#define SEND_BT_CB() xSemaphoreGive(bt_hidh_cb_semaphore) + +static SemaphoreHandle_t ble_hidh_cb_semaphore = NULL; +#define WAIT_BLE_CB() xSemaphoreTake(ble_hidh_cb_semaphore, portMAX_DELAY) +#define SEND_BLE_CB() xSemaphoreGive(ble_hidh_cb_semaphore) + +#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(*a)) + +#if !CONFIG_BT_NIMBLE_ENABLED +static const char *ble_gap_evt_names[] = { "ADV_DATA_SET_COMPLETE", "SCAN_RSP_DATA_SET_COMPLETE", "SCAN_PARAM_SET_COMPLETE", "SCAN_RESULT", "ADV_DATA_RAW_SET_COMPLETE", "SCAN_RSP_DATA_RAW_SET_COMPLETE", "ADV_START_COMPLETE", "SCAN_START_COMPLETE", "AUTH_CMPL", "KEY", "SEC_REQ", "PASSKEY_NOTIF", "PASSKEY_REQ", "OOB_REQ", "LOCAL_IR", "LOCAL_ER", "NC_REQ", "ADV_STOP_COMPLETE", "SCAN_STOP_COMPLETE", "SET_STATIC_RAND_ADDR", "UPDATE_CONN_PARAMS", "SET_PKT_LENGTH_COMPLETE", "SET_LOCAL_PRIVACY_COMPLETE", "REMOVE_BOND_DEV_COMPLETE", "CLEAR_BOND_DEV_COMPLETE", "GET_BOND_DEV_COMPLETE", "READ_RSSI_COMPLETE", "UPDATE_WHITELIST_COMPLETE"}; +static const char *bt_gap_evt_names[] = { "DISC_RES", "DISC_STATE_CHANGED", "RMT_SRVCS", "RMT_SRVC_REC", "AUTH_CMPL", "PIN_REQ", "CFM_REQ", "KEY_NOTIF", "KEY_REQ", "READ_RSSI_DELTA"}; +static const char *ble_addr_type_names[] = {"PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM"}; + +const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type) +{ + if (ble_addr_type > BLE_ADDR_TYPE_RPA_RANDOM) { + return "UNKNOWN"; + } + return ble_addr_type_names[ble_addr_type]; +} + +const char *ble_gap_evt_str(uint8_t event) +{ + if (event >= SIZEOF_ARRAY(ble_gap_evt_names)) { + return "UNKNOWN"; + } + return ble_gap_evt_names[event]; +} + +const char *bt_gap_evt_str(uint8_t event) +{ + if (event >= SIZEOF_ARRAY(bt_gap_evt_names)) { + return "UNKNOWN"; + } + return bt_gap_evt_names[event]; +} +#endif + +#if CONFIG_BT_BLE_ENABLED +const char *esp_ble_key_type_str(esp_ble_key_type_t key_type) +{ + const char *key_str = NULL; + switch (key_type) { + case ESP_LE_KEY_NONE: + key_str = "ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = "ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = "ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = "ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = "ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = "ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = "ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = "ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = "ESP_LE_KEY_LCSRK"; + break; + default: + key_str = "INVALID BLE KEY TYPE"; + break; + + } + return key_str; +} +#endif /* CONFIG_BT_BLE_ENABLED */ + +#if !CONFIG_BT_NIMBLE_ENABLED +void esp_hid_scan_results_free(esp_hid_scan_result_t *results) +{ + esp_hid_scan_result_t *r = NULL; + while (results) { + r = results; + results = results->next; + if (r->name != NULL) { + free((char *)r->name); + } + free(r); + } +} +#endif + +#if (CONFIG_BT_HID_DEVICE_ENABLED || CONFIG_BT_BLE_ENABLED) +static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results) +{ + esp_hid_scan_result_t *r = results; + while (r) { + if (memcmp(bda, r->bda, sizeof(esp_bd_addr_t)) == 0) { + return r; + } + r = r->next; + } + return NULL; +} +#endif /* (CONFIG_BT_HID_DEVICE_ENABLED || CONFIG_BT_BLE_ENABLED) */ + +#if CONFIG_BT_HID_DEVICE_ENABLED +static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi) +{ + esp_hid_scan_result_t *r = find_scan_result(bda, bt_scan_results); + if (r) { + //Some info may come later + if (r->name == NULL && name && name_len) { + char *name_s = (char *)malloc(name_len + 1); + if (name_s == NULL) { + ESP_LOGE(TAG, "Malloc result name failed!"); + return; + } + memcpy(name_s, name, name_len); + name_s[name_len] = 0; + r->name = (const char *)name_s; + } + if (r->bt.uuid.len == 0 && uuid->len) { + memcpy(&r->bt.uuid, uuid, sizeof(esp_bt_uuid_t)); + } + if (rssi != 0) { + r->rssi = rssi; + } + return; + } + + r = (esp_hid_scan_result_t *)malloc(sizeof(esp_hid_scan_result_t)); + if (r == NULL) { + ESP_LOGE(TAG, "Malloc bt_hidh_scan_result_t failed!"); + return; + } + r->transport = ESP_HID_TRANSPORT_BT; + memcpy(r->bda, bda, sizeof(esp_bd_addr_t)); + memcpy(&r->bt.cod, cod, sizeof(esp_bt_cod_t)); + memcpy(&r->bt.uuid, uuid, sizeof(esp_bt_uuid_t)); + r->usage = esp_hid_usage_from_cod((uint32_t)cod); + r->rssi = rssi; + r->name = NULL; + if (name_len && name) { + char *name_s = (char *)malloc(name_len + 1); + if (name_s == NULL) { + free(r); + ESP_LOGE(TAG, "Malloc result name failed!"); + return; + } + memcpy(name_s, name, name_len); + name_s[name_len] = 0; + r->name = (const char *)name_s; + } + r->next = bt_scan_results; + bt_scan_results = r; + num_bt_scan_results++; +} +#endif + +#if CONFIG_BT_BLE_ENABLED +static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi) +{ + if (find_scan_result(bda, ble_scan_results)) { + ESP_LOGW(TAG, "Result already exists!"); + return; + } + esp_hid_scan_result_t *r = (esp_hid_scan_result_t *)malloc(sizeof(esp_hid_scan_result_t)); + if (r == NULL) { + ESP_LOGE(TAG, "Malloc ble_hidh_scan_result_t failed!"); + return; + } + r->transport = ESP_HID_TRANSPORT_BLE; + memcpy(r->bda, bda, sizeof(esp_bd_addr_t)); + r->ble.appearance = appearance; + r->ble.addr_type = addr_type; + r->usage = esp_hid_usage_from_appearance(appearance); + r->rssi = rssi; + r->name = NULL; + if (name_len && name) { + char *name_s = (char *)malloc(name_len + 1); + if (name_s == NULL) { + free(r); + ESP_LOGE(TAG, "Malloc result name failed!"); + return; + } + memcpy(name_s, name, name_len); + name_s[name_len] = 0; + r->name = (const char *)name_s; + } + r->next = ble_scan_results; + ble_scan_results = r; + num_ble_scan_results++; +} +#endif /* CONFIG_BT_BLE_ENABLED */ + +#if !CONFIG_BT_NIMBLE_ENABLED +void print_uuid(esp_bt_uuid_t *uuid) +{ + if (uuid->len == ESP_UUID_LEN_16) { + GAP_DBG_PRINTF("UUID16: 0x%04x", uuid->uuid.uuid16); + } else if (uuid->len == ESP_UUID_LEN_32) { + GAP_DBG_PRINTF("UUID32: 0x%08x", uuid->uuid.uuid32); + } else if (uuid->len == ESP_UUID_LEN_128) { + GAP_DBG_PRINTF("UUID128: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", uuid->uuid.uuid128[0], + uuid->uuid.uuid128[1], uuid->uuid.uuid128[2], uuid->uuid.uuid128[3], + uuid->uuid.uuid128[4], uuid->uuid.uuid128[5], uuid->uuid.uuid128[6], + uuid->uuid.uuid128[7], uuid->uuid.uuid128[8], uuid->uuid.uuid128[9], + uuid->uuid.uuid128[10], uuid->uuid.uuid128[11], uuid->uuid.uuid128[12], + uuid->uuid.uuid128[13], uuid->uuid.uuid128[14], uuid->uuid.uuid128[15]); + } +} +#endif + +#if CONFIG_BT_HID_DEVICE_ENABLED +static void handle_bt_device_result(struct disc_res_param *disc_res) +{ + GAP_DBG_PRINTF("BT : " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(disc_res->bda)); + uint32_t codv = 0; + esp_bt_cod_t *cod = (esp_bt_cod_t *)&codv; + int8_t rssi = 0; + uint8_t *name = NULL; + uint8_t name_len = 0; + esp_bt_uuid_t uuid; + + uuid.len = ESP_UUID_LEN_16; + uuid.uuid.uuid16 = 0; + + for (int i = 0; i < disc_res->num_prop; i++) { + esp_bt_gap_dev_prop_t *prop = &disc_res->prop[i]; + if (prop->type != ESP_BT_GAP_DEV_PROP_EIR) { + GAP_DBG_PRINTF(", %s: ", gap_bt_prop_type_names[prop->type]); + } + if (prop->type == ESP_BT_GAP_DEV_PROP_BDNAME) { + if (prop->val == NULL) { + continue; + } + name = (uint8_t *)prop->val; + name_len = strlen((const char *)name); + GAP_DBG_PRINTF("%s", (const char *)name); + } else if (prop->type == ESP_BT_GAP_DEV_PROP_RSSI) { + rssi = *((int8_t *)prop->val); + GAP_DBG_PRINTF("%d", rssi); + } else if (prop->type == ESP_BT_GAP_DEV_PROP_COD) { + memcpy(&codv, prop->val, sizeof(uint32_t)); + GAP_DBG_PRINTF("major: %s, minor: %d, service: 0x%03x", esp_hid_cod_major_str(cod->major), cod->minor, cod->service); + } else if (prop->type == ESP_BT_GAP_DEV_PROP_EIR) { + uint8_t len = 0; + uint8_t *data = 0; + + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_16BITS_UUID, &len); + if (data == NULL) { + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_16BITS_UUID, &len); + } + if (data && len == ESP_UUID_LEN_16) { + uuid.len = ESP_UUID_LEN_16; + uuid.uuid.uuid16 = data[0] + (data[1] << 8); + GAP_DBG_PRINTF(", "); print_uuid(&uuid); + continue; + } + + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_32BITS_UUID, &len); + if (data == NULL) { + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_32BITS_UUID, &len); + } + if (data && len == ESP_UUID_LEN_32) { + uuid.len = len; + memcpy(&uuid.uuid.uuid32, data, sizeof(uint32_t)); + GAP_DBG_PRINTF(", "); print_uuid(&uuid); + continue; + } + + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_128BITS_UUID, &len); + if (data == NULL) { + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_128BITS_UUID, &len); + } + if (data && len == ESP_UUID_LEN_128) { + uuid.len = len; + memcpy(uuid.uuid.uuid128, (uint8_t *)data, len); + GAP_DBG_PRINTF(", "); print_uuid(&uuid); + continue; + } + + //try to find a name + if (name == NULL) { + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &len); + if (data == NULL) { + data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &len); + } + if (data && len) { + name = data; + name_len = len; + GAP_DBG_PRINTF(", NAME: "); + for (int x = 0; x < len; x++) { + GAP_DBG_PRINTF("%c", (char)data[x]); + } + } + } + } + } + GAP_DBG_PRINTF("\n"); + + if (cod->major == ESP_BT_COD_MAJOR_DEV_PERIPHERAL || (find_scan_result(disc_res->bda, bt_scan_results) != NULL)) { + add_bt_scan_result(disc_res->bda, cod, &uuid, name, name_len, rssi); + } +} +#endif + +#if CONFIG_BT_BLE_ENABLED +static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst) +{ + + uint16_t uuid = 0; + uint16_t appearance = 0; + char name[64] = {0}; + + uint8_t uuid_len = 0; + uint8_t *uuid_d = esp_ble_resolve_adv_data_by_type(scan_rst->ble_adv, + scan_rst->adv_data_len + scan_rst->scan_rsp_len, + ESP_BLE_AD_TYPE_16SRV_CMPL, + &uuid_len); + if (uuid_d != NULL && uuid_len) { + uuid = uuid_d[0] + (uuid_d[1] << 8); + } + + uint8_t appearance_len = 0; + uint8_t *appearance_d = esp_ble_resolve_adv_data_by_type(scan_rst->ble_adv, + scan_rst->adv_data_len + scan_rst->scan_rsp_len, + ESP_BLE_AD_TYPE_APPEARANCE, + &appearance_len); + if (appearance_d != NULL && appearance_len) { + appearance = appearance_d[0] + (appearance_d[1] << 8); + } + + uint8_t adv_name_len = 0; + uint8_t *adv_name = esp_ble_resolve_adv_data_by_type(scan_rst->ble_adv, + scan_rst->adv_data_len + scan_rst->scan_rsp_len, + ESP_BLE_AD_TYPE_NAME_CMPL, + &adv_name_len); + + if (adv_name == NULL) { + adv_name = esp_ble_resolve_adv_data_by_type(scan_rst->ble_adv, + scan_rst->adv_data_len + scan_rst->scan_rsp_len, + ESP_BLE_AD_TYPE_NAME_SHORT, + &adv_name_len); + } + + if (adv_name != NULL && adv_name_len) { + memcpy(name, adv_name, adv_name_len); + name[adv_name_len] = 0; + } + + GAP_DBG_PRINTF("BLE: " ESP_BD_ADDR_STR ", ", ESP_BD_ADDR_HEX(scan_rst->bda)); + GAP_DBG_PRINTF("RSSI: %d, ", scan_rst->rssi); + GAP_DBG_PRINTF("UUID: 0x%04x, ", uuid); + GAP_DBG_PRINTF("APPEARANCE: 0x%04x, ", appearance); + GAP_DBG_PRINTF("ADDR_TYPE: '%s'", ble_addr_type_str(scan_rst->ble_addr_type)); + if (adv_name_len) { + GAP_DBG_PRINTF(", NAME: '%s'", name); + } + GAP_DBG_PRINTF("\n"); + + if (uuid == ESP_GATT_UUID_HID_SVC) { + add_ble_scan_result(scan_rst->bda, scan_rst->ble_addr_type, appearance, adv_name, adv_name_len, scan_rst->rssi); + } +} +#endif /* CONFIG_BT_BLE_ENABLED */ + +#if CONFIG_BT_HID_DEVICE_ENABLED +/* + * BT GAP + * */ + +static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + switch (event) { + case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { + ESP_LOGV(TAG, "BT GAP DISC_STATE %s", (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) ? "START" : "STOP"); + if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { + SEND_BT_CB(); + } + break; + } + case ESP_BT_GAP_DISC_RES_EVT: { + handle_bt_device_result(¶m->disc_res); + break; + } + case ESP_BT_GAP_PIN_REQ_EVT: { + ESP_LOGI(TAG, "BT GAP PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); + if (param->pin_req.min_16_digit) { + ESP_LOGI(TAG, "Input pin code: 0000 0000 0000 0000"); + esp_bt_pin_code_t pin_code = {0}; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); + } else { + ESP_LOGI(TAG, "Input pin code: 1234"); + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); + } + break; + } + +#if (CONFIG_EXAMPLE_SSP_ENABLED == true) + case ESP_BT_GAP_CFM_REQ_EVT: + ESP_LOGI(TAG, "BT GAP CFM_REQ_EVT Please compare the numeric value: %06" PRIu32, + param->cfm_req.num_val); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); + break; + case ESP_BT_GAP_KEY_NOTIF_EVT: + ESP_LOGI(TAG, "BT GAP KEY_NOTIF_EVT passkey:%06" PRIu32, param->key_notif.passkey); + break; + case ESP_BT_GAP_KEY_REQ_EVT: + ESP_LOGI(TAG, "BT GAP KEY_REQ_EVT Please enter passkey!"); + break; +#endif + case ESP_BT_GAP_MODE_CHG_EVT: + ESP_LOGI(TAG, "BT GAP MODE_CHG_EVT mode:%d", param->mode_chg.mode); + break; + default: + ESP_LOGV(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event)); + break; + } +} + +static esp_err_t init_bt_gap(void) +{ + esp_err_t ret; +#if (CONFIG_EXAMPLE_SSP_ENABLED) + /* Set default parameters for Secure Simple Pairing */ + esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE; + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); +#endif + /* + * Set default parameters for Legacy Pairing + * Use fixed pin code + */ + esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED; + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_set_pin(pin_type, 4, pin_code); + + if ((ret = esp_bt_gap_register_callback(bt_gap_event_handler)) != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_gap_register_callback failed: %d", ret); + return ret; + } + + // Allow BT devices to connect back to us + if ((ret = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_NON_DISCOVERABLE)) != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_gap_set_scan_mode failed: %d", ret); + return ret; + } + return ret; +} + +static esp_err_t start_bt_scan(uint32_t seconds) +{ + esp_err_t ret = ESP_OK; + if ((ret = esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, (int)(seconds / 1.28), 0)) != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_gap_start_discovery failed: %d", ret); + return ret; + } + return ret; +} +#endif + +#if CONFIG_BT_BLE_ENABLED +/* + * BLE GAP + * */ +extern void ble_hid_task_start_up(void); +static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + switch (event) { + /* + * SCAN + * */ + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { + ESP_LOGV(TAG, "BLE GAP EVENT SCAN_PARAM_SET_COMPLETE"); + SEND_BLE_CB(); + break; + } + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param; + switch (scan_result->scan_rst.search_evt) { + case ESP_GAP_SEARCH_INQ_RES_EVT: { + handle_ble_device_result(&scan_result->scan_rst); + break; + } + case ESP_GAP_SEARCH_INQ_CMPL_EVT: + ESP_LOGV(TAG, "BLE GAP EVENT SCAN DONE: %d", scan_result->scan_rst.num_resps); + SEND_BLE_CB(); + break; + default: + break; + } + break; + } + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: { + ESP_LOGV(TAG, "BLE GAP EVENT SCAN CANCELED"); + break; + } + + /* + * ADVERTISEMENT + * */ + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + ESP_LOGV(TAG, "BLE GAP ADV_DATA_SET_COMPLETE"); + break; + + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + ESP_LOGV(TAG, "BLE GAP ADV_START_COMPLETE"); + break; + + /* + * AUTHENTICATION + * */ + case ESP_GAP_BLE_AUTH_CMPL_EVT: + if (!param->ble_security.auth_cmpl.success) { + // if AUTH ERROR,hid maybe don't work. + ESP_LOGE(TAG, "BLE GAP AUTH ERROR: 0x%x", param->ble_security.auth_cmpl.fail_reason); + } else { + ESP_LOGI(TAG, "BLE GAP AUTH SUCCESS"); + } + ble_hid_task_start_up(); + break; + + case ESP_GAP_BLE_KEY_EVT: //shows the ble key info share with peer device to the user. + ESP_LOGI(TAG, "BLE GAP KEY type = %s", esp_ble_key_type_str(param->ble_security.ble_key.key_type)); + break; + + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // ESP_IO_CAP_OUT + // The app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + // Show the passkey number to the user to input it in the peer device. + ESP_LOGI(TAG, "BLE GAP PASSKEY_NOTIF passkey:%"PRIu32, param->ble_security.key_notif.passkey); + break; + + case ESP_GAP_BLE_NC_REQ_EVT: // ESP_IO_CAP_IO + // The app will receive this event when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability. + // show the passkey number to the user to confirm it with the number displayed by peer device. + ESP_LOGI(TAG, "BLE GAP NC_REQ passkey:%"PRIu32, param->ble_security.key_notif.passkey); + esp_ble_confirm_reply(param->ble_security.key_notif.bd_addr, true); + break; + + case ESP_GAP_BLE_PASSKEY_REQ_EVT: // ESP_IO_CAP_IN + // The app will receive this evt when the IO has Input capability and the peer device IO has Output capability. + // See the passkey number on the peer device and send it back. + ESP_LOGI(TAG, "BLE GAP PASSKEY_REQ"); + //esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, 1234); + break; + + case ESP_GAP_BLE_SEC_REQ_EVT: + ESP_LOGI(TAG, "BLE GAP SEC_REQ"); + // Send the positive(true) security response to the peer device to accept the security request. + // If not accept the security request, should send the security response with negative(false) accept value. + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + break; + + default: + ESP_LOGV(TAG, "BLE GAP EVENT %s", ble_gap_evt_str(event)); + break; + } +} + +static esp_err_t init_ble_gap(void) +{ + esp_err_t ret; + + if ((ret = esp_ble_gap_register_callback(ble_gap_event_handler)) != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", ret); + return ret; + } + return ret; +} + +static esp_ble_scan_params_t hid_scan_params = { + .scan_type = BLE_SCAN_TYPE_ACTIVE, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_interval = 0x50, + .scan_window = 0x30, + .scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE, +}; + +static esp_err_t start_ble_scan(uint32_t seconds) +{ + esp_err_t ret = ESP_OK; + if ((ret = esp_ble_gap_set_scan_params(&hid_scan_params)) != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gap_set_scan_params failed: %d", ret); + return ret; + } + WAIT_BLE_CB(); + + if ((ret = esp_ble_gap_start_scanning(seconds)) != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gap_start_scanning failed: %d", ret); + return ret; + } + return ret; +} + +#if !CONFIG_BT_NIMBLE_ENABLED +esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name) +{ + + esp_err_t ret; + + const uint8_t hidd_service_uuid128[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x12, 0x18, 0x00, 0x00, + }; + + esp_ble_adv_data_t ble_adv_data = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec + .max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec + .appearance = appearance, + .manufacturer_len = 0, + .p_manufacturer_data = NULL, + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = sizeof(hidd_service_uuid128), + .p_service_uuid = (uint8_t *)hidd_service_uuid128, + .flag = 0x6, + }; + + esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND; + //esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT;//you have to enter the key on the host + //esp_ble_io_cap_t iocap = ESP_IO_CAP_IN;//you have to enter the key on the device + esp_ble_io_cap_t iocap = ESP_IO_CAP_IO;//you have to agree that key matches on both + //esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;//device is not capable of input or output, insecure + uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + uint8_t key_size = 16; //the key size should be 7~16 bytes + uint32_t passkey = 1234;//ESP_IO_CAP_OUT + + if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, 1)) != ESP_OK) { + ESP_LOGE(TAG, "GAP set_security_param AUTHEN_REQ_MODE failed: %d", ret); + return ret; + } + + if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, 1)) != ESP_OK) { + ESP_LOGE(TAG, "GAP set_security_param IOCAP_MODE failed: %d", ret); + return ret; + } + + if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, 1)) != ESP_OK) { + ESP_LOGE(TAG, "GAP set_security_param SET_INIT_KEY failed: %d", ret); + return ret; + } + + if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, 1)) != ESP_OK) { + ESP_LOGE(TAG, "GAP set_security_param SET_RSP_KEY failed: %d", ret); + return ret; + } + + if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, 1)) != ESP_OK) { + ESP_LOGE(TAG, "GAP set_security_param MAX_KEY_SIZE failed: %d", ret); + return ret; + } + + if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t))) != ESP_OK) { + ESP_LOGE(TAG, "GAP set_security_param SET_STATIC_PASSKEY failed: %d", ret); + return ret; + } + + if ((ret = esp_ble_gap_set_device_name(device_name)) != ESP_OK) { + ESP_LOGE(TAG, "GAP set_device_name failed: %d", ret); + return ret; + } + + if ((ret = esp_ble_gap_config_adv_data(&ble_adv_data)) != ESP_OK) { + ESP_LOGE(TAG, "GAP config_adv_data failed: %d", ret); + return ret; + } + + return ret; +} +#endif +esp_err_t esp_hid_ble_gap_adv_start(void) +{ + static esp_ble_adv_params_t hidd_adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x30, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + }; + return esp_ble_gap_start_advertising(&hidd_adv_params); +} +#endif /* CONFIG_BT_BLE_ENABLED */ + +#if CONFIG_BT_NIMBLE_ENABLED +#define GATT_SVR_SVC_HID_UUID 0x1812 + +static struct ble_hs_adv_fields fields; +static struct ble_hs_adv_fields scan_rsp_fields; +static ble_uuid16_t s_adv_uuid16; + +esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name) +{ + /** + * Legacy ADV payload is limited to 31 bytes. "HeavenlyHeroStar" (16 chars) + * plus flags, HID UUID, appearance and TX power exceeds that limit. + * Keep discoverability fields in ADV; put appearance/TX power in scan rsp. + */ + (void)appearance; + + memset(&fields, 0, sizeof fields); + memset(&scan_rsp_fields, 0, sizeof scan_rsp_fields); + + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + s_adv_uuid16 = (ble_uuid16_t)BLE_UUID16_INIT(GATT_SVR_SVC_HID_UUID); + fields.uuids16 = &s_adv_uuid16; + fields.num_uuids16 = 1; + fields.uuids16_is_complete = 1; + + scan_rsp_fields.appearance = appearance; + scan_rsp_fields.appearance_is_present = 1; + scan_rsp_fields.tx_pwr_lvl_is_present = 1; + scan_rsp_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + /* Just Works pairing (typical for BLE HID keyboards on Windows). */ + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; + ble_hs_cfg.sm_bonding = 1; + ble_hs_cfg.sm_mitm = 0; + ble_hs_cfg.sm_sc = 1; + ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC; + ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC; + + return ESP_OK; + +} + +static int +nimble_hid_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + ESP_LOGI(TAG, "connection %s; status=%d", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + return 0; + break; + case BLE_GAP_EVENT_DISCONNECT: + ESP_LOGI(TAG, "disconnect; reason=%d", event->disconnect.reason); + + return 0; + case BLE_GAP_EVENT_CONN_UPDATE: + /* The central has updated the connection parameters. */ + ESP_LOGI(TAG, "connection updated; status=%d", + event->conn_update.status); + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: + ESP_LOGI(TAG, "advertise complete; reason=%d", + event->adv_complete.reason); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); +#if BOARD_BLE_ENABLED + if (event->subscribe.cur_notify && !event->subscribe.prev_notify) { + ble_hid_on_input_notify(true); + } +#endif + return 0; + + case BLE_GAP_EVENT_MTU: + ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + MODLOG_DFLT(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + if (rc == 0) { + ESP_LOGI(TAG, "encrypted=%d bonded=%d", + desc.sec_state.encrypted, desc.sec_state.bonded); + } + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + MODLOG_DFLT(INFO, "notify_tx event; conn_handle=%d attr_handle=%d " + "status=%d is_indication=%d", + event->notify_tx.conn_handle, + event->notify_tx.attr_handle, + event->notify_tx.status, + event->notify_tx.indication); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + + case BLE_GAP_EVENT_PASSKEY_ACTION: + ESP_LOGI(TAG, "PASSKEY_ACTION_EVENT started"); + struct ble_sm_io pkey = {0}; + int key = 0; + + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + pkey.action = event->passkey.params.action; + pkey.passkey = 123456; // This is the passkey to be entered on peer + ESP_LOGI(TAG, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey); + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc); + } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + ESP_LOGI(TAG, "Accepting passkey.."); + pkey.action = event->passkey.params.action; + pkey.numcmp_accept = key; + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc); + } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + static uint8_t tem_oob[16] = {0}; + pkey.action = event->passkey.params.action; + for (int i = 0; i < 16; i++) { + pkey.oob[i] = tem_oob[i]; + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc); + } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { + ESP_LOGI(TAG, "Input not supported passing -> 123456"); + pkey.action = event->passkey.params.action; + pkey.passkey = 123456; + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc); + } + return 0; + } + return 0; +} +esp_err_t esp_hid_ble_gap_adv_start(void) +{ + int rc; + struct ble_gap_adv_params adv_params; + /* maximum possible duration for hid device(180s) */ + int32_t adv_duration_ms = 180000; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return rc; + } + rc = ble_gap_adv_rsp_set_fields(&scan_rsp_fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting scan response data; rc=%d\n", rc); + return rc; + } + /* Begin advertising. */ + memset(&adv_params, 0, sizeof adv_params); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(30);/* Recommended interval 30ms to 50ms */ + adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(50); + rc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, adv_duration_ms, + &adv_params, nimble_hid_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return rc; + } + return rc; +} +#endif + + +/* + * CONTROLLER INIT + * */ + +#if !CONFIG_BT_NIMBLE_ENABLED +static esp_err_t init_low_level(uint8_t mode) +{ + esp_err_t ret; + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); +#if CONFIG_IDF_TARGET_ESP32 + bt_cfg.mode = mode; +#endif +#if CONFIG_BT_HID_DEVICE_ENABLED + if (mode & ESP_BT_MODE_CLASSIC_BT) { + bt_cfg.bt_max_acl_conn = 3; + bt_cfg.bt_max_sync_conn = 3; + } else +#endif + { + ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + if (ret) { + ESP_LOGE(TAG, "esp_bt_controller_mem_release failed: %d", ret); + return ret; + } + } + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %d", ret); + return ret; + } + + ret = esp_bt_controller_enable(mode); + if (ret) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %d", ret); + return ret; + } + + esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT(); +#if (CONFIG_EXAMPLE_SSP_ENABLED == false) + bluedroid_cfg.ssp_en = false; +#endif + ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg); + if (ret) { + ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", ret); + return ret; + } + + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", ret); + return ret; + } +#if CONFIG_BT_HID_DEVICE_ENABLED + if (mode & ESP_BT_MODE_CLASSIC_BT) { + ret = init_bt_gap(); + if (ret) { + return ret; + } + } +#endif +#if CONFIG_BT_BLE_ENABLED + if (mode & ESP_BT_MODE_BLE) { + ret = init_ble_gap(); + if (ret) { + return ret; + } + } +#endif /* CONFIG_BT_BLE_ENABLED */ + return ret; +} + +static esp_err_t deinit_low_level(void) +{ + esp_err_t ret; + + if (bt_scan_results) { + esp_hid_scan_results_free(bt_scan_results); + bt_scan_results = NULL; + num_bt_scan_results = 0; + } + if (ble_scan_results) { + esp_hid_scan_results_free(ble_scan_results); + ble_scan_results = NULL; + num_ble_scan_results = 0; + } + + ret = esp_bluedroid_disable(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", ret); + } + + ret = esp_bluedroid_deinit(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_bluedroid_deinit failed: %d", ret); + } + + ret = esp_bt_controller_disable(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_disable failed: %d", ret); + } + + ret = esp_bt_controller_deinit(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_deinit failed: %d", ret); + } + + return ret; +} +#endif + +#if CONFIG_BT_NIMBLE_ENABLED +static esp_err_t init_low_level(uint8_t mode) +{ + esp_err_t ret; + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); +#if CONFIG_IDF_TARGET_ESP32 + bt_cfg.mode = mode; +#endif + ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + if (ret) { + ESP_LOGE(TAG, "esp_bt_controller_mem_release failed: %d", ret); + return ret; + } + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %d", ret); + return ret; + } + + ret = esp_bt_controller_enable(mode); + if (ret) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %d", ret); + return ret; + } + + ret = esp_nimble_init(); + if (ret) { + ESP_LOGE(TAG, "esp_nimble_init failed: %d", ret); + return ret; + } + + + return ret; +} + +static esp_err_t deinit_low_level(void) +{ + esp_err_t ret; + + ret = esp_nimble_deinit(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_nimble_deinit failed: %d", ret); + } + + ret = esp_bt_controller_disable(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_disable failed: %d", ret); + } + + ret = esp_bt_controller_deinit(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_deinit failed: %d", ret); + } + + return ret; +} +#endif + +esp_err_t esp_hid_gap_deinit(void) +{ + esp_err_t ret; + + ret = deinit_low_level(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "deinit_low_level failed: %d", ret); + } + + if (bt_hidh_cb_semaphore != NULL) { + vSemaphoreDelete(bt_hidh_cb_semaphore); + bt_hidh_cb_semaphore = NULL; + } + + if (ble_hidh_cb_semaphore != NULL) { + vSemaphoreDelete(ble_hidh_cb_semaphore); + ble_hidh_cb_semaphore = NULL; + } + + return ESP_OK; +} + +esp_err_t esp_hid_gap_init(uint8_t mode) +{ + esp_err_t ret; + if (!mode || mode > ESP_BT_MODE_BTDM) { + ESP_LOGE(TAG, "Invalid mode given!"); + return ESP_FAIL; + } + + if (bt_hidh_cb_semaphore != NULL) { + ESP_LOGE(TAG, "Already initialised"); + return ESP_FAIL; + } + + bt_hidh_cb_semaphore = xSemaphoreCreateBinary(); + if (bt_hidh_cb_semaphore == NULL) { + ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!"); + return ESP_FAIL; + } + + ble_hidh_cb_semaphore = xSemaphoreCreateBinary(); + if (ble_hidh_cb_semaphore == NULL) { + ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!"); + vSemaphoreDelete(bt_hidh_cb_semaphore); + bt_hidh_cb_semaphore = NULL; + return ESP_FAIL; + } + + ret = init_low_level(mode); + if (ret != ESP_OK) { + vSemaphoreDelete(bt_hidh_cb_semaphore); + bt_hidh_cb_semaphore = NULL; + vSemaphoreDelete(ble_hidh_cb_semaphore); + ble_hidh_cb_semaphore = NULL; + return ret; + } + + return ESP_OK; +} + +#if !CONFIG_BT_NIMBLE_ENABLED +esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results) +{ + if (num_bt_scan_results || bt_scan_results || num_ble_scan_results || ble_scan_results) { + ESP_LOGE(TAG, "There are old scan results. Free them first!"); + return ESP_FAIL; + } + +#if CONFIG_BT_BLE_ENABLED + if (start_ble_scan(seconds) == ESP_OK) { + WAIT_BLE_CB(); + } else { + return ESP_FAIL; + } +#endif /* CONFIG_BT_BLE_ENABLED */ + +#if CONFIG_BT_HID_DEVICE_ENABLED + if (start_bt_scan(seconds) == ESP_OK) { + WAIT_BT_CB(); + } else { + return ESP_FAIL; + } +#endif + + *num_results = num_bt_scan_results + num_ble_scan_results; + *results = bt_scan_results; + if (num_bt_scan_results) { + while (bt_scan_results->next != NULL) { + bt_scan_results = bt_scan_results->next; + } + bt_scan_results->next = ble_scan_results; + } else { + *results = ble_scan_results; + } + + num_bt_scan_results = 0; + bt_scan_results = NULL; + num_ble_scan_results = 0; + ble_scan_results = NULL; + return ESP_OK; +} +#endif diff --git a/main/services/esp_hid_gap.h b/main/services/esp_hid_gap.h new file mode 100644 index 0000000..fe01fc7 --- /dev/null +++ b/main/services/esp_hid_gap.h @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#ifndef _ESP_HID_GAP_H_ +#define _ESP_HID_GAP_H_ + +#define HIDD_IDLE_MODE 0x00 +#define HIDD_BLE_MODE 0x01 +#define HIDD_BT_MODE 0x02 +#define HIDD_BTDM_MODE 0x03 + +#if CONFIG_BT_HID_DEVICE_ENABLED +#if CONFIG_BT_BLE_ENABLED +#define HID_DEV_MODE HIDD_BTDM_MODE +#else +#define HID_DEV_MODE HIDD_BT_MODE +#endif +#elif CONFIG_BT_BLE_ENABLED +#define HID_DEV_MODE HIDD_BLE_MODE +#elif CONFIG_BT_NIMBLE_ENABLED +#define HID_DEV_MODE HIDD_BLE_MODE +#else +#define HID_DEV_MODE HIDD_IDLE_MODE +#endif + +#include "esp_err.h" +#include "esp_log.h" + +#include "esp_bt.h" +#if !CONFIG_BT_NIMBLE_ENABLED +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#endif +#include "esp_hid_common.h" +#if CONFIG_BT_BLE_ENABLED +#include "esp_gattc_api.h" +#include "esp_gatt_defs.h" +#include "esp_gap_ble_api.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if !CONFIG_BT_NIMBLE_ENABLED +typedef struct esp_hidh_scan_result_s { + struct esp_hidh_scan_result_s *next; + + esp_bd_addr_t bda; + const char *name; + int8_t rssi; + esp_hid_usage_t usage; + esp_hid_transport_t transport; //BT, BLE or USB + union { + struct { + esp_bt_cod_t cod; + esp_bt_uuid_t uuid; + } bt; + struct { + esp_ble_addr_type_t addr_type; + uint16_t appearance; + } ble; + }; +} esp_hid_scan_result_t; + +esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results); +void esp_hid_scan_results_free(esp_hid_scan_result_t *results); +const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type); +void print_uuid(esp_bt_uuid_t *uuid); +#endif + +esp_err_t esp_hid_gap_init(uint8_t mode); +esp_err_t esp_hid_gap_deinit(void); + +esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name); +esp_err_t esp_hid_ble_gap_adv_start(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP_HIDH_GAP_H_ */ diff --git a/sdkconfig.defaults b/sdkconfig.defaults index c90c0d2..573fe13 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -5,7 +5,17 @@ CONFIG_ESP_LCD_TOUCH_CST816S_DISABLE_READ_ID=y CONFIG_PARTITION_TABLE_SINGLE_APP=n CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y -# BLE (NimBLE peripheral) +# BLE (NimBLE peripheral + HID keyboard) CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n +CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y +CONFIG_BT_NIMBLE_SM_LEGACY=y +CONFIG_BT_NIMBLE_SM_SC=y +CONFIG_BT_NIMBLE_NVS_PERSIST=y +CONFIG_BT_NIMBLE_HID_SERVICE=y +CONFIG_BT_NIMBLE_SVC_HID_MAX_RPTS=3 +CONFIG_BT_NIMBLE_BAS_SERVICE=y +CONFIG_BT_NIMBLE_DIS_SERVICE=y +CONFIG_BT_NIMBLE_SVC_DIS_PNP_ID=y +CONFIG_BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME=y