/* This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "nvs_flash.h" #include "esp_bt.h" #include "esp_bt_defs.h" #if CONFIG_BT_BLE_ENABLED #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_gatt_defs.h" #endif #include "esp_bt_main.h" #include "esp_bt_device.h" #include "esp_hidd.h" #include "esp_hid_gap.h" static const char *TAG = "HID_DEV_DEMO"; typedef struct { xTaskHandle task_hdl; esp_hidd_dev_t *hid_dev; uint8_t protocol_mode; uint8_t *buffer; } local_param_t; #if CONFIG_BT_BLE_ENABLED static local_param_t s_ble_hid_param = {0}; const unsigned char hidapiReportMap[] = { //8 bytes input, 8 bytes feature 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x0A, 0x00, 0x01, // Usage (0x0100) 0xA1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID (1) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x08, // Report Count (8) 0x09, 0x01, // Usage (0x01) 0x82, 0x02, 0x01, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes) 0x95, 0x08, // Report Count (8) 0x09, 0x02, // Usage (0x02) 0xB2, 0x02, 0x01, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes) 0x95, 0x08, // Report Count (8) 0x09, 0x03, // Usage (0x03) 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0, // End Collection // 38 bytes }; const unsigned char mediaReportMap[] = { 0x05, 0x0C, // Usage Page (Consumer) 0x09, 0x01, // Usage (Consumer Control) 0xA1, 0x01, // Collection (Application) 0x85, 0x03, // Report ID (3) 0x09, 0x02, // Usage (Numeric Key Pad) 0xA1, 0x02, // Collection (Logical) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (0x01) 0x29, 0x0A, // Usage Maximum (0x0A) 0x15, 0x01, // Logical Minimum (1) 0x25, 0x0A, // Logical Maximum (10) 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, // End Collection 0x05, 0x0C, // Usage Page (Consumer) 0x09, 0x86, // Usage (Channel) 0x15, 0xFF, // Logical Minimum (-1) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x02, // Report Size (2) 0x95, 0x01, // Report Count (1) 0x81, 0x46, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,Null State) 0x09, 0xE9, // Usage (Volume Increment) 0x09, 0xEA, // Usage (Volume Decrement) 0x15, 0x00, // Logical Minimum (0) 0x75, 0x01, // Report Size (1) 0x95, 0x02, // Report Count (2) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0xE2, // Usage (Mute) 0x09, 0x30, // Usage (Power) 0x09, 0x83, // Usage (Recall Last) 0x09, 0x81, // Usage (Assign Selection) 0x09, 0xB0, // Usage (Play) 0x09, 0xB1, // Usage (Pause) 0x09, 0xB2, // Usage (Record) 0x09, 0xB3, // Usage (Fast Forward) 0x09, 0xB4, // Usage (Rewind) 0x09, 0xB5, // Usage (Scan Next Track) 0x09, 0xB6, // Usage (Scan Previous Track) 0x09, 0xB7, // Usage (Stop) 0x15, 0x01, // Logical Minimum (1) 0x25, 0x0C, // Logical Maximum (12) 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x80, // Usage (Selection) 0xA1, 0x02, // Collection (Logical) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (0x01) 0x29, 0x03, // Usage Maximum (0x03) 0x15, 0x01, // Logical Minimum (1) 0x25, 0x03, // Logical Maximum (3) 0x75, 0x02, // Report Size (2) 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, // End Collection 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, // End Collection }; static esp_hid_raw_report_map_t ble_report_maps[] = { { .data = hidapiReportMap, .len = sizeof(hidapiReportMap) }, { .data = mediaReportMap, .len = sizeof(mediaReportMap) } }; static esp_hid_device_config_t ble_hid_config = { .vendor_id = 0x16C0, .product_id = 0x05DF, .version = 0x0100, .device_name = "ESP BLE HID2", .manufacturer_name = "Espressif", .serial_number = "1234567890", .report_maps = ble_report_maps, .report_maps_len = 2 }; #define HID_CC_RPT_MUTE 1 #define HID_CC_RPT_POWER 2 #define HID_CC_RPT_LAST 3 #define HID_CC_RPT_ASSIGN_SEL 4 #define HID_CC_RPT_PLAY 5 #define HID_CC_RPT_PAUSE 6 #define HID_CC_RPT_RECORD 7 #define HID_CC_RPT_FAST_FWD 8 #define HID_CC_RPT_REWIND 9 #define HID_CC_RPT_SCAN_NEXT_TRK 10 #define HID_CC_RPT_SCAN_PREV_TRK 11 #define HID_CC_RPT_STOP 12 #define HID_CC_RPT_CHANNEL_UP 0x10 #define HID_CC_RPT_CHANNEL_DOWN 0x30 #define HID_CC_RPT_VOLUME_UP 0x40 #define HID_CC_RPT_VOLUME_DOWN 0x80 // HID Consumer Control report bitmasks #define HID_CC_RPT_NUMERIC_BITS 0xF0 #define HID_CC_RPT_CHANNEL_BITS 0xCF #define HID_CC_RPT_VOLUME_BITS 0x3F #define HID_CC_RPT_BUTTON_BITS 0xF0 #define HID_CC_RPT_SELECTION_BITS 0xCF // Macros for the HID Consumer Control 2-byte report #define HID_CC_RPT_SET_NUMERIC(s, x) (s)[0] &= HID_CC_RPT_NUMERIC_BITS; (s)[0] = (x) #define HID_CC_RPT_SET_CHANNEL(s, x) (s)[0] &= HID_CC_RPT_CHANNEL_BITS; (s)[0] |= ((x) & 0x03) << 4 #define HID_CC_RPT_SET_VOLUME_UP(s) (s)[0] &= HID_CC_RPT_VOLUME_BITS; (s)[0] |= 0x40 #define HID_CC_RPT_SET_VOLUME_DOWN(s) (s)[0] &= HID_CC_RPT_VOLUME_BITS; (s)[0] |= 0x80 #define HID_CC_RPT_SET_BUTTON(s, x) (s)[1] &= HID_CC_RPT_BUTTON_BITS; (s)[1] |= (x) #define HID_CC_RPT_SET_SELECTION(s, x) (s)[1] &= HID_CC_RPT_SELECTION_BITS; (s)[1] |= ((x) & 0x03) << 4 // HID Consumer Usage IDs (subset of the codes available in the USB HID Usage Tables spec) #define HID_CONSUMER_POWER 48 // Power #define HID_CONSUMER_RESET 49 // Reset #define HID_CONSUMER_SLEEP 50 // Sleep #define HID_CONSUMER_MENU 64 // Menu #define HID_CONSUMER_SELECTION 128 // Selection #define HID_CONSUMER_ASSIGN_SEL 129 // Assign Selection #define HID_CONSUMER_MODE_STEP 130 // Mode Step #define HID_CONSUMER_RECALL_LAST 131 // Recall Last #define HID_CONSUMER_QUIT 148 // Quit #define HID_CONSUMER_HELP 149 // Help #define HID_CONSUMER_CHANNEL_UP 156 // Channel Increment #define HID_CONSUMER_CHANNEL_DOWN 157 // Channel Decrement #define HID_CONSUMER_PLAY 176 // Play #define HID_CONSUMER_PAUSE 177 // Pause #define HID_CONSUMER_RECORD 178 // Record #define HID_CONSUMER_FAST_FORWARD 179 // Fast Forward #define HID_CONSUMER_REWIND 180 // Rewind #define HID_CONSUMER_SCAN_NEXT_TRK 181 // Scan Next Track #define HID_CONSUMER_SCAN_PREV_TRK 182 // Scan Previous Track #define HID_CONSUMER_STOP 183 // Stop #define HID_CONSUMER_EJECT 184 // Eject #define HID_CONSUMER_RANDOM_PLAY 185 // Random Play #define HID_CONSUMER_SELECT_DISC 186 // Select Disk #define HID_CONSUMER_ENTER_DISC 187 // Enter Disc #define HID_CONSUMER_REPEAT 188 // Repeat #define HID_CONSUMER_STOP_EJECT 204 // Stop/Eject #define HID_CONSUMER_PLAY_PAUSE 205 // Play/Pause #define HID_CONSUMER_PLAY_SKIP 206 // Play/Skip #define HID_CONSUMER_VOLUME 224 // Volume #define HID_CONSUMER_BALANCE 225 // Balance #define HID_CONSUMER_MUTE 226 // Mute #define HID_CONSUMER_BASS 227 // Bass #define HID_CONSUMER_VOLUME_UP 233 // Volume Increment #define HID_CONSUMER_VOLUME_DOWN 234 // Volume Decrement #define HID_RPT_ID_CC_IN 3 // Consumer Control input report ID #define HID_CC_IN_RPT_LEN 2 // Consumer Control input report Len void esp_hidd_send_consumer_value(uint8_t key_cmd, bool key_pressed) { uint8_t buffer[HID_CC_IN_RPT_LEN] = {0, 0}; if (key_pressed) { switch (key_cmd) { case HID_CONSUMER_CHANNEL_UP: HID_CC_RPT_SET_CHANNEL(buffer, HID_CC_RPT_CHANNEL_UP); break; case HID_CONSUMER_CHANNEL_DOWN: HID_CC_RPT_SET_CHANNEL(buffer, HID_CC_RPT_CHANNEL_DOWN); break; case HID_CONSUMER_VOLUME_UP: HID_CC_RPT_SET_VOLUME_UP(buffer); break; case HID_CONSUMER_VOLUME_DOWN: HID_CC_RPT_SET_VOLUME_DOWN(buffer); break; case HID_CONSUMER_MUTE: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_MUTE); break; case HID_CONSUMER_POWER: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_POWER); break; case HID_CONSUMER_RECALL_LAST: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_LAST); break; case HID_CONSUMER_ASSIGN_SEL: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_ASSIGN_SEL); break; case HID_CONSUMER_PLAY: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_PLAY); break; case HID_CONSUMER_PAUSE: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_PAUSE); break; case HID_CONSUMER_RECORD: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_RECORD); break; case HID_CONSUMER_FAST_FORWARD: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_FAST_FWD); break; case HID_CONSUMER_REWIND: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_REWIND); break; case HID_CONSUMER_SCAN_NEXT_TRK: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_SCAN_NEXT_TRK); break; case HID_CONSUMER_SCAN_PREV_TRK: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_SCAN_PREV_TRK); break; case HID_CONSUMER_STOP: HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_STOP); break; default: break; } } esp_hidd_dev_input_set(s_ble_hid_param.hid_dev, 1, HID_RPT_ID_CC_IN, buffer, HID_CC_IN_RPT_LEN); return; } void ble_hid_demo_task(void *pvParameters) { static bool send_volum_up = false; while (1) { ESP_LOGI(TAG, "Send the volume"); if (send_volum_up) { esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, true); vTaskDelay(100 / portTICK_PERIOD_MS); esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, false); } else { esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, true); vTaskDelay(100 / portTICK_PERIOD_MS); esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, false); } send_volum_up = !send_volum_up; vTaskDelay(2000 / portTICK_PERIOD_MS); } } void ble_hid_task_start_up(void) { xTaskCreate(ble_hid_demo_task, "ble_hid_demo_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3, &s_ble_hid_param.task_hdl); } void ble_hid_task_shut_down(void) { if (s_ble_hid_param.task_hdl) { vTaskDelete(s_ble_hid_param.task_hdl); s_ble_hid_param.task_hdl = NULL; } } static void ble_hidd_event_callback(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; static const char *TAG = "HID_DEV_BLE"; switch (event) { case ESP_HIDD_START_EVENT: { ESP_LOGI(TAG, "START"); esp_hid_ble_gap_adv_start(); break; } case ESP_HIDD_CONNECT_EVENT: { ESP_LOGI(TAG, "CONNECT"); ble_hid_task_start_up();//todo: this should be on auth_complete (in GAP) break; } case ESP_HIDD_PROTOCOL_MODE_EVENT: { ESP_LOGI(TAG, "PROTOCOL MODE[%u]: %s", param->protocol_mode.map_index, param->protocol_mode.protocol_mode ? "REPORT" : "BOOT"); break; } case ESP_HIDD_CONTROL_EVENT: { ESP_LOGI(TAG, "CONTROL[%u]: %sSUSPEND", param->control.map_index, param->control.control ? "EXIT_" : ""); break; } case ESP_HIDD_OUTPUT_EVENT: { ESP_LOGI(TAG, "OUTPUT[%u]: %8s ID: %2u, Len: %d, Data:", param->output.map_index, esp_hid_usage_str(param->output.usage), param->output.report_id, param->output.length); ESP_LOG_BUFFER_HEX(TAG, param->output.data, param->output.length); break; } case ESP_HIDD_FEATURE_EVENT: { ESP_LOGI(TAG, "FEATURE[%u]: %8s ID: %2u, Len: %d, Data:", param->feature.map_index, esp_hid_usage_str(param->feature.usage), param->feature.report_id, param->feature.length); ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length); break; } case ESP_HIDD_DISCONNECT_EVENT: { ESP_LOGI(TAG, "DISCONNECT: %s", esp_hid_disconnect_reason_str(esp_hidd_dev_transport_get(param->disconnect.dev), param->disconnect.reason)); ble_hid_task_shut_down(); esp_hid_ble_gap_adv_start(); break; } case ESP_HIDD_STOP_EVENT: { ESP_LOGI(TAG, "STOP"); break; } default: break; } return; } #endif #if CONFIG_BT_HID_DEVICE_ENABLED static local_param_t s_bt_hid_param = {0}; const unsigned char mouseReportMap[] = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x02, // USAGE (Mouse) 0xa1, 0x01, // COLLECTION (Application) 0x09, 0x01, // USAGE (Pointer) 0xa1, 0x00, // COLLECTION (Physical) 0x05, 0x09, // USAGE_PAGE (Button) 0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x95, 0x03, // REPORT_COUNT (3) 0x75, 0x01, // REPORT_SIZE (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x05, // REPORT_SIZE (5) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x30, // USAGE (X) 0x09, 0x31, // USAGE (Y) 0x09, 0x38, // USAGE (Wheel) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x03, // REPORT_COUNT (3) 0x81, 0x06, // INPUT (Data,Var,Rel) 0xc0, // END_COLLECTION 0xc0 // END_COLLECTION }; static esp_hid_raw_report_map_t bt_report_maps[] = { { .data = mouseReportMap, .len = sizeof(mouseReportMap) }, }; static esp_hid_device_config_t bt_hid_config = { .vendor_id = 0x16C0, .product_id = 0x05DF, .version = 0x0100, .device_name = "ESP BT HID1", .manufacturer_name = "Espressif", .serial_number = "1234567890", .report_maps = bt_report_maps, .report_maps_len = 1 }; // send the buttons, change in x, and change in y void send_mouse(uint8_t buttons, char dx, char dy, char wheel) { static uint8_t buffer[4] = {0}; buffer[0] = buttons; buffer[1] = dx; buffer[2] = dy; buffer[3] = wheel; esp_hidd_dev_input_set(s_bt_hid_param.hid_dev, 0, 0, buffer, 4); } void bt_hid_demo_task(void *pvParameters) { static const char* help_string = "########################################################################\n"\ "BT hid mouse demo usage:\n"\ "You can input these value to simulate mouse: 'q', 'w', 'e', 'a', 's', 'd', 'h'\n"\ "q -- click the left key\n"\ "w -- move up\n"\ "e -- click the right key\n"\ "a -- move left\n"\ "s -- move down\n"\ "d -- move right\n"\ "h -- show the help\n"\ "########################################################################\n"; printf("%s\n", help_string); char c; while (1) { c = fgetc(stdin); switch (c) { case 'q': send_mouse(1, 0, 0, 0); break; case 'w': send_mouse(0, 0, -10, 0); break; case 'e': send_mouse(2, 0, 0, 0); break; case 'a': send_mouse(0, -10, 0, 0); break; case 's': send_mouse(0, 0, 10, 0); break; case 'd': send_mouse(0, 10, 0, 0); break; case 'h': printf("%s\n", help_string); break; default: break; } vTaskDelay(10 / portTICK_PERIOD_MS); } } void bt_hid_task_start_up(void) { xTaskCreate(bt_hid_demo_task, "bt_hid_demo_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3, &s_bt_hid_param.task_hdl); return; } void bt_hid_task_shut_down(void) { if (s_bt_hid_param.task_hdl) { vTaskDelete(s_bt_hid_param.task_hdl); s_bt_hid_param.task_hdl = NULL; } } static void bt_hidd_event_callback(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; static const char *TAG = "HID_DEV_BT"; switch (event) { case ESP_HIDD_START_EVENT: { if (param->start.status == ESP_OK) { ESP_LOGI(TAG, "START OK"); ESP_LOGI(TAG, "Setting to connectable, discoverable"); esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); } else { ESP_LOGE(TAG, "START failed!"); } break; } case ESP_HIDD_CONNECT_EVENT: { if (param->connect.status == ESP_OK) { ESP_LOGI(TAG, "CONNECT OK"); ESP_LOGI(TAG, "Setting to non-connectable, non-discoverable"); esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); bt_hid_task_start_up(); } else { ESP_LOGE(TAG, "CONNECT failed!"); } break; } case ESP_HIDD_PROTOCOL_MODE_EVENT: { ESP_LOGI(TAG, "PROTOCOL MODE[%u]: %s", param->protocol_mode.map_index, param->protocol_mode.protocol_mode ? "REPORT" : "BOOT"); break; } case ESP_HIDD_OUTPUT_EVENT: { ESP_LOGI(TAG, "OUTPUT[%u]: %8s ID: %2u, Len: %d, Data:", param->output.map_index, esp_hid_usage_str(param->output.usage), param->output.report_id, param->output.length); ESP_LOG_BUFFER_HEX(TAG, param->output.data, param->output.length); break; } case ESP_HIDD_FEATURE_EVENT: { ESP_LOGI(TAG, "FEATURE[%u]: %8s ID: %2u, Len: %d, Data:", param->feature.map_index, esp_hid_usage_str(param->feature.usage), param->feature.report_id, param->feature.length); ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length); break; } case ESP_HIDD_DISCONNECT_EVENT: { if (param->disconnect.status == ESP_OK) { ESP_LOGI(TAG, "DISCONNECT OK"); bt_hid_task_shut_down(); ESP_LOGI(TAG, "Setting to connectable, discoverable again"); esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); } else { ESP_LOGE(TAG, "DISCONNECT failed!"); } break; } case ESP_HIDD_STOP_EVENT: { ESP_LOGI(TAG, "STOP"); break; } default: break; } return; } #endif void app_main(void) { esp_err_t ret; #if HID_DEV_MODE == HIDD_IDLE_MODE ESP_LOGE(TAG, "Please turn on BT HID device or BLE!"); return; #endif ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); ESP_LOGI(TAG, "setting hid gap, mode:%d", HID_DEV_MODE); ret = esp_hid_gap_init(HID_DEV_MODE); ESP_ERROR_CHECK( ret ); #if CONFIG_BT_BLE_ENABLED ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_GENERIC, ble_hid_config.device_name); ESP_ERROR_CHECK( ret ); if ((ret = esp_ble_gatts_register_callback(esp_hidd_gatts_event_handler)) != ESP_OK) { ESP_LOGE(TAG, "GATTS register callback failed: %d", ret); return; } ESP_LOGI(TAG, "setting ble device"); ESP_ERROR_CHECK( esp_hidd_dev_init(&ble_hid_config, ESP_HID_TRANSPORT_BLE, ble_hidd_event_callback, &s_ble_hid_param.hid_dev)); #endif #if CONFIG_BT_HID_DEVICE_ENABLED ESP_LOGI(TAG, "setting device name"); esp_bt_dev_set_device_name(bt_hid_config.device_name); ESP_LOGI(TAG, "setting cod major, peripheral"); esp_bt_cod_t cod; cod.major = ESP_BT_COD_MAJOR_DEV_PERIPHERAL; esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR); vTaskDelay(1000 / portTICK_PERIOD_MS); ESP_LOGI(TAG, "setting bt device"); ESP_ERROR_CHECK( esp_hidd_dev_init(&bt_hid_config, ESP_HID_TRANSPORT_BT, bt_hidd_event_callback, &s_bt_hid_param.hid_dev)); #endif }