Merge branch 'docs/update_esp_hid_device' into 'master'

[docs] fix documents for Bluetooth HID Device API reference and example

See merge request espressif/esp-idf!17558
pull/8968/head
Jiang Jiang Jian 2022-05-05 23:58:07 +08:00
commit 34211f898c
9 zmienionych plików z 493 dodań i 172 usunięć

Wyświetl plik

@ -16,86 +16,100 @@
extern "C" {
#endif
/* sub_class of hid device */
#define ESP_HID_CLASS_UNKNOWN (0x00<<2)
#define ESP_HID_CLASS_JOS (0x01<<2) /* joy stick */
#define ESP_HID_CLASS_GPD (0x02<<2) /* game pad */
#define ESP_HID_CLASS_RMC (0x03<<2) /* remote control */
#define ESP_HID_CLASS_SED (0x04<<2) /* sensing device */
#define ESP_HID_CLASS_DGT (0x05<<2) /* Digitizer tablet */
#define ESP_HID_CLASS_CDR (0x06<<2) /* card reader */
#define ESP_HID_CLASS_KBD (0x10<<2) /* keyboard */
#define ESP_HID_CLASS_MIC (0x20<<2) /* pointing device */
#define ESP_HID_CLASS_COM (0x30<<2) /* Combo keyboard/pointing */
/// subclass of hid device
#define ESP_HID_CLASS_UNKNOWN (0x00<<2) /*!< unknown HID device subclass */
#define ESP_HID_CLASS_JOS (0x01<<2) /*!< joystick */
#define ESP_HID_CLASS_GPD (0x02<<2) /*!< game pad */
#define ESP_HID_CLASS_RMC (0x03<<2) /*!< remote control */
#define ESP_HID_CLASS_SED (0x04<<2) /*!< sensing device */
#define ESP_HID_CLASS_DGT (0x05<<2) /*!< digitizer tablet */
#define ESP_HID_CLASS_CDR (0x06<<2) /*!< card reader */
#define ESP_HID_CLASS_KBD (0x10<<2) /*!< keyboard */
#define ESP_HID_CLASS_MIC (0x20<<2) /*!< pointing device */
#define ESP_HID_CLASS_COM (0x30<<2) /*!< combo keyboard/pointing */
/**
* @brief HIDD handshake error
* @brief HIDD handshake result code
*/
typedef enum {
ESP_HID_PAR_HANDSHAKE_RSP_SUCCESS = 0,
ESP_HID_PAR_HANDSHAKE_RSP_NOT_READY = 1,
ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID = 2,
ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ = 3,
ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM = 4,
ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN = 14,
ESP_HID_PAR_HANDSHAKE_RSP_ERR_FATAL = 15
ESP_HID_PAR_HANDSHAKE_RSP_SUCCESS = 0, /*!< successful */
ESP_HID_PAR_HANDSHAKE_RSP_NOT_READY = 1, /*!< not ready, device is too busy to accept data */
ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID = 2, /*!< invalid report ID */
ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ = 3, /*!< device does not support the request */
ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM = 4, /*!< parameter value is out of range or inappropriate */
ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN = 14, /*!< device could not identify the error condition */
ESP_HID_PAR_HANDSHAKE_RSP_ERR_FATAL = 15, /*!< restart is essential to resume functionality */
} esp_hidd_handshake_error_t;
/**
* @brief HIDD report types
*/
typedef enum {
ESP_HIDD_REPORT_TYPE_OTHER = 0,
ESP_HIDD_REPORT_TYPE_INPUT,
ESP_HIDD_REPORT_TYPE_OUTPUT,
ESP_HIDD_REPORT_TYPE_FEATURE,
// special value for reports to be sent on INTR(INPUT is assumed)
ESP_HIDD_REPORT_TYPE_INTRDATA
ESP_HIDD_REPORT_TYPE_OTHER = 0, /*!< unknown report type */
ESP_HIDD_REPORT_TYPE_INPUT, /*!< input report */
ESP_HIDD_REPORT_TYPE_OUTPUT, /*!< output report */
ESP_HIDD_REPORT_TYPE_FEATURE, /*!< feature report */
ESP_HIDD_REPORT_TYPE_INTRDATA, /*!< special value for reports to be sent on interrupt channel, INPUT is assumed */
} esp_hidd_report_type_t;
/**
* @brief HIDD connection state
*/
typedef enum {
ESP_HIDD_CONN_STATE_CONNECTED,
ESP_HIDD_CONN_STATE_CONNECTING,
ESP_HIDD_CONN_STATE_DISCONNECTED,
ESP_HIDD_CONN_STATE_DISCONNECTING,
ESP_HIDD_CONN_STATE_UNKNOWN
ESP_HIDD_CONN_STATE_CONNECTED, /*!< HID connection established */
ESP_HIDD_CONN_STATE_CONNECTING, /*!< connection to remote Bluetooth device */
ESP_HIDD_CONN_STATE_DISCONNECTED, /*!< connection released */
ESP_HIDD_CONN_STATE_DISCONNECTING, /*!< disconnecting to remote Bluetooth device*/
ESP_HIDD_CONN_STATE_UNKNOWN, /*!< unknown connection state */
} esp_hidd_connection_state_t;
/**
* @brief HID device protocol modes
*/
typedef enum {
ESP_HIDD_REPORT_MODE = 0x00,
ESP_HIDD_BOOT_MODE = 0x01,
ESP_HIDD_UNSUPPORTED_MODE = 0xff
ESP_HIDD_REPORT_MODE = 0x00, /*!< Report Protocol Mode */
ESP_HIDD_BOOT_MODE = 0x01, /*!< Boot Protocol Mode */
ESP_HIDD_UNSUPPORTED_MODE = 0xff, /*!< unsupported */
} esp_hidd_protocol_mode_t;
/**
* @brief HID Boot Protocol report IDs
*/
typedef enum {
ESP_HIDD_BOOT_REPORT_ID_KEYBOARD = 1, /*!< report ID of Boot Protocol keyboard report */
ESP_HIDD_BOOT_REPORT_ID_MOUSE = 2, /*!< report ID of Boot Protocol mouse report */
} esp_hidd_boot_report_id_t;
/**
* @brief HIDD characteristics for SDP report
* @breif HID Boot Protocol report size including report ID
*/
enum {
ESP_HIDD_BOOT_REPORT_SIZE_KEYBOARD = 9, /*!< report size of Boot Protocol keyboard report */
ESP_HIDD_BOOT_REPORT_SIZE_MOUSE = 4, /*!< report size of Boot Protocol mouse report */
};
/**
* @brief HID device characteristics for SDP server
*/
typedef struct {
const char *name;
const char *description;
const char *provider;
uint8_t subclass;
uint8_t *desc_list;
int desc_list_len;
const char *name; /*!< service name */
const char *description; /*!< service description */
const char *provider; /*!< provider name */
uint8_t subclass; /*!< HID device subclass */
uint8_t *desc_list; /*!< HID descriptor list */
int desc_list_len; /*!< size in bytes of HID descriptor list */
} esp_hidd_app_param_t;
/**
* @brief HIDD Quality of Service parameters
* @brief HIDD Quality of Service parameters negotiated over L2CAP
*/
typedef struct {
uint8_t service_type;
uint32_t token_rate;
uint32_t token_bucket_size;
uint32_t peak_bandwidth;
uint32_t access_latency;
uint32_t delay_variation;
uint8_t service_type; /*!< the level of service, 0 indicates no traffic */
uint32_t token_rate; /*!< token rate in bytes per second, 0 indicates "don't care" */
uint32_t token_bucket_size; /*!< limit on the burstness of the application data */
uint32_t peak_bandwidth; /*!< bytes per second, value 0 indicates "don't care" */
uint32_t access_latency; /*!< maximum acceptable delay in microseconds */
uint32_t delay_variation; /*!< the difference in microseconds between the max and min delay */
} esp_hidd_qos_param_t;
/**

Wyświetl plik

@ -39,6 +39,7 @@ BT_DOCS = ['api-guides/blufi.rst',
CLASSIC_BT_DOCS = ['api-reference/bluetooth/classic_bt.rst',
'api-reference/bluetooth/esp_a2dp.rst',
'api-reference/bluetooth/esp_avrc.rst',
'api-reference/bluetooth/esp_hidd.rst',
'api-reference/bluetooth/esp_hf_defs.rst',
'api-reference/bluetooth/esp_hf_client.rst',
'api-reference/bluetooth/esp_hf_ag.rst',

Wyświetl plik

@ -152,6 +152,7 @@ api-reference/bluetooth/esp_a2dp
api-reference/bluetooth/esp_blufi
api-reference/bluetooth/bt_le
api-reference/bluetooth/esp_hf_defs
api-reference/bluetooth/esp_hidd
api-reference/bluetooth/esp_gap_bt
api-reference/bluetooth/esp_gatt_defs
api-reference/bluetooth/esp-ble-mesh

Wyświetl plik

@ -55,6 +55,7 @@ INPUT = \
$(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h \
$(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_hf_defs.h \
$(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_spp_api.h \
$(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h \
$(PROJECT_PATH)/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h \
$(PROJECT_PATH)/components/bt/include/esp32/include/esp_bt.h \
$(PROJECT_PATH)/components/console/esp_console.h \

Wyświetl plik

@ -11,3 +11,4 @@ CLASSIC BT
BT HFP Define <esp_hf_defs>
BT HFP Client <esp_hf_client>
BT HFP AG <esp_hf_ag>
BT HID DEVICE <esp_hidd>

Wyświetl plik

@ -0,0 +1,19 @@
Bluetooth HID Device API
========================
Overview
--------
A Bluetooth HID device is a device providing the service of human or other data input and output to and from a Bluetooth HID Host. Users can use the Bluetooth HID Device APIs to make devices like keyboards, mice, joysticks and so on.
Application Example
-------------------
Check :example:`bluetooth/bluedroid/classic_bt` folder in ESP-IDF examples, which contains the following application:
* This is an example of Bluetooth HID mouse device. The device running this example can be discovered and connected by a Bluetooth HID Host device such as a PC, and the pointer will move left and right after HID connection is established - :example:`bluetooth/bluedroid/classic_bt/bt_hid_mouse_device`
API Reference
-------------
.. include-build-file:: inc/esp_hidd_api.inc

Wyświetl plik

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/bluetooth/esp_hidd.rst

Wyświetl plik

@ -1,11 +1,264 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# ESP-IDF HID Device over Bluetooth Classic Demo
# Bluetooth HID Device example
Demo of HID Device over Bluetooth Classic.
This example aims to show how to implement a Bluetooth HID device using the APIs provided by Classic Bluetooth HID profile.
This turns the device into a mouse, but can be altered to be any kind of HID device.
This example simulates a Bluetooth HID mouse device that periodically sends report to remote Bluetooth HID host after connection. The report indicates a horizontally moving pointer and can be observed on the display on the HID host side. If you want to build an HID device, this can be your first example to look at.
After loading the code, connect with a computer to a device broadcasting
as "HID Mouse Example". The mouse should move left and right while they are connected.
## How to use example
### Hardware Required
* This example is able to run on any commonly available ESP32 development board, e.g. ESP32-DevKitC.
* This example is supposed to connect to a Classic Bluetooth HID Host device, e.g. laptop or tablet.
### Configure the project
```
idf.py menuconfig
```
* Check and enable Classic Bluetooth and Classic BT HID Device under Component config --> Bluetooth --> Bluedroid Options
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The following log will be shown on the IDF monitor console:
```
I (572) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (663) BTDM_INIT: BT controller compile version [5688ed5]
I (663) system_api: Base MAC address is not set
I (663) system_api: read default base MAC address from EFUSE
I (673) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
W (683) phy_init: failed to load RF calibration data (0xffffffff), falling back to full calibration
I (1533) app_main: setting device name
I (1533) esp_bt_gap_cb: event: 10
I (1543) app_main: setting cod major, peripheral
I (3543) app_main: register hid device callback
I (3543) app_main: starting hid device
I (3543) esp_bt_hidd_cb: setting hid parameters
I (3543) esp_bt_gap_cb: event: 10
I (3543) esp_bt_hidd_cb: setting hid parameters success!
I (3553) esp_bt_hidd_cb: setting to connectable, discoverable
I (3563) bt_address: my bluetooth address is 78:E3:6D:CD:02:2A
I (3563) app_main: exiting
```
The messages show the successful initialization of Bluetooth stack and HID application. ESP32 will become discoverable with the Bluetooth device name as "HID Mouse Example", by nearby Bluetooth HID Host device.
Connect to ESP32 on the HID Host side, then finish bonding. After that the HID connection will be established. IDF monitor console will continue to print messages like:
```
I (50663) esp_bt_gap_cb: authentication success: privacy_k65
I (50663) esp_bt_gap_cb: 64 a2 f9 69 57 a4
W (51443) BT_APPL: new conn_srvc id:20, app_id:1
I (51443) esp_bt_hidd_cb: connected to 64:a2:f9:69:57:a4
I (51443) esp_bt_hidd_cb: making self non-discoverable and non-connectable.
I (51443) mouse_move_task: starting
I (51463) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
I (51493) esp_bt_gap_cb: ESP_BT_GAP_MODE_CHG_EVT mode:2
I (51503) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
I (51553) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
I (51603) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
I (51653) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
I (51703) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
I (51753) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
I (51803) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
```
ESP32 will generate and send HID mouse reports periodically. On the screen of HID Host, the cursor will move horizontally from left to right and then right to left, and so on so forth.
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
## Example Breakdown
### Initial settings for Bluetooth HID device profile
Bluetooth HID device requires the specific major and minor device type in the Class of Device (CoD), the following lines of source code performs the configuration of CoD:
```
void app_main(void) {
...
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);
...
}
```
Bluetooth HID device profile requires the information of service name, provide, device subclass, report descriptor for SDP server, as well as L2CAP QoS configurations from the application. Following lines in function `app_main` initialize these information fields:
```
void app_main(void) {
...
// Initialize HID SDP information and L2CAP parameters.
// to be used in the call of `esp_bt_hid_device_register_app` after profile initialization finishes
do {
s_local_param.app_param.name = "Mouse";
s_local_param.app_param.description = "Mouse Example";
s_local_param.app_param.provider = "ESP32";
s_local_param.app_param.subclass = ESP_HID_CLASS_MIC;
s_local_param.app_param.desc_list = hid_mouse_descriptor;
s_local_param.app_param.desc_list_len = hid_mouse_descriptor_len;
memset(&s_local_param.both_qos, 0, sizeof(esp_hidd_qos_param_t)); // don't set the qos parameters
} while (0);
// Report Protocol Mode is the default mode, according to Bluetooth HID specification
s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE;
ESP_LOGI(TAG, "register hid device callback");
esp_bt_hid_device_register_callback(esp_bt_hidd_cb);
ESP_LOGI(TAG, "starting hid device");
esp_bt_hid_device_init();
...
}
```
The information is set to global struct `s_local_param` and will be used upon successful profile initialization, i.e. reception of `ESP_HIDD_INIT_EVT` which is generated after the call of `esp_bt_hid_device_init()`:
```
void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
{
...
switch (event) {
case ESP_HIDD_INIT_EVT:
if (param->init.status == ESP_HIDD_SUCCESS) {
ESP_LOGI(TAG, "setting hid parameters");
esp_bt_hid_device_register_app(&s_local_param.app_param, &s_local_param.both_qos, &s_local_param.both_qos);
} else {
ESP_LOGE(TAG, "init hidd failed!");
}
break;
...
}
...
}
```
### Determination of HID Report Mode
There are two HID report modes: Report Protocol Mode and Boot Protocol Mode. The former is the default mode. The two report modes differ in the report contents and format. The example supports both of the two modes.
Report Mode requires report descriptor to describe the usage and format of the reports. For Bluetooth HID device, the report descriptor shall be provided in the SDP server, which can be discovered and used by remote HID Host.
Boot Mode only supports keyboards and mice, with pre-defined report formats. Therefore it does not require a report descriptor parser on the remote HID Host. It is originally used to simplify the design of PC BIOSs.
The following code lines set Report Protocol Mode as the default Report Mode:
```
void app_main(void) {
...
// Report Protocol Mode is the default mode, according to Bluetooth HID specification
s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE;
...
}
```
Report Mode can be choosen by remote HID Host through the SET_PROTOCOL request:
```
void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
{
...
switch (event) {
...
case ESP_HIDD_SET_PROTOCOL_EVT:
ESP_LOGI(TAG, "ESP_HIDD_SET_PROTOCOL_EVT");
if (param->set_protocol.protocol_mode == ESP_HIDD_BOOT_MODE) {
ESP_LOGI(TAG, " - boot protocol");
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
s_local_param.x_dir = -1;
xSemaphoreGive(s_local_param.mouse_mutex);
} else if (param->set_protocol.protocol_mode == ESP_HIDD_REPORT_MODE) {
ESP_LOGI(TAG, " - report protocol");
}
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
s_local_param.protocol_mode = param->set_protocol.protocol_mode;
xSemaphoreGive(s_local_param.mouse_mutex);
break;
....
}
....
}
```
### Report generation
The example simulates a mouse by creating a FreeRTOS task that periodically generates and sends the HID mouse report:
```
// move the mouse left and right
void mouse_move_task(void* pvParameters)
{
const char* TAG = "mouse_move_task";
ESP_LOGI(TAG, "starting");
for(;;) {
s_local_param.x_dir = 1;
int8_t step = 10;
for (int i = 0; i < 2; i++) {
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
s_local_param.x_dir *= -1;
xSemaphoreGive(s_local_param.mouse_mutex);
for (int j = 0; j < 100; j++) {
send_mouse_report(0, s_local_param.x_dir * step, 0, 0);
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
```
Function `send_mouse_report` is used to pack the information into a mouse HID report and sends it to HID Host, according to the Report Mode applied:
```
// send the buttons, change in x, and change in y
void send_mouse_report(uint8_t buttons, char dx, char dy, char wheel)
{
uint8_t report_id;
uint16_t report_size;
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
report_id = 0;
report_size = REPORT_PROTOCOL_MOUSE_REPORT_SIZE;
s_local_param.buffer[0] = buttons;
s_local_param.buffer[1] = dx;
s_local_param.buffer[2] = dy;
s_local_param.buffer[3] = wheel;
} else {
// Boot Mode
report_id = ESP_HIDD_BOOT_REPORT_ID_MOUSE;
report_size = ESP_HIDD_BOOT_REPORT_SIZE_MOUSE - 1;
s_local_param.buffer[0] = buttons;
s_local_param.buffer[1] = dx;
s_local_param.buffer[2] = dy;
}
esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, report_id, report_size, s_local_param.buffer);
xSemaphoreGive(s_local_param.mouse_mutex);
}
```

Wyświetl plik

@ -19,103 +19,24 @@
#include "freertos/task.h"
#include "freertos/semphr.h"
#define BOOT_PROTO_MOUSE_RPT_ID 0x02
typedef struct
{
#define REPORT_PROTOCOL_MOUSE_REPORT_SIZE (4)
#define REPORT_BUFFER_SIZE REPORT_PROTOCOL_MOUSE_REPORT_SIZE
typedef struct {
esp_hidd_app_param_t app_param;
esp_hidd_qos_param_t both_qos;
uint8_t protocol_mode;
SemaphoreHandle_t mouse_mutex;
TaskHandle_t mouse_task_hdl;
uint8_t buffer[4];
uint8_t buffer[REPORT_BUFFER_SIZE];
int8_t x_dir;
} local_param_t;
static local_param_t s_local_param = {0};
bool check_report_id_type(uint8_t report_id, uint8_t report_type)
{
bool ret = false;
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
do {
if (report_type != ESP_HIDD_REPORT_TYPE_INPUT) {
break;
}
if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
if (report_id == BOOT_PROTO_MOUSE_RPT_ID) {
ret = true;
break;
}
} else {
if (report_id == 0) {
ret = true;
break;
}
}
} while (0);
if (!ret) {
if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
} else {
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
}
}
xSemaphoreGive(s_local_param.mouse_mutex);
return ret;
}
// send the buttons, change in x, and change in y
void send_mouse(uint8_t buttons, char dx, char dy, char wheel)
{
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
s_local_param.buffer[0] = buttons;
s_local_param.buffer[1] = dx;
s_local_param.buffer[2] = dy;
s_local_param.buffer[3] = wheel;
esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, 0x00, 4, s_local_param.buffer);
} else if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
s_local_param.buffer[0] = buttons;
s_local_param.buffer[1] = dx;
s_local_param.buffer[2] = dy;
esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, BOOT_PROTO_MOUSE_RPT_ID, 3, s_local_param.buffer);
}
xSemaphoreGive(s_local_param.mouse_mutex);
}
// move the mouse left and right
void mouse_move_task(void* pvParameters) {
const char* TAG = "mouse_move_task";
ESP_LOGI(TAG, "starting");
for(;;) {
s_local_param.x_dir = 1;
int8_t step = 10;
for (int i = 0; i < 2; i++) {
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
s_local_param.x_dir *= -1;
xSemaphoreGive(s_local_param.mouse_mutex);
for (int j = 0; j < 100; j++) {
send_mouse(0, s_local_param.x_dir * step, 0, 0);
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
static void print_bt_address(void) {
const char* TAG = "bt_address";
const uint8_t* bd_addr;
bd_addr = esp_bt_dev_get_address();
ESP_LOGI(TAG, "my bluetooth address is %02X:%02X:%02X:%02X:%02X:%02X",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
}
// a generic mouse descriptor
uint8_t hid_descriptor_mouse_boot_mode[] = {
// HID report descriptor for a generic mouse. The contents of the report are:
// 3 buttons, moving information for X and Y cursors, information for a wheel.
uint8_t hid_mouse_descriptor[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
@ -148,13 +69,109 @@ uint8_t hid_descriptor_mouse_boot_mode[] = {
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
int hid_descriptor_mouse_boot_mode_len = sizeof(hid_descriptor_mouse_boot_mode);
const int hid_mouse_descriptor_len = sizeof(hid_mouse_descriptor);
/**
* @brief Integrity check of the report ID and report type for GET_REPORT request from HID host.
* Boot Protocol Mode requires report ID. For Report Protocol Mode, when the report descriptor
* does not declare report ID Global ITEMS, the report ID does not exist in the GET_REPORT request,
* and a value of 0 for report_id will occur in ESP_HIDD_GET_REPORT_EVT callback parameter.
*/
bool check_report_id_type(uint8_t report_id, uint8_t report_type)
{
bool ret = false;
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
do {
if (report_type != ESP_HIDD_REPORT_TYPE_INPUT) {
break;
}
if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
if (report_id == ESP_HIDD_BOOT_REPORT_ID_MOUSE) {
ret = true;
break;
}
} else {
if (report_id == 0) {
ret = true;
break;
}
}
} while (0);
if (!ret) {
if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
} else {
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
}
}
xSemaphoreGive(s_local_param.mouse_mutex);
return ret;
}
// send the buttons, change in x, and change in y
void send_mouse_report(uint8_t buttons, char dx, char dy, char wheel)
{
uint8_t report_id;
uint16_t report_size;
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
report_id = 0;
report_size = REPORT_PROTOCOL_MOUSE_REPORT_SIZE;
s_local_param.buffer[0] = buttons;
s_local_param.buffer[1] = dx;
s_local_param.buffer[2] = dy;
s_local_param.buffer[3] = wheel;
} else {
// Boot Mode
report_id = ESP_HIDD_BOOT_REPORT_ID_MOUSE;
report_size = ESP_HIDD_BOOT_REPORT_SIZE_MOUSE - 1;
s_local_param.buffer[0] = buttons;
s_local_param.buffer[1] = dx;
s_local_param.buffer[2] = dy;
}
esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, report_id, report_size, s_local_param.buffer);
xSemaphoreGive(s_local_param.mouse_mutex);
}
// move the mouse left and right
void mouse_move_task(void *pvParameters)
{
const char *TAG = "mouse_move_task";
ESP_LOGI(TAG, "starting");
for (;;) {
s_local_param.x_dir = 1;
int8_t step = 10;
for (int i = 0; i < 2; i++) {
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
s_local_param.x_dir *= -1;
xSemaphoreGive(s_local_param.mouse_mutex);
for (int j = 0; j < 100; j++) {
send_mouse_report(0, s_local_param.x_dir * step, 0, 0);
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
static void print_bt_address(void)
{
const char *TAG = "bt_address";
const uint8_t *bd_addr;
bd_addr = esp_bt_dev_get_address();
ESP_LOGI(TAG, "my bluetooth address is %02X:%02X:%02X:%02X:%02X:%02X",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
}
void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
const char* TAG = "esp_bt_gap_cb";
const char *TAG = "esp_bt_gap_cb";
switch (event) {
case ESP_BT_GAP_AUTH_CMPL_EVT:{
case ESP_BT_GAP_AUTH_CMPL_EVT: {
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
ESP_LOGI(TAG, "authentication success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
@ -163,7 +180,7 @@ void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
}
break;
}
case ESP_BT_GAP_PIN_REQ_EVT:{
case ESP_BT_GAP_PIN_REQ_EVT: {
ESP_LOGI(TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
if (param->pin_req.min_16_digit) {
ESP_LOGI(TAG, "Input pin code: 0000 0000 0000 0000");
@ -206,7 +223,7 @@ void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
void bt_app_task_start_up(void)
{
s_local_param.mouse_mutex = xSemaphoreCreateMutex();
memset(s_local_param.buffer, 0, 4);
memset(s_local_param.buffer, 0, REPORT_BUFFER_SIZE);
xTaskCreate(mouse_move_task, "mouse_move_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3, &s_local_param.mouse_task_hdl);
return;
}
@ -227,7 +244,7 @@ void bt_app_task_shut_down(void)
void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
{
static const char* TAG = "esp_bt_hidd_cb";
static const char *TAG = "esp_bt_hidd_cb";
switch (event) {
case ESP_HIDD_INIT_EVT:
if (param->init.status == ESP_HIDD_SUCCESS) {
@ -305,12 +322,18 @@ void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
ESP_LOGI(TAG, "ESP_HIDD_GET_REPORT_EVT id:0x%02x, type:%d, size:%d", param->get_report.report_id,
param->get_report.report_type, param->get_report.buffer_size);
if (check_report_id_type(param->get_report.report_id, param->get_report.report_type)) {
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
uint8_t report_id;
uint16_t report_len;
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
esp_bt_hid_device_send_report(param->get_report.report_type, 0x00, 4, s_local_param.buffer);
} else if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
esp_bt_hid_device_send_report(param->get_report.report_type, 0x02, 3, s_local_param.buffer);
report_id = 0;
report_len = REPORT_PROTOCOL_MOUSE_REPORT_SIZE;
} else {
// Boot Mode
report_id = ESP_HIDD_BOOT_REPORT_ID_MOUSE;
report_len = ESP_HIDD_BOOT_REPORT_SIZE_MOUSE - 1;
}
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
esp_bt_hid_device_send_report(param->get_report.report_type, report_id, report_len, s_local_param.buffer);
xSemaphoreGive(s_local_param.mouse_mutex);
} else {
ESP_LOGE(TAG, "check_report_id failed!");
@ -356,46 +379,38 @@ void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
}
}
void app_main(void) {
const char* TAG = "app_main";
esp_err_t ret;
void app_main(void)
{
const char *TAG = "app_main";
esp_err_t ret;
s_local_param.app_param.name = "Mouse";
s_local_param.app_param.description = "Mouse Example";
s_local_param.app_param.provider = "ESP32";
s_local_param.app_param.subclass = ESP_HID_CLASS_MIC;
s_local_param.app_param.desc_list = hid_descriptor_mouse_boot_mode;
s_local_param.app_param.desc_list_len = hid_descriptor_mouse_boot_mode_len;
memset(&s_local_param.both_qos, 0, sizeof(esp_hidd_qos_param_t)); // don't set the qos parameters
s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE;
ret = nvs_flash_init();
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_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
ESP_LOGE(TAG, "initialize controller failed: %s\n", esp_err_to_name(ret));
ESP_LOGE(TAG, "initialize controller failed: %s\n", esp_err_to_name(ret));
return;
}
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
ESP_LOGE(TAG, "enable controller failed: %s\n", esp_err_to_name(ret));
ESP_LOGE(TAG, "enable controller failed: %s\n", esp_err_to_name(ret));
return;
}
if ((ret = esp_bluedroid_init()) != ESP_OK) {
ESP_LOGE(TAG, "initialize bluedroid failed: %s\n", esp_err_to_name(ret));
ESP_LOGE(TAG, "initialize bluedroid failed: %s\n", esp_err_to_name(ret));
return;
}
if ((ret = esp_bluedroid_enable()) != ESP_OK) {
ESP_LOGE(TAG, "enable bluedroid failed: %s\n", esp_err_to_name(ret));
ESP_LOGE(TAG, "enable bluedroid failed: %s\n", esp_err_to_name(ret));
return;
}
@ -404,22 +419,37 @@ void app_main(void) {
return;
}
ESP_LOGI(TAG, "setting device name");
esp_bt_dev_set_device_name("HID Mouse Example");
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);
esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR);
vTaskDelay(2000 / portTICK_PERIOD_MS);
// Initialize HID SDP information and L2CAP parameters.
// to be used in the call of `esp_bt_hid_device_register_app` after profile initialization finishes
do {
s_local_param.app_param.name = "Mouse";
s_local_param.app_param.description = "Mouse Example";
s_local_param.app_param.provider = "ESP32";
s_local_param.app_param.subclass = ESP_HID_CLASS_MIC;
s_local_param.app_param.desc_list = hid_mouse_descriptor;
s_local_param.app_param.desc_list_len = hid_mouse_descriptor_len;
memset(&s_local_param.both_qos, 0, sizeof(esp_hidd_qos_param_t)); // don't set the qos parameters
} while (0);
// Report Protocol Mode is the default mode, according to Bluetooth HID specification
s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE;
ESP_LOGI(TAG, "register hid device callback");
esp_bt_hid_device_register_callback(esp_bt_hidd_cb);
ESP_LOGI(TAG, "starting hid device");
esp_bt_hid_device_init();
esp_bt_hid_device_init();
#if (CONFIG_BT_SSP_ENABLED == true)
/* Set default parameters for Secure Simple Pairing */
@ -437,5 +467,5 @@ void app_main(void) {
esp_bt_gap_set_pin(pin_type, 0, pin_code);
print_bt_address();
ESP_LOGI(TAG, "exiting");
ESP_LOGI(TAG, "exiting");
}