feat(nimble): added HID over Gatt profile support

This commit is contained in:
Roshan Bangar 2023-07-31 15:32:07 +05:30 committed by Rahul Tank
parent 04b6feb1ad
commit a95bf9e0c3
24 changed files with 3004 additions and 78 deletions

View File

@ -537,6 +537,8 @@ if(CONFIG_BT_ENABLED)
host/nimble/nimble/nimble/host/services/prox/include
host/nimble/nimble/nimble/host/services/cts/include
host/nimble/nimble/nimble/host/services/tps/include
host/nimble/nimble/nimble/host/services/hid/include
host/nimble/nimble/nimble/host/services/sps/include
host/nimble/nimble/nimble/host/util/include
host/nimble/nimble/nimble/host/store/ram/include
host/nimble/nimble/nimble/host/store/config/include
@ -557,6 +559,8 @@ if(CONFIG_BT_ENABLED)
"host/nimble/nimble/nimble/host/services/lls/src/ble_svc_lls.c"
"host/nimble/nimble/nimble/host/services/prox/src/ble_svc_prox.c"
"host/nimble/nimble/nimble/host/services/cts/src/ble_svc_cts.c"
"host/nimble/nimble/nimble/host/services/hid/src/ble_svc_hid.c"
"host/nimble/nimble/nimble/host/services/sps/src/ble_svc_sps.c"
"host/nimble/nimble/nimble/host/src/ble_hs_conn.c"
"host/nimble/nimble/nimble/host/src/ble_store_util.c"
"host/nimble/nimble/nimble/host/src/ble_sm.c"

View File

@ -659,6 +659,29 @@ config BT_NIMBLE_BLE_GATT_BLOB_TRANSFER
This option is used when data to be sent is more than 512 bytes. For peripheral role,
BT_NIMBLE_MSYS_1_BLOCK_COUNT needs to be increased according to the need.
menu "BLE Services"
menuconfig BT_NIMBLE_HID_SERVICE
bool "HID service"
depends on BT_NIMBLE_ENABLED
default n
help
Enable HID service support
config BT_NIMBLE_SVC_HID_MAX_INSTANCES
depends on BT_NIMBLE_HID_SERVICE
int "Maximum HID service instances"
default 2
help
Defines maximum number of HID service instances
config BT_NIMBLE_SVC_HID_MAX_RPTS
depends on BT_NIMBLE_HID_SERVICE
int "Maximum HID Report characteristics per service instance"
default 3
help
Defines maximum number of report characteristics per service instance
endmenu
config BT_NIMBLE_VS_SUPPORT
bool "Enable support for VSC and VSE"
help

@ -1 +1 @@
Subproject commit 08ac3ca677be417e0087159629d0359c4a9a1585
Subproject commit 34e3842741f598c00082ef7d2cea5f22615366f6

View File

@ -908,6 +908,20 @@
#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM
#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM (0)
#endif
/*** nimble/host/services/hid */
#ifndef MYNEWT_VAL_BLE_SVC_HID_SERVICE
#define MYNEWT_VAL_BLE_SVC_HID_SERVICE CONFIG_BT_NIMBLE_HID_SERVICE
#endif
#ifndef MYNEWT_VAL_BLE_SVC_HID_MAX_RPTS
#define MYNEWT_VAL_BLE_SVC_HID_MAX_RPTS CONFIG_BT_NIMBLE_SVC_HID_MAX_RPTS
#endif
#ifndef MYNEWT_VAL_BLE_SVC_HID_MAX_SVC_INSTANCES
#define MYNEWT_VAL_BLE_SVC_HID_MAX_SVC_INSTANCES CONFIG_BT_NIMBLE_SVC_HID_MAX_INSTANCES
#endif
#ifndef MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO
#define MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO (9)
#endif
@ -1570,6 +1584,15 @@
#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1)
#endif
#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT (NULL)
#endif
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (-1)
#endif
/*** @apache-mynewt-nimble/nimble/host/services/gap */
#ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE
#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE

View File

@ -13,6 +13,11 @@ if(CONFIG_BT_ENABLED)
"src/bt_hidh.c"
"src/bt_hidd.c")
endif()
if(CONFIG_BT_NIMBLE_ENABLED)
list(APPEND srcs
"src/nimble_hidd.c"
"src/nimble_hidh.c")
endif()
endif()
idf_component_register(SRCS "${srcs}"

View File

@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
#if CONFIG_BT_NIMBLE_ENABLED
/**
* @brief Open BlueTooth HID Device using BlueDroid
* @param bda : BT Device Address
* @param transport : BT Device Protocol (Classic/HID)
* @param remote_addr_type : BLE Remote address type
*
* @return: ESP_OK on success
*/
esp_hidh_dev_t *esp_hidh_dev_open(uint8_t *bda, esp_hid_transport_t transport, uint8_t remote_addr_type);
#endif /* CONFIG_BT_NIMBLE_ENABLED */
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,8 @@
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -28,6 +20,10 @@ extern "C" {
#include "esp_hidh_bluedroid.h"
#endif
#if CONFIG_BT_NIMBLE_ENABLED
#include "esp_hidh_nimble.h"
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,8 @@
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -22,7 +14,7 @@
extern "C" {
#endif
#if CONFIG_GATTS_ENABLE
#if CONFIG_GATTS_ENABLE || CONFIG_BT_NIMBLE_ENABLED
esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev, const esp_hid_device_config_t *config, esp_event_handler_t callback);

View File

@ -1,16 +1,8 @@
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -20,12 +12,16 @@
extern "C" {
#endif
#if CONFIG_GATTC_ENABLE
#if CONFIG_GATTC_ENABLE || CONFIG_BT_NIMBLE_ENABLED
esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config);
esp_err_t esp_ble_hidh_deinit(void);
#if CONFIG_BT_NIMBLE_ENABLED
esp_hidh_dev_t *esp_ble_hidh_dev_open(uint8_t *bda, uint8_t address_type);
#else
esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t address_type);
#endif
#endif /* CONFIG_GATTC_ENABLE */

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,6 +17,9 @@
#include "esp_event.h"
#include "sys/queue.h"
#include "esp_timer.h"
#if CONFIG_BT_NIMBLE_ENABLED
#include "nimble/ble.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -52,7 +55,11 @@ struct esp_hidh_dev_s {
esp_timer_handle_t trans_timer; //transactiion timer
uint8_t report_type; //Get_Report tansaction report_type
uint8_t report_id; //Get_Report tansaction report_id
#if CONFIG_BT_NIMBLE_ENABLED
uint8_t *protocol_mode; // protocol mode is unique for each hid service instance
#else
uint8_t protocol_mode; //device protocol mode
#endif
bool connected; //we have all required data to communicate
bool opened; //we opened the device manually, else the device connected to us
bool added; //If lower layer has added the device
@ -82,6 +89,9 @@ struct esp_hidh_dev_s {
#if CONFIG_BLUEDROID_ENABLED
esp_bd_addr_t bda;
#endif /* CONFIG_BLUEDROID_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
uint8_t bda[6];
#endif
union {
#if CONFIG_BT_HID_HOST_ENABLED
@ -102,6 +112,15 @@ struct esp_hidh_dev_s {
uint16_t battery_ccc_handle;
} ble;
#endif /* CONFIG_GATTC_ENABLE */
#if CONFIG_BT_NIMBLE_ENABLED
struct {
uint8_t address_type;
int conn_id;
uint16_t appearance;
uint16_t battery_handle;
uint16_t battery_ccc_handle;
} ble;
#endif
};
TAILQ_ENTRY(esp_hidh_dev_s) devices;
};
@ -115,6 +134,10 @@ esp_hidh_dev_t *esp_hidh_dev_get_by_bda(esp_bd_addr_t bda); //BT/BLE
esp_hidh_dev_t *esp_hidh_dev_get_by_handle(uint8_t handle); //Classic Bluetooth Only
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id); //BLE Only
#endif /* CONFIG_BLUEDROID_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
esp_hidh_dev_t *esp_hidh_dev_get_by_bda(uint8_t* bda); // BLE Only
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id); //BLE Only
#endif
esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_type_proto(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t protocol_mode);
esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_and_type(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type);

View File

@ -1,16 +1,8 @@
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"

View File

@ -8,7 +8,7 @@
#include "esp_hidd_private.h"
#include "esp_event_base.h"
#if CONFIG_GATTS_ENABLE
#if CONFIG_GATTS_ENABLE || CONFIG_BT_NIMBLE_ENABLED
#include "ble_hidd.h"
#endif /* CONFIG_GATTS_ENABLE */
@ -27,7 +27,7 @@ esp_err_t esp_hidd_dev_init(const esp_hid_device_config_t *config, esp_hid_trans
}
switch (transport) {
#if CONFIG_GATTS_ENABLE
#if CONFIG_GATTS_ENABLE || CONFIG_BT_NIMBLE_ENABLED
case ESP_HID_TRANSPORT_BLE:
ret = esp_ble_hidd_dev_init(dev, config, callback);
break;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -88,7 +88,7 @@ esp_err_t esp_hidh_init(const esp_hidh_config_t *config)
}
#endif /* CONFIG_BT_HID_HOST_ENABLED */
#if CONFIG_GATTC_ENABLE
#if CONFIG_GATTC_ENABLE || CONFIG_BT_NIMBLE_ENABLED
if (err == ESP_OK) {
err = esp_ble_hidh_init(config);
}
@ -123,7 +123,7 @@ esp_err_t esp_hidh_deinit(void)
}
#endif /* CONFIG_BT_HID_HOST_ENABLED */
#if CONFIG_GATTC_ENABLE
#if CONFIG_GATTC_ENABLE || CONFIG_BT_NIMBLE_ENABLED
if (err == ESP_OK) {
err = esp_ble_hidh_deinit();
}
@ -150,6 +150,11 @@ esp_hidh_dev_t *esp_hidh_dev_open(esp_bd_addr_t bda, esp_hid_transport_t transpo
dev = esp_ble_hidh_dev_open(bda, (esp_ble_addr_type_t)remote_addr_type);
}
#endif /* CONFIG_GATTC_ENABLE */
#if CONFIG_BT_NIMBLE_ENABLED
if (transport == ESP_HID_TRANSPORT_BLE) {
dev = esp_ble_hidh_dev_open(bda, remote_addr_type);
}
#endif /* CONFIG_BT_NIMBLE_ENABLED */
#if CONFIG_BT_HID_HOST_ENABLED
if (transport == ESP_HID_TRANSPORT_BT) {
dev = esp_bt_hidh_dev_open(bda);
@ -159,6 +164,19 @@ esp_hidh_dev_t *esp_hidh_dev_open(esp_bd_addr_t bda, esp_hid_transport_t transpo
}
#endif /* CONFIG_BLUEDROID_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
esp_hidh_dev_t *esp_hidh_dev_open(uint8_t *bda, esp_hid_transport_t transport, uint8_t remote_addr_type)
{
if (esp_hidh_dev_get_by_bda(bda) != NULL) {
ESP_LOGE(TAG, "Already Connected");
return NULL;
}
esp_hidh_dev_t *dev = NULL;
dev = esp_ble_hidh_dev_open(bda, remote_addr_type);
return dev;
}
#endif /* CONFIG_BT_NIMBLE_ENABLED */
esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev)
{
esp_err_t ret = ESP_OK;
@ -329,6 +347,14 @@ const uint8_t *esp_hidh_dev_bda_get(esp_hidh_dev_t *dev)
esp_hidh_dev_unlock(dev);
}
#endif /* CONFIG_BLUEDROID_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
if (esp_hidh_dev_exists(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->bda;
esp_hidh_dev_unlock(dev);
}
#endif /* CONFIG_BT_NIMBLE_ENABLED */
return ret;
}
@ -823,3 +849,82 @@ void esp_hidh_post_process_event_handler(void *event_handler_arg, esp_event_base
}
}
#endif /* CONFIG_BLUEDROID_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
esp_hidh_dev_t *esp_hidh_dev_get_by_bda(uint8_t *bda)
{
esp_hidh_dev_t * d = NULL;
lock_devices();
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
if (memcmp(bda, d->bda, sizeof(uint8_t) * 6) == 0) {
unlock_devices();
return d;
}
}
unlock_devices();
return NULL;
}
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id)
{
esp_hidh_dev_t * d = NULL;
lock_devices();
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
if (d->ble.conn_id == conn_id) {
unlock_devices();
return d;
}
}
unlock_devices();
return NULL;
}
/**
* The deep copy data append the end of the esp_hidh_event_data_t, move the data pointer to the correct address. This is
* a workaround way, it's better to use flexible array in the interface.
*/
void esp_hidh_preprocess_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id,
void *event_data)
{
esp_hidh_event_t event = (esp_hidh_event_t)event_id;
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
switch (event) {
case ESP_HIDH_INPUT_EVENT:
if (param->input.length && param->input.data) {
param->input.data = (uint8_t *)param + sizeof(esp_hidh_event_data_t);
}
break;
case ESP_HIDH_FEATURE_EVENT:
if (param->feature.length && param->feature.data) {
param->feature.data = (uint8_t *)param + sizeof(esp_hidh_event_data_t);
}
break;
default:
break;
}
}
void esp_hidh_post_process_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id,
void *event_data)
{
esp_hidh_event_t event = (esp_hidh_event_t)event_id;
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
switch (event) {
case ESP_HIDH_OPEN_EVENT:
if (param->open.status != ESP_OK) {
esp_hidh_dev_t *dev = param->open.dev;
if (dev) {
esp_hidh_dev_free_inner(dev);
}
}
break;
case ESP_HIDH_CLOSE_EVENT:
esp_hidh_dev_free_inner(param->close.dev);
break;
default:
break;
}
}
#endif /* CONFIG_BT_NIMBLE_ENABLED */

View File

@ -0,0 +1,710 @@
/*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdbool.h>
#include "ble_hidd.h"
#include "esp_hidd_private.h"
#include "esp_log.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
#include "nimble/nimble_opt.h"
#include "host/ble_hs.h"
#include "host/ble_gap.h"
#include "host/ble_hs_adv.h"
#include "host/ble_hs_hci.h"
#include "host/ble_att.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "services/bas/ble_svc_bas.h"
#include "services/hid/ble_svc_hid.h"
#include "services/dis/ble_svc_dis.h"
#include "services/sps/ble_svc_sps.h"
#if CONFIG_BT_NIMBLE_HID_SERVICE
static const char *TAG = "NIMBLE_HIDD";
#define BLE_SVC_BAS_UUID16 0x180F
typedef struct esp_ble_hidd_dev_s esp_ble_hidd_dev_t;
// there can be only one BLE HID device
static esp_ble_hidd_dev_t *s_dev = NULL;
typedef hidd_report_item_t hidd_le_report_item_t;
typedef struct {
esp_hid_raw_report_map_t reports_map;
uint8_t reports_len;
hidd_le_report_item_t *reports;
uint16_t hid_svc;
uint16_t hid_control_handle;
uint16_t hid_protocol_handle;
} hidd_dev_map_t;
struct esp_ble_hidd_dev_s {
esp_hidd_dev_t *dev;
esp_event_loop_handle_t event_loop_handle;
esp_hid_device_config_t config;
uint16_t appearance;
bool connected;
uint16_t conn_id;
uint8_t control; // 0x00 suspend, 0x01 suspend off
uint8_t protocol; // 0x00 boot, 0x01 report
uint16_t bat_svc_handle;
uint16_t info_svc_handle;
struct ble_gatt_svc hid_incl_svc;
uint16_t bat_level_handle;
uint8_t pnp[7]; /* something related to device info service */
hidd_dev_map_t *devices;
uint8_t devices_len;
};
// HID Information characteristic value
static const uint8_t hidInfo[4] = {
0x11, 0x01, // bcdHID (USB HID version)
0x00, // bCountryCode
ESP_HID_FLAGS_REMOTE_WAKE | ESP_HID_FLAGS_NORMALLY_CONNECTABLE // Flags
};
static int create_hid_db(int device_index)
{
int rc = 0;
struct ble_svc_hid_params hparams = {0};
int report_mode_rpts = 0;
/* fill hid info */
memcpy(&hparams.hid_info, hidInfo, sizeof hparams.hid_info);
/* fill report map */
memcpy(&hparams.report_map, (uint8_t *)s_dev->devices[device_index].reports_map.data, s_dev->devices[device_index].reports_map.len);
hparams.report_map_len = s_dev->devices[device_index].reports_map.len;
hparams.external_rpt_ref = BLE_SVC_BAS_UUID16;
/* fill protocol mode */
hparams.proto_mode_present = 1;
hparams.proto_mode = s_dev->protocol;
for (uint8_t i = 0; i < s_dev->devices[device_index].reports_len; i++) {
hidd_le_report_item_t *report = &s_dev->devices[device_index].reports[i];
if (report->protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT) {
/* only consider report mode reports, all boot mode reports will be registered by default */
if (report->report_type == ESP_HID_REPORT_TYPE_INPUT) {
/* Input Report */
hparams.rpts[report_mode_rpts].type = ESP_HID_REPORT_TYPE_INPUT;
} else if (report->report_type == ESP_HID_REPORT_TYPE_OUTPUT) {
/* Output Report */
hparams.rpts[report_mode_rpts].type = ESP_HID_REPORT_TYPE_OUTPUT;
} else {
/* Feature Report */
hparams.rpts[report_mode_rpts].type = ESP_HID_REPORT_TYPE_FEATURE;
}
hparams.rpts[report_mode_rpts].id = report->report_id;
report_mode_rpts++;
} else {
if (report->report_type == ESP_HID_REPORT_TYPE_INPUT) {
/* Boot mode reports */
if (report->usage == ESP_HID_USAGE_KEYBOARD) { //Boot Keyboard Input
hparams.kbd_inp_present = 1;
} else { //Boot Mouse Input
hparams.mouse_inp_present = 1;
}
} else { //Boot Keyboard Output
hparams.kbd_out_present = 1;
}
}
}
hparams.rpts_len = report_mode_rpts;
/* Add service */
rc = ble_svc_hid_add(hparams);
if(rc != 0) {
return rc;
}
return rc;
}
static int ble_hid_create_info_db() {
int rc;
rc = 0;
ble_svc_dis_init();
uint8_t pnp_val[7] = {
0x02, //0x1=BT, 0x2=USB
s_dev->config.vendor_id & 0xFF, (s_dev->config.vendor_id >> 8) & 0xFF, //VID
s_dev->config.product_id & 0xFF, (s_dev->config.product_id >> 8) & 0xFF, //PID
s_dev->config.version & 0xFF, (s_dev->config.version >> 8) & 0xFF //VERSION
};
memcpy(s_dev->pnp, pnp_val, 7);
ble_svc_dis_pnp_id_set((char *)s_dev->pnp);
if (s_dev->config.manufacturer_name && s_dev->config.manufacturer_name[0]) {
rc = ble_svc_dis_manufacturer_name_set(s_dev->config.manufacturer_name);
}
if (s_dev->config.serial_number && s_dev->config.serial_number[0]) {
rc = ble_svc_dis_serial_number_set(s_dev->config.serial_number);
}
return rc;
}
static int nimble_hid_start_gatts(void)
{
int rc = ESP_OK;
ble_svc_gap_init();
ble_svc_gatt_init();
ble_svc_sps_init(0, 0); // initialize with 0
ble_svc_bas_init();
ble_hid_create_info_db();
for (uint8_t d = 0; d < s_dev->devices_len; d++) {
rc = create_hid_db(d);
if(rc != 0) {
return rc;
}
}
/* init the hid svc */
ble_svc_hid_init();
return rc;
}
static int nimble_hid_stop_gatts(esp_ble_hidd_dev_t *dev)
{
int rc = ESP_OK;
/* stop gatt database */
ble_gatts_stop();
return rc;
}
/* Identify the reports using the report map */
static int ble_hid_init_config(esp_ble_hidd_dev_t *dev, const esp_hid_device_config_t *config)
{
memset((uint8_t *)(&dev->config), 0, sizeof(esp_hid_device_config_t));
dev->config.vendor_id = config->vendor_id;
dev->config.product_id = config->product_id;
dev->config.version = config->version;
if (config->device_name != NULL) {
dev->config.device_name = strdup(config->device_name);
}
if (config->manufacturer_name != NULL) {
dev->config.manufacturer_name = strdup(config->manufacturer_name);
}
if (config->serial_number != NULL) {
dev->config.serial_number = strdup(config->serial_number);
}
dev->appearance = ESP_HID_APPEARANCE_GENERIC;
if (config->report_maps_len) {
dev->devices = (hidd_dev_map_t *)malloc(config->report_maps_len * sizeof(hidd_dev_map_t));
if (dev->devices == NULL) {
ESP_LOGE(TAG, "devices malloc(%d) failed", config->report_maps_len);
return ESP_FAIL;
}
memset(dev->devices, 0, config->report_maps_len * sizeof(hidd_dev_map_t));
dev->devices_len = config->report_maps_len;
for (uint8_t d = 0; d < dev->devices_len; d++) {
//raw report map
uint8_t *map = (uint8_t *)malloc(config->report_maps[d].len);
if (map == NULL) {
ESP_LOGE(TAG, "report map malloc(%d) failed", config->report_maps[d].len);
return ESP_FAIL;
}
memcpy(map, config->report_maps[d].data, config->report_maps[d].len);
dev->devices[d].reports_map.data = (const uint8_t *)map;
dev->devices[d].reports_map.len = config->report_maps[d].len;
esp_hid_report_map_t *rmap = esp_hid_parse_report_map(config->report_maps[d].data, config->report_maps[d].len);
if (rmap == NULL) {
ESP_LOGE(TAG, "hid_parse_report_map[%d](%d) failed", d, config->report_maps[d].len);
return ESP_FAIL;
}
dev->appearance = rmap->appearance;
dev->devices[d].reports_len = rmap->reports_len;
dev->devices[d].reports = (hidd_le_report_item_t *)malloc(rmap->reports_len * sizeof(hidd_le_report_item_t));
if (dev->devices[d].reports == NULL) {
ESP_LOGE(TAG, "reports malloc(%d) failed", rmap->reports_len * sizeof(hidd_le_report_item_t));
free(rmap);
return ESP_FAIL;
}
for (uint8_t r = 0; r < rmap->reports_len; r++) {
dev->devices[d].reports[r].map_index = d;
dev->devices[d].reports[r].report_id = rmap->reports[r].report_id;
dev->devices[d].reports[r].protocol_mode = rmap->reports[r].protocol_mode;
dev->devices[d].reports[r].report_type = rmap->reports[r].report_type;
dev->devices[d].reports[r].usage = rmap->reports[r].usage;
dev->devices[d].reports[r].value_len = rmap->reports[r].value_len;
}
free(rmap->reports);
free(rmap);
}
}
return ESP_OK;
}
static int ble_hid_free_config(esp_ble_hidd_dev_t *dev)
{
for (uint8_t d = 0; d < dev->devices_len; d++) {
free((void *)dev->devices[d].reports);
free((void *)dev->devices[d].reports_map.data);
}
free((void *)dev->devices);
free((void *)dev->config.device_name);
free((void *)dev->config.manufacturer_name);
free((void *)dev->config.serial_number);
if (dev->event_loop_handle != NULL) {
esp_event_loop_delete(dev->event_loop_handle);
}
return ESP_OK;
}
static int nimble_hidd_dev_deinit(void *devp) {
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
if (!s_dev) {
ESP_LOGE(TAG, "HID device profile already uninitialized");
return ESP_OK;
}
if (s_dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
return ESP_FAIL;
}
s_dev = NULL;
nimble_hid_stop_gatts(dev);
esp_event_post_to(dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_STOP_EVENT, NULL, 0, portMAX_DELAY);
ble_hid_free_config(dev);
free(dev);
return ESP_OK;
}
static bool nimble_hidd_dev_connected(void *devp)
{
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
return (dev != NULL && s_dev == dev && dev->connected);
}
static int nimble_hidd_dev_battery_set(void *devp, uint8_t level)
{
int rc;
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
if (!dev || s_dev != dev) {
return ESP_FAIL;
}
if (!dev->connected) {
/* Return success if not yet connected */
return ESP_OK;
}
rc = ble_svc_bas_battery_level_set(level);
if (rc) {
ESP_LOGE(TAG, "esp_ble_gatts_send_notify failed: %d", rc);
return ESP_FAIL;
}
return ESP_OK;
}
/* if mode is NULL, find the first matching report */
static hidd_le_report_item_t* find_report(uint8_t id, uint8_t type, uint8_t *mode) {
hidd_le_report_item_t *rpt;
for (uint8_t d = 0; d < s_dev->devices_len; d++) {
for (uint8_t i = 0; i < s_dev->devices[d].reports_len; i++) {
rpt = &s_dev->devices[d].reports[i];
if(rpt->report_id == id && rpt->report_type == type && (!mode || (mode && *mode == rpt->protocol_mode))) {
return rpt;
}
}
}
return NULL;
}
static hidd_le_report_item_t* find_report_by_usage_and_type(uint8_t usage, uint8_t type, uint8_t *mode) {
hidd_le_report_item_t *rpt;
for (uint8_t d = 0; d < s_dev->devices_len; d++) {
for (uint8_t i = 0; i < s_dev->devices[d].reports_len; i++) {
rpt = &s_dev->devices[d].reports[i];
if(rpt->usage == usage && rpt->report_type == type && (!mode || (mode && *mode == rpt->protocol_mode))) {
return rpt;
}
}
}
return NULL;
}
static int nimble_hidd_dev_input_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length)
{
hidd_le_report_item_t *p_rpt;
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
int rc;
struct os_mbuf *om;
if (!dev || s_dev != dev) {
return ESP_FAIL;
}
if (!dev->connected) {
ESP_LOGE(TAG, "%s Device Not Connected", __func__);
return ESP_FAIL;
}
/* check the protocol mode */
/* as the protocol mode is always present, its safe to read the characteristic */
rc = ble_att_svr_read_local(s_dev->devices[index].hid_protocol_handle, &om);
if(rc != 0) {
ESP_LOGE(TAG, "Unable to fetch protocol_mode\n");
return ESP_FAIL;
}
rc = ble_hs_mbuf_to_flat(om, &dev->protocol, sizeof(dev->protocol), NULL);
if(rc != 0) {
return ESP_FAIL;
}
/* free the mbuf */
os_mbuf_free_chain(om);
om = NULL;
p_rpt = find_report(id, ESP_HID_REPORT_TYPE_INPUT, &dev->protocol);
assert(p_rpt != NULL);
om = ble_hs_mbuf_from_flat((void*)data, length);
assert(om != NULL);
/* NOTE : om is freed by stack */
rc = ble_att_svr_write_local(p_rpt->handle, om);
if (rc != 0) {
ESP_LOGE(TAG, "Write Input Report Failed: %d", rc);
return ESP_FAIL;
}
return ESP_OK;
}
static int nimble_hidd_dev_feature_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length)
{
/* This function is a no-op for now */
hidd_le_report_item_t *p_rpt;
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
int rc;
struct os_mbuf *om;
if (!dev || s_dev != dev) {
return ESP_FAIL;
}
if (!dev->connected) {
ESP_LOGE(TAG, "%s Device Not Connected", __func__);
return ESP_FAIL;
}
/* check the protocol mode */
/* as the protocol mode is always present, its safe to read the characteristic */
rc = ble_att_svr_read_local(s_dev->devices[index].hid_protocol_handle, &om);
if(rc != 0) {
ESP_LOGE(TAG, "Unable to fetch protocol_mode\n");
return ESP_FAIL;
}
rc = ble_hs_mbuf_to_flat(om, &dev->protocol, sizeof(dev->protocol), NULL);
if(rc != 0) {
return ESP_FAIL;
}
/* free the mbuf */
os_mbuf_free_chain(om);
om = NULL;
p_rpt = find_report(id, ESP_HID_REPORT_TYPE_FEATURE, &dev->protocol);
assert(p_rpt != NULL);
om = ble_hs_mbuf_from_flat((void*)data, length);
assert(om != NULL);
/* NOTE : om is freed by stack*/
rc = ble_att_svr_write_local(p_rpt->handle, om);
if (rc != 0) {
ESP_LOGE(TAG, "Set Feature Report Failed: %d", rc);
return ESP_FAIL;
}
return ESP_OK;
}
static int nimble_hidd_dev_event_handler_register(void *devp, esp_event_handler_t callback, esp_hidd_event_t event)
{
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
if (!dev || s_dev != dev) {
return ESP_FAIL;
}
return esp_event_handler_register_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback, dev->dev);
}
static int esp_ble_hidd_dev_event_handler_unregister(void *devp, esp_event_handler_t callback, esp_hidd_event_t event)
{
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
if (!dev || s_dev != dev) {
return ESP_FAIL;
}
return esp_event_handler_unregister_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback);
}
static void ble_hidd_dev_free(void)
{
if (s_dev) {
ble_hid_free_config(s_dev);
free(s_dev);
s_dev = NULL;
}
}
static int nimble_hid_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
struct os_mbuf *om;
uint8_t data;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
ESP_LOGD(TAG, "connection %s; status=%d",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
/* save connection handle */
s_dev->connected = true;
s_dev->conn_id = event->connect.conn_handle;
esp_hidd_event_data_t cb_param = {
.connect.dev = s_dev->dev,
.connect.status = event->connect.status
};
/* reset the protocol mode value */
data = ESP_HID_PROTOCOL_MODE_REPORT;
om = ble_hs_mbuf_from_flat(&data, 1);
if(om == NULL) {
ESP_LOGD(TAG, "No memory to allocate mbuf");
}
/* NOTE : om is freed by stack */
for(int i = 0; i < s_dev->devices_len; i++) {
rc = ble_att_svr_write_local(s_dev->devices[i].hid_protocol_handle, om);
if (rc != 0) {
ESP_LOGE(TAG, "Write on Protocol Mode Failed: %d", rc);
}
}
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_CONNECT_EVENT,
&cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY);
return 0;
break;
case BLE_GAP_EVENT_DISCONNECT:
ESP_LOGD(TAG, "disconnect; reason=%d", event->disconnect.reason);
if (s_dev->connected) {
s_dev->connected = false;
esp_hidd_event_data_t cb_param = {0};
cb_param.disconnect.dev = s_dev->dev;
cb_param.disconnect.reason = event->disconnect.reason;
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_DISCONNECT_EVENT,
&cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY);
}
return 0;
}
return 0;
}
/** service index is used to identify the hid service instance
of the registered characteristic.
Assuming the first instance of the hid service is registered first.
Increment service index as the hid services get registered */
static int service_index = -1;
static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
hidd_le_report_item_t *rpt = NULL;
struct os_mbuf *om;
uint16_t uuid16;
uint16_t report_info;
uint8_t report_type, report_id;
uint16_t report_handle;
uint8_t protocol_mode;
int rc;
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
ESP_LOGD(TAG, "registered service %s with handle=%d",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
uuid16 = ble_uuid_u16(ctxt->svc.svc_def->uuid);
if(uuid16 == BLE_SVC_HID_UUID16) {
++service_index;
s_dev->devices[service_index].hid_svc = ctxt->svc.handle;
}
break;
case BLE_GATT_REGISTER_OP_CHR:
ESP_LOGD(TAG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
uuid16 = ble_uuid_u16(ctxt->chr.chr_def->uuid);
if(uuid16 == BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT) {
/* assuming this characteristic is from the last registered hid service */
s_dev->devices[service_index].hid_control_handle = ctxt->chr.val_handle;
}
if(uuid16 == BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE) {
/* assuming this characteristic is from the last registered hid service */
s_dev->devices[service_index].hid_protocol_handle = ctxt->chr.val_handle;
}
if(uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP) {
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode);
if(rpt == NULL) {
ESP_LOGE(TAG, "Unknown boot kbd input report registration");
return;
}
rpt->handle = ctxt->chr.val_handle;
}
if(uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT) {
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_OUTPUT, &protocol_mode);
if(rpt == NULL) {
ESP_LOGE(TAG, "Unknown boot kbd output report registration");
return;
}
rpt->handle = ctxt->chr.val_handle;
}
if(uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP) {
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_MOUSE, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode);
if(rpt == NULL) {
ESP_LOGE(TAG, "Unknown boot mouse input report registration");
return;
}
rpt->handle = ctxt->chr.val_handle;
}
break;
case BLE_GATT_REGISTER_OP_DSC:
ESP_LOGD(TAG, "registering descriptor %s with handle=%d",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
uuid16 = ble_uuid_u16(ctxt->dsc.dsc_def->uuid);
if(uuid16 == BLE_SVC_HID_DSC_UUID16_RPT_REF) {
rc = ble_att_svr_read_local(ctxt->dsc.handle, &om);
assert(rc == 0);
ble_hs_mbuf_to_flat(om, &report_info, sizeof report_info, NULL);
report_type = (uint8_t)((report_info & 0xFF00) >> 8);
report_id = report_info & 0x00FF;
report_handle = (*(uint16_t*)(ctxt->dsc.dsc_def->arg));
protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT;
rpt = find_report(report_id, report_type, &protocol_mode);
assert(rpt != NULL);
rpt->handle = report_handle;
/* free the mbuf */
os_mbuf_free_chain(om);
om = NULL;
}
break;
default:
assert(0);
break;
}
}
static void nimble_host_synced(void) {
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, NULL, 0, portMAX_DELAY);
}
void nimble_host_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static struct ble_gap_event_listener nimble_gap_event_listener;
esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_config_t *config, esp_event_handler_t callback)
{
int rc;
if (s_dev) {
ESP_LOGE(TAG, "HID device profile already initialized");
return ESP_FAIL;
}
s_dev = (esp_ble_hidd_dev_t *)calloc(1, sizeof(esp_ble_hidd_dev_t));
if (s_dev == NULL) {
ESP_LOGE(TAG, "HID device could not be allocated");
return ESP_FAIL;
}
// Reset the hid device target environment
s_dev->control = ESP_HID_CONTROL_EXIT_SUSPEND;
s_dev->protocol = ESP_HID_PROTOCOL_MODE_REPORT;
s_dev->event_loop_handle = NULL;
s_dev->dev = dev_p;
esp_event_loop_args_t event_task_args = {
.queue_size = 5,
.task_name = "ble_hidd_events",
.task_priority = uxTaskPriorityGet(NULL),
.task_stack_size = 2048,
.task_core_id = tskNO_AFFINITY
};
rc = esp_event_loop_create(&event_task_args, &s_dev->event_loop_handle);
if (rc != ESP_OK) {
ESP_LOGE(TAG, "HID device event loop could not be created");
ble_hidd_dev_free();
return rc;
}
rc = ble_hid_init_config(s_dev, config);
if (rc != ESP_OK) {
ble_hidd_dev_free();
return rc;
}
dev_p->dev = s_dev;
dev_p->connected = nimble_hidd_dev_connected;
dev_p->deinit = nimble_hidd_dev_deinit;
dev_p->battery_set = nimble_hidd_dev_battery_set;
dev_p->input_set = nimble_hidd_dev_input_set;
dev_p->feature_set = nimble_hidd_dev_feature_set;
dev_p->event_handler_register = nimble_hidd_dev_event_handler_register;
dev_p->event_handler_unregister = esp_ble_hidd_dev_event_handler_unregister;
rc = nimble_hidd_dev_event_handler_register(s_dev, esp_hidd_process_event_data_handler, ESP_EVENT_ANY_ID);
if (rc != ESP_OK) {
ble_hidd_dev_free();
return rc;
}
if (callback != NULL) {
rc = nimble_hidd_dev_event_handler_register(s_dev, callback, ESP_EVENT_ANY_ID);
if (rc != ESP_OK) {
ble_hidd_dev_free();
return rc;
}
}
ble_hs_cfg.reset_cb = nimble_host_reset;
ble_hs_cfg.sync_cb = nimble_host_synced;
ble_hs_cfg.gatts_register_cb = nimble_gatt_svr_register_cb;
rc = nimble_hid_start_gatts();
if(rc != ESP_OK) {
return rc;
}
ble_gap_event_listener_register(&nimble_gap_event_listener,
nimble_hid_gap_event, NULL);
return rc;
}
#endif // CONFIG_BT_NIMBLE_HID_SERVICE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
menu "HID Example Configuration"
choice EXAMPLE_HID_DEVICE_ROLE
prompt "HID Device Role"
depends on BT_NIMBLE_ENABLED
default EXAMPLE_MEDIA_ENABLE
help
Three Supported Roles for Device
- Media Device
- Keyboard
- Mouse
config EXAMPLE_MEDIA_ENABLE
select BT_NIMBLE_HID_SERVICE
bool "Enable Media Device"
config EXAMPLE_KBD_ENABLE
select BT_NIMBLE_HID_SERVICE
bool "Enable Keyboard Device"
config EXAMPLE_MOUSE_ENABLE
select BT_NIMBLE_HID_SERVICE
bool "Enable Mouse Device"
endchoice
config EXAMPLE_HID_DEVICE_ROLE
int
default 1 if EXAMPLE_MEDIA_ENABLE
default 2 if EXAMPLE_KBD_ENABLE
default 3 if EXAMPLE_MOUSE_ENABLE
endmenu

View File

@ -18,6 +18,12 @@
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"
#if CONFIG_BT_NIMBLE_ENABLED
#include "host/ble_hs.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#else
#include "esp_bt_defs.h"
#if CONFIG_BT_BLE_ENABLED
#include "esp_gap_ble_api.h"
@ -26,6 +32,7 @@
#endif
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#endif
#include "esp_hidd.h"
#include "esp_hid_gap.h"
@ -40,7 +47,7 @@ typedef struct
uint8_t *buffer;
} local_param_t;
#if CONFIG_BT_BLE_ENABLED
#if CONFIG_BT_BLE_ENABLED || CONFIG_BT_NIMBLE_ENABLED
static local_param_t s_ble_hid_param = {0};
const unsigned char mediaReportMap[] = {
@ -102,19 +109,287 @@ const unsigned char mediaReportMap[] = {
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
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
};
// 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_ble_hid_param.hid_dev, 0, 0, buffer, 4);
}
void ble_hid_demo_task_mouse(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);
}
}
#endif
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
#define CASE(a, b, c) \
case a: \
buffer[0] = b; \
buffer[2] = c; \
break;\
// USB keyboard codes
#define USB_HID_MODIFIER_LEFT_CTRL 0x01
#define USB_HID_MODIFIER_LEFT_SHIFT 0x02
#define USB_HID_MODIFIER_LEFT_ALT 0x04
#define USB_HID_MODIFIER_RIGHT_CTRL 0x10
#define USB_HID_MODIFIER_RIGHT_SHIFT 0x20
#define USB_HID_MODIFIER_RIGHT_ALT 0x40
#define USB_HID_SPACE 0x2C
#define USB_HID_DOT 0x37
#define USB_HID_NEWLINE 0x28
#define USB_HID_FSLASH 0x38
#define USB_HID_BSLASH 0x31
#define USB_HID_COMMA 0x36
#define USB_HID_DOT 0x37
const unsigned char keyboardReportMap[] = { //7 bytes input (modifiers, resrvd, keys*5), 1 byte output
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (Num Lock)
0x29, 0x05, // Usage Maximum (Kana)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x05, // Report Count (5)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x29, 0x65, // Usage Maximum (0x65)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
// 65 bytes
};
static void char_to_code(uint8_t *buffer, char ch)
{
// Check if lower or upper case
if(ch >= 'a' && ch <= 'z')
{
buffer[0] = 0;
// convert ch to HID letter, starting at a = 4
buffer[2] = (uint8_t)(4 + (ch - 'a'));
}
else if(ch >= 'A' && ch <= 'Z')
{
// Add left shift
buffer[0] = USB_HID_MODIFIER_LEFT_SHIFT;
// convert ch to lower case
ch = ch - ('A'-'a');
// convert ch to HID letter, starting at a = 4
buffer[2] = (uint8_t)(4 + (ch - 'a'));
}
else if(ch >= '0' && ch <= '9') // Check if number
{
buffer[0] = 0;
// convert ch to HID number, starting at 1 = 30, 0 = 39
if(ch == '0')
{
buffer[2] = 39;
}
else
{
buffer[2] = (uint8_t)(30 + (ch - '1'));
}
}
else // not a letter nor a number
{
switch(ch)
{
CASE(' ', 0, USB_HID_SPACE);
CASE('.', 0,USB_HID_DOT);
CASE('\n', 0, USB_HID_NEWLINE);
CASE('?', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_FSLASH);
CASE('/', 0 ,USB_HID_FSLASH);
CASE('\\', 0, USB_HID_BSLASH);
CASE('|', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_BSLASH);
CASE(',', 0, USB_HID_COMMA);
CASE('<', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_COMMA);
CASE('>', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_COMMA);
CASE('@', USB_HID_MODIFIER_LEFT_SHIFT, 31);
CASE('!', USB_HID_MODIFIER_LEFT_SHIFT, 30);
CASE('#', USB_HID_MODIFIER_LEFT_SHIFT, 32);
CASE('$', USB_HID_MODIFIER_LEFT_SHIFT, 33);
CASE('%', USB_HID_MODIFIER_LEFT_SHIFT, 34);
CASE('^', USB_HID_MODIFIER_LEFT_SHIFT,35);
CASE('&', USB_HID_MODIFIER_LEFT_SHIFT, 36);
CASE('*', USB_HID_MODIFIER_LEFT_SHIFT, 37);
CASE('(', USB_HID_MODIFIER_LEFT_SHIFT, 38);
CASE(')', USB_HID_MODIFIER_LEFT_SHIFT, 39);
CASE('-', 0, 0x2D);
CASE('_', USB_HID_MODIFIER_LEFT_SHIFT, 0x2D);
CASE('=', 0, 0x2E);
CASE('+', USB_HID_MODIFIER_LEFT_SHIFT, 39);
CASE(8, 0, 0x2A); // backspace
CASE('\t', 0, 0x2B);
default:
buffer[0] = 0;
buffer[2] = 0;
}
}
}
void send_keyboard(char c)
{
static uint8_t buffer[8] = {0};
char_to_code(buffer, c);
esp_hidd_dev_input_set(s_ble_hid_param.hid_dev, 0, 1, buffer, 8);
/* send the keyrelease event with sufficient delay */
vTaskDelay(50 / portTICK_PERIOD_MS);
memset(buffer, 0, sizeof(uint8_t) * 8);
esp_hidd_dev_input_set(s_ble_hid_param.hid_dev, 0, 1, buffer, 8);
}
void ble_hid_demo_task_kbd(void *pvParameters)
{
static const char* help_string = "########################################################################\n"\
"BT hid keyboard demo usage:\n"\
"########################################################################\n";
/* TODO : Add support for function keys and ctrl, alt, esc, etc. */
printf("%s\n", help_string);
char c;
while (1) {
c = fgetc(stdin);
if(c != 255) {
send_keyboard(c);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
#endif
static esp_hid_raw_report_map_t ble_report_maps[] = {
#if !CONFIG_BT_NIMBLE_ENABLED || CONFIG_EXAMPLE_HID_DEVICE_ROLE == 1
/* This block is compiled for bluedroid as well */
{
.data = mediaReportMap,
.len = sizeof(mediaReportMap)
}
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
{
.data = keyboardReportMap,
.len = sizeof(keyboardReportMap)
},
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
{
.data = mouseReportMap,
.len = sizeof(mouseReportMap)
},
#endif
};
static esp_hid_device_config_t ble_hid_config = {
.vendor_id = 0x16C0,
.product_id = 0x05DF,
.version = 0x0100,
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
.device_name = "ESP Keyboard",
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
.device_name = "ESP Mouse",
#else
.device_name = "ESP BLE HID2",
#endif
.manufacturer_name = "Espressif",
.serial_number = "1234567890",
.report_maps = ble_report_maps,
@ -272,6 +547,7 @@ void esp_hidd_send_consumer_value(uint8_t key_cmd, bool key_pressed)
return;
}
#if !CONFIG_BT_NIMBLE_ENABLED || CONFIG_EXAMPLE_HID_DEVICE_ROLE == 1
void ble_hid_demo_task(void *pvParameters)
{
static bool send_volum_up = false;
@ -290,6 +566,7 @@ void ble_hid_demo_task(void *pvParameters)
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
#endif
void ble_hid_task_start_up(void)
{
@ -297,8 +574,23 @@ void ble_hid_task_start_up(void)
// Task already exists
return;
}
#if !CONFIG_BT_NIMBLE_ENABLED
/* Executed for bluedroid */
xTaskCreate(ble_hid_demo_task, "ble_hid_demo_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3,
&s_ble_hid_param.task_hdl);
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 1
xTaskCreate(ble_hid_demo_task, "ble_hid_demo_task", 3 * 1024, NULL, configMAX_PRIORITIES - 3,
&s_ble_hid_param.task_hdl);
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
/* Nimble Specific */
xTaskCreate(ble_hid_demo_task_kbd, "ble_hid_demo_task_kbd", 3 * 1024, NULL, configMAX_PRIORITIES - 3,
&s_ble_hid_param.task_hdl);
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
/* Nimble Specific */
xTaskCreate(ble_hid_demo_task_mouse, "ble_hid_demo_task_mouse", 3 * 1024, NULL, configMAX_PRIORITIES - 3,
&s_ble_hid_param.task_hdl);
#endif
}
void ble_hid_task_shut_down(void)
@ -557,6 +849,18 @@ static void bt_hidd_event_callback(void *handler_args, esp_event_base_t base, in
}
#endif
#if CONFIG_BT_NIMBLE_ENABLED
void ble_hid_device_host_task(void *param)
{
ESP_LOGI(TAG, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */
nimble_port_run();
nimble_port_freertos_deinit();
}
void ble_store_config_init(void);
#endif
void app_main(void)
{
esp_err_t ret;
@ -575,14 +879,21 @@ void app_main(void)
ret = esp_hid_gap_init(HID_DEV_MODE);
ESP_ERROR_CHECK( ret );
#if CONFIG_BT_BLE_ENABLED
#if CONFIG_BT_BLE_ENABLED || CONFIG_BT_NIMBLE_ENABLED
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_KEYBOARD, ble_hid_config.device_name);
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_MOUSE, ble_hid_config.device_name);
#else
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_GENERIC, ble_hid_config.device_name);
#endif
ESP_ERROR_CHECK( ret );
#if CONFIG_BT_BLE_ENABLED
if ((ret = esp_ble_gatts_register_callback(esp_hidd_gatts_event_handler)) != ESP_OK) {
ESP_LOGE(TAG, "GATTS register callback failed: %d", ret);
return;
}
#endif
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));
@ -600,4 +911,15 @@ void app_main(void)
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
#if CONFIG_BT_NIMBLE_ENABLED
/* XXX Need to have template for store */
ble_store_config_init();
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Starting nimble task after gatts is initialized*/
ret = esp_nimble_enable(ble_hid_device_host_task);
if (ret) {
ESP_LOGE(TAG, "esp_nimble_enable failed: %d", ret);
}
#endif
}

View File

@ -15,17 +15,28 @@
#include "esp_hid_gap.h"
#if CONFIG_BT_NIMBLE_ENABLED
#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"
#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)
@ -37,6 +48,7 @@ static SemaphoreHandle_t ble_hidh_cb_semaphore = NULL;
#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"};
@ -64,6 +76,7 @@ const char *bt_gap_evt_str(uint8_t event)
}
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)
@ -106,6 +119,7 @@ const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
}
#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;
@ -118,6 +132,7 @@ void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
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)
@ -223,6 +238,7 @@ static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type
}
#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) {
@ -238,6 +254,7 @@ void print_uuid(esp_bt_uuid_t *uuid)
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)
@ -588,6 +605,7 @@ static esp_err_t start_ble_scan(uint32_t seconds)
return ret;
}
#if !CONFIG_BT_NIMBLE_ENABLED
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
{
@ -665,7 +683,7 @@ esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
return ret;
}
#endif
esp_err_t esp_hid_ble_gap_adv_start(void)
{
static esp_ble_adv_params_t hidd_adv_params = {
@ -679,11 +697,212 @@ esp_err_t esp_hid_ble_gap_adv_start(void)
return esp_ble_gap_start_advertising(&hidd_adv_params);
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
static struct ble_hs_adv_fields fields;
#define GATT_SVR_SVC_HID_UUID 0x1812
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
{
ble_uuid16_t *uuid16, *uuid16_1;
/**
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info).
* o Advertising tx power.
* o Device name.
* o 16-bit service UUIDs (HID).
*/
memset(&fields, 0, sizeof fields);
/* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported).
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
/* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assigning the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
uuid16 = (ble_uuid16_t *)malloc(sizeof(ble_uuid16_t));
uuid16_1 = (ble_uuid16_t[]) {
BLE_UUID16_INIT(GATT_SVR_SVC_HID_UUID)
};
memcpy(uuid16, uuid16_1, sizeof(ble_uuid16_t));
fields.uuids16 = uuid16;
fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1;
/* Initialize the security configuration */
ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY;
ble_hs_cfg.sm_bonding = 1;
ble_hs_cfg.sm_mitm = 1;
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);
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);
assert(rc == 0);
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;
}
/* 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;
@ -745,6 +964,43 @@ static esp_err_t init_low_level(uint8_t mode)
#endif /* CONFIG_BT_BLE_ENABLED */
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;
}
#endif
esp_err_t esp_hid_gap_init(uint8_t mode)
{
@ -785,6 +1041,7 @@ esp_err_t esp_hid_gap_init(uint8_t mode)
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) {
@ -825,3 +1082,4 @@ esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_resul
ble_scan_results = NULL;
return ESP_OK;
}
#endif

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -20,6 +20,8 @@
#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
@ -28,9 +30,11 @@
#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"
@ -42,6 +46,7 @@
extern "C" {
#endif
#if !CONFIG_BT_NIMBLE_ENABLED
typedef struct esp_hidh_scan_result_s {
struct esp_hidh_scan_result_s *next;
@ -62,16 +67,17 @@ typedef struct esp_hidh_scan_result_s {
};
} esp_hid_scan_result_t;
esp_err_t esp_hid_gap_init(uint8_t mode);
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_ble_gap_adv_init(uint16_t appearance, const char *device_name);
esp_err_t esp_hid_ble_gap_adv_start(void);
void print_uuid(esp_bt_uuid_t *uuid);
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,9 @@
menu "HID Example Configuration"
config EXAMPLE_HID_HOST_ENABLED
bool "Enable Example HID Host"
depends on BT_NIMBLE_ENABLED
default y
select BT_NIMBLE_HID_SERVICE
help
This enables Nimble HID Host
endmenu

View File

@ -14,6 +14,15 @@
#include "freertos/semphr.h"
#include "esp_hid_gap.h"
#if CONFIG_BT_NIMBLE_ENABLED
#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"
#define BLE_HID_SVC_UUID 0x1812 /* HID Service*/
#endif
static const char *TAG = "ESP_HID_GAP";
@ -40,6 +49,7 @@ static SemaphoreHandle_t ble_hidh_cb_semaphore = NULL;
#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"};
@ -67,7 +77,7 @@ const char *bt_gap_evt_str(uint8_t event)
}
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)
{
@ -136,6 +146,19 @@ static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_r
}
#endif /* (CONFIG_BT_HID_HOST_ENABLED || CONFIG_BT_BLE_ENABLED) */
#if (CONFIG_BT_NIMBLE_ENABLED)
static esp_hid_scan_result_t *find_scan_result(const uint8_t *bda, esp_hid_scan_result_t *results)
{
esp_hid_scan_result_t *r = results;
while (r) {
if (memcmp(bda, r->bda, sizeof(r->bda)) == 0) {
return r;
}
r = r->next;
}
return NULL;
}
#endif
#if CONFIG_BT_HID_HOST_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)
{
@ -226,6 +249,43 @@ static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
static void add_ble_scan_result(const uint8_t *bda, uint8_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(r->bda));
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) {
@ -572,6 +632,7 @@ static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_p
break;
}
}
#endif
static esp_err_t init_ble_gap(void)
{
@ -705,6 +766,252 @@ esp_err_t esp_hid_ble_gap_adv_start(void)
* 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
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 void handle_ble_device_result(const struct ble_gap_disc_desc *disc)
{
int rc;
struct ble_hs_adv_fields fields;
uint16_t appearance;
uint8_t adv_name[BLE_HS_ADV_MAX_SZ];
uint8_t adv_name_len = 0;
appearance = 0; /* silent warnings for now */
rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
if(rc != 0) {
return;
}
if (fields.name != NULL) {
assert(fields.name_len < sizeof adv_name - 1);
memcpy(adv_name, fields.name, fields.name_len);
adv_name[fields.name_len] = '\0';
adv_name_len = fields.name_len;
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
fields.name_is_complete ? "" : "in", adv_name);
}
if (fields.appearance_is_present) {
MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields.appearance);
appearance = fields.appearance;
}
for (int i = 0; i < fields.num_uuids16; i++) {
if (ble_uuid_u16(&fields.uuids16[i].u) == BLE_HID_SVC_UUID &&
((adv_name_len > 0 && memcmp("ESP BLE HID2", adv_name, adv_name_len) == 0) ||
(adv_name_len > 0 && memcmp("ESP Mouse", adv_name, adv_name_len) == 0) ||
(adv_name_len > 0 && memcmp("ESP Keyboard", adv_name, adv_name_len) == 0))) {
add_ble_scan_result(disc->addr.val, disc->addr.type, appearance, adv_name, adv_name_len, disc->rssi);
break;
}
}
}
#endif
#if CONFIG_BT_NIMBLE_ENABLED
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_DISC:
handle_ble_device_result(&event->disc);
if (rc != 0) {
return 0;
}
/* An advertisment report was received during GAP discovery. */
return 0;
break;
case BLE_GAP_EVENT_DISC_COMPLETE:
MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
event->disc_complete.reason);
SEND_BLE_CB();
return 0;
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);
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);
assert(rc == 0);
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;
}
static esp_err_t start_nimble_scan(uint32_t seconds)
{
uint8_t own_addr_type;
struct ble_gap_disc_params disc_params;
int rc;
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return rc;
}
/* Tell the controller to filter duplicates; we don't want to process
* repeated advertisements from the same device.
*/
disc_params.filter_duplicates = 1;
/**
* Perform active scan.
*/
disc_params.passive = 0;
/* Use defaults for the rest of the parameters. */
disc_params.itvl = 0x50;
disc_params.window = 0x30;
disc_params.filter_policy = 0;
disc_params.limited = 0;
rc = ble_gap_disc(own_addr_type, seconds * 1000, &disc_params,
nimble_hid_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
rc);
}
return rc;
}
#else
static esp_err_t init_low_level(uint8_t mode)
{
esp_err_t ret;
@ -766,6 +1073,7 @@ static esp_err_t init_low_level(uint8_t mode)
#endif /* CONFIG_BT_BLE_ENABLED */
return ret;
}
#endif
esp_err_t esp_hid_gap_init(uint8_t mode)
{
@ -820,6 +1128,14 @@ esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_resul
return ESP_FAIL;
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
if (start_nimble_scan(seconds) == ESP_OK) {
WAIT_BLE_CB();
} else {
return ESP_FAIL;
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_HID_HOST_ENABLED
if (start_bt_scan(seconds) == ESP_OK) {

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -21,6 +21,8 @@
#endif
#elif CONFIG_BT_BLE_ENABLED
#define HID_HOST_MODE HIDH_BLE_MODE
#elif CONFIG_BT_NIMBLE_ENABLED
#define HID_HOST_MODE HIDH_BLE_MODE
#else
#define HID_HOST_MODE HIDH_IDLE_MODE
#endif
@ -29,9 +31,11 @@
#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"
@ -39,19 +43,28 @@
#include "esp_gap_ble_api.h"
#endif
#if CONFIG_BT_NIMBLE_ENABLED
#include "nimble/ble.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_hidh_scan_result_s {
struct esp_hidh_scan_result_s *next;
#if CONFIG_BT_NIMBLE_ENABLED
uint8_t bda[6];
#else
esp_bd_addr_t bda;
#endif
const char *name;
int8_t rssi;
esp_hid_usage_t usage;
esp_hid_transport_t transport; //BT, BLE or USB
union {
#if !CONFIG_BT_NIMBLE_ENABLED
struct {
esp_bt_cod_t cod;
esp_bt_uuid_t uuid;
@ -60,6 +73,12 @@ typedef struct esp_hidh_scan_result_s {
esp_ble_addr_type_t addr_type;
uint16_t appearance;
} ble;
#else
struct {
uint8_t addr_type;
uint16_t appearance;
} ble;
#endif
};
} esp_hid_scan_result_t;
@ -70,8 +89,10 @@ void esp_hid_scan_results_free(esp_hid_scan_result_t *results);
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);
#if !CONFIG_BT_NIMBLE_ENABLED
void print_uuid(esp_bt_uuid_t *uuid);
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
#endif
#ifdef __cplusplus
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -17,12 +17,34 @@
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"
#if CONFIG_BT_NIMBLE_ENABLED
#include "host/ble_hs.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#else
#include "esp_bt_defs.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#endif
#if CONFIG_BT_NIMBLE_ENABLED
#include "host/ble_hs.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#define ESP_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x"
#define ESP_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
#else
#include "esp_bt_defs.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#endif
#include "esp_hidh.h"
#include "esp_hid_gap.h"
@ -99,6 +121,13 @@ void hid_demo_task(void *pvParameters)
printf("ADDR_TYPE: '%s', ", ble_addr_type_str(r->ble.addr_type));
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_NIMBLE_ENABLED
if (r->transport == ESP_HID_TRANSPORT_BLE) {
cr = r;
printf("APPEARANCE: 0x%04x, ", r->ble.appearance);
printf("ADDR_TYPE: '%d', ", r->ble.addr_type);
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_HID_HOST_ENABLED
if (r->transport == ESP_HID_TRANSPORT_BT) {
cr = r;
@ -123,6 +152,17 @@ void hid_demo_task(void *pvParameters)
vTaskDelete(NULL);
}
#if CONFIG_BT_NIMBLE_ENABLED
void ble_hid_host_task(void *param)
{
ESP_LOGI(TAG, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */
nimble_port_run();
nimble_port_freertos_deinit();
}
void ble_store_config_init(void);
#endif
void app_main(void)
{
esp_err_t ret;
@ -148,5 +188,16 @@ void app_main(void)
};
ESP_ERROR_CHECK( esp_hidh_init(&config) );
#if CONFIG_BT_NIMBLE_ENABLED
/* XXX Need to have template for store */
ble_store_config_init();
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Starting nimble task after gatts is initialized*/
ret = esp_nimble_enable(ble_hid_host_task);
if (ret) {
ESP_LOGE(TAG, "esp_nimble_enable failed: %d", ret);
}
#endif
xTaskCreate(&hid_demo_task, "hid_task", 6 * 1024, NULL, 2, NULL);
}

View File

@ -416,13 +416,9 @@ components/esp_hid/include/esp_hidd_transport.h
components/esp_hid/include/esp_hidh.h
components/esp_hid/include/esp_hidh_bluedroid.h
components/esp_hid/include/esp_hidh_gattc.h
components/esp_hid/include/esp_hidh_transport.h
components/esp_hid/private/ble_hidd.h
components/esp_hid/private/ble_hidh.h
components/esp_hid/private/bt_hidd.h
components/esp_hid/private/bt_hidh.h
components/esp_hid/private/esp_hidd_private.h
components/esp_hid/src/esp_hid_common.c
components/esp_hid/test/hid_descriptor.h
components/esp_hid/test/test_esp_hid.c
components/esp_hw_support/include/esp_clk.h