1
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
87
88
89
90
96
97
98
99
100
116
117
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
174
175
176
177
178
179
189
190
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
214
215
216
217
218
221
224
225
226
227
228
235
236
249
250
251
252
253
254
262
264
277
284
285
286
289
290
291
292
293
294
295
298
301
302
319
329
332
333
334
335
336
337
338
346
347
348
349
352
353
356
370
373
388
390
391
392
393
394
395
396
397
398
399
400
404
405
406
407
408
409
413
414
418
419
420
421
422
423
427
428
432
433
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
476
477
478
482
483
484
485
486
487
488
/* ... */
#include "esp_log.h"
#include "esp_hidd_api.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_bt.h"
#include "esp_err.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_gap_bt_api.h"
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"14 includes
#define REPORT_PROTOCOL_MOUSE_REPORT_SIZE (4)
#define REPORT_BUFFER_SIZE REPORT_PROTOCOL_MOUSE_REPORT_SIZE
static const char local_device_name[] = CONFIG_EXAMPLE_LOCAL_DEVICE_NAME;
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[REPORT_BUFFER_SIZE];
int8_t x_dir;
}{ ... } local_param_t;
static local_param_t s_local_param = {0};
uint8_t hid_mouse_descriptor[] = {
0x05, 0x01,
0x09, 0x02,
0xa1, 0x01,
0x09, 0x01,
0xa1, 0x00,
0x05, 0x09,
0x19, 0x01,
0x29, 0x03,
0x15, 0x00,
0x25, 0x01,
0x95, 0x03,
0x75, 0x01,
0x81, 0x02,
0x95, 0x01,
0x75, 0x05,
0x81, 0x03,
0x05, 0x01,
0x09, 0x30,
0x09, 0x31,
0x09, 0x38,
0x15, 0x81,
0x25, 0x7f,
0x75, 0x08,
0x95, 0x03,
0x81, 0x06,
0xc0,
0xc0
}{...};
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size)
{
if (bda == NULL || str == NULL || size < 18) {
return NULL;
}{...}
uint8_t *p = bda;
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
p[0], p[1], p[2], p[3], p[4], p[5]);
return str;
}{ ... }
const int hid_mouse_descriptor_len = sizeof(hid_mouse_descriptor);
/* ... */
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;
}{ ... }
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 {
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);
}{ ... }
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);
}{...}
}{ ... }
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";
switch (event) {
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);
}{...} else {
ESP_LOGE(TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
}{...}
break;
}{...}
... 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");
esp_bt_pin_code_t pin_code = {0};
esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
}{...} else {
ESP_LOGI(TAG, "Input pin code: 1234");
esp_bt_pin_code_t pin_code;
pin_code[0] = '1';
pin_code[1] = '2';
pin_code[2] = '3';
pin_code[3] = '4';
esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code);
}{...}
break;
}{...}
#if (CONFIG_EXAMPLE_SSP_ENABLED == true)...
case ESP_BT_GAP_CFM_REQ_EVT:
ESP_LOGI(TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %"PRIu32, param->cfm_req.num_val);
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
break;...
case ESP_BT_GAP_KEY_NOTIF_EVT:
ESP_LOGI(TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%"PRIu32, param->key_notif.passkey);
break;...
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
break;/* ... */
#endif
case ESP_BT_GAP_MODE_CHG_EVT:
ESP_LOGI(TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode);
break;...
default:
ESP_LOGI(TAG, "event: %d", event);
break;...
}{...}
return;
}{ ... }
void bt_app_task_start_up(void)
{
s_local_param.mouse_mutex = xSemaphoreCreateMutex();
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;
}{ ... }
void bt_app_task_shut_down(void)
{
if (s_local_param.mouse_task_hdl) {
vTaskDelete(s_local_param.mouse_task_hdl);
s_local_param.mouse_task_hdl = NULL;
}{...}
if (s_local_param.mouse_mutex) {
vSemaphoreDelete(s_local_param.mouse_mutex);
s_local_param.mouse_mutex = NULL;
}{...}
return;
}{ ... }
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";
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;...
case ESP_HIDD_DEINIT_EVT:
break;...
case ESP_HIDD_REGISTER_APP_EVT:
if (param->register_app.status == ESP_HIDD_SUCCESS) {
ESP_LOGI(TAG, "setting hid parameters success!");
ESP_LOGI(TAG, "setting to connectable, discoverable");
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
if (param->register_app.in_use) {
ESP_LOGI(TAG, "start virtual cable plug!");
esp_bt_hid_device_connect(param->register_app.bd_addr);
}{...}
}{...} else {
ESP_LOGE(TAG, "setting hid parameters failed!");
}{...}
break;...
case ESP_HIDD_UNREGISTER_APP_EVT:
if (param->unregister_app.status == ESP_HIDD_SUCCESS) {
ESP_LOGI(TAG, "unregister app success!");
}{...} else {
ESP_LOGE(TAG, "unregister app failed!");
}{...}
break;...
case ESP_HIDD_OPEN_EVT:
if (param->open.status == ESP_HIDD_SUCCESS) {
if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTING) {
ESP_LOGI(TAG, "connecting...");
}{...} else if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTED) {
ESP_LOGI(TAG, "connected to %02x:%02x:%02x:%02x:%02x:%02x", param->open.bd_addr[0],
param->open.bd_addr[1], param->open.bd_addr[2], param->open.bd_addr[3], param->open.bd_addr[4],
param->open.bd_addr[5]);
bt_app_task_start_up();
ESP_LOGI(TAG, "making self non-discoverable and non-connectable.");
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
}{...} else {
ESP_LOGE(TAG, "unknown connection status");
}{...}
}{...} else {
ESP_LOGE(TAG, "open failed!");
}{...}
break;...
case ESP_HIDD_CLOSE_EVT:
ESP_LOGI(TAG, "ESP_HIDD_CLOSE_EVT");
if (param->close.status == ESP_HIDD_SUCCESS) {
if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTING) {
ESP_LOGI(TAG, "disconnecting...");
}{...} else if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) {
ESP_LOGI(TAG, "disconnected!");
bt_app_task_shut_down();
ESP_LOGI(TAG, "making self discoverable and connectable again.");
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
}{...} else {
ESP_LOGE(TAG, "unknown connection status");
}{...}
}{...} else {
ESP_LOGE(TAG, "close failed!");
}{...}
break;...
case ESP_HIDD_SEND_REPORT_EVT:
if (param->send_report.status == ESP_HIDD_SUCCESS) {
ESP_LOGI(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d", param->send_report.report_id,
param->send_report.report_type);
}{...} else {
ESP_LOGE(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d, status:%d, reason:%d",
param->send_report.report_id, param->send_report.report_type, param->send_report.status,
param->send_report.reason);
}{...}
break;...
case ESP_HIDD_REPORT_ERR_EVT:
ESP_LOGI(TAG, "ESP_HIDD_REPORT_ERR_EVT");
break;...
case ESP_HIDD_GET_REPORT_EVT:
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)) {
uint8_t report_id;
uint16_t report_len;
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
report_id = 0;
report_len = REPORT_PROTOCOL_MOUSE_REPORT_SIZE;
}{...} else {
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!");
}{...}
break;...
case ESP_HIDD_SET_REPORT_EVT:
ESP_LOGI(TAG, "ESP_HIDD_SET_REPORT_EVT");
break;...
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;...
case ESP_HIDD_INTR_DATA_EVT:
ESP_LOGI(TAG, "ESP_HIDD_INTR_DATA_EVT");
break;...
case ESP_HIDD_VC_UNPLUG_EVT:
ESP_LOGI(TAG, "ESP_HIDD_VC_UNPLUG_EVT");
if (param->vc_unplug.status == ESP_HIDD_SUCCESS) {
if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) {
ESP_LOGI(TAG, "disconnected!");
bt_app_task_shut_down();
ESP_LOGI(TAG, "making self discoverable and connectable again.");
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
}{...} else {
ESP_LOGE(TAG, "unknown connection status");
}{...}
}{...} else {
ESP_LOGE(TAG, "close failed!");
}{...}
break;...
default:
break;...
}{...}
}{ ... }
void app_main(void)
{
const char *TAG = "app_main";
esp_err_t ret;
char bda_str[18] = {0};
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_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", 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", esp_err_to_name(ret));
return;
}{...}
esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
#if (CONFIG_EXAMPLE_SSP_ENABLED == false)
bluedroid_cfg.ssp_en = false;
#endif
if ((ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg)) != ESP_OK) {
ESP_LOGE(TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(ret));
return;
}{...}
if ((ret = esp_bluedroid_enable()) != ESP_OK) {
ESP_LOGE(TAG, "enable bluedroid failed: %s", esp_err_to_name(ret));
return;
}{...}
if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) {
ESP_LOGE(TAG, "gap register failed: %s", esp_err_to_name(ret));
return;
}{...}
ESP_LOGI(TAG, "setting device name");
esp_bt_gap_set_device_name(local_device_name);
ESP_LOGI(TAG, "setting cod major, peripheral");
esp_bt_cod_t cod = {0};
cod.major = ESP_BT_COD_MAJOR_DEV_PERIPHERAL;
cod.minor = ESP_BT_COD_MINOR_PERIPHERAL_POINTING;
esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR);
vTaskDelay(2000 / portTICK_PERIOD_MS);
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));
}{...} while (0);
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();
#if (CONFIG_EXAMPLE_SSP_ENABLED == true)
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE;
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));/* ... */
#endif
/* ... */
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
esp_bt_pin_code_t pin_code;
esp_bt_gap_set_pin(pin_type, 0, pin_code);
ESP_LOGI(TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str)));
ESP_LOGI(TAG, "exiting");
}{ ... }