Implements Classic Bluetooth HID Device profile for keyboard/mouse emulation:
Firmware:
- bt_hid.c/h: HID device driver with combo keyboard/mouse HID descriptor
- cmd_hid_{enable,disable,connect,disconnect}: HID lifecycle management
- cmd_hid_send_keyboard: Send keyboard reports (modifier + up to 6 keys)
- cmd_hid_send_mouse: Send mouse reports (buttons + relative X/Y)
- cmd_hid_status: Query HID state (enabled, registered, connected)
Python MCP tools:
- esp32_hid_enable/disable: Control HID device mode
- esp32_hid_connect/disconnect: Manage HID host connections
- esp32_hid_send_keyboard/send_mouse: Send HID reports
- esp32_hid_status: Get connection state
Config:
- Enable BT_HID_ENABLED + BT_HID_DEVICE_ENABLED in sdkconfig.defaults
- Add bt_hid.c to CMakeLists.txt
Tested E2E: Linux (hci1) connects to ESP32 HID device, keyboard and
mouse reports sent successfully.
556 lines
19 KiB
C
556 lines
19 KiB
C
/*
|
|
* bt_hid.c -- Classic Bluetooth HID Device (keyboard/mouse emulation)
|
|
*
|
|
* Implements a combo HID device that can act as both keyboard and mouse.
|
|
* Uses ESP-IDF Bluedroid HID Device API.
|
|
*/
|
|
|
|
#include "bt_hid.h"
|
|
#include "protocol.h"
|
|
#include "uart_handler.h"
|
|
#include "event_reporter.h"
|
|
|
|
#include "esp_log.h"
|
|
#include "esp_bt.h"
|
|
#include "esp_bt_main.h"
|
|
#include "esp_bt_device.h"
|
|
#include "esp_gap_bt_api.h"
|
|
#include "esp_hidd_api.h"
|
|
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
|
|
static const char *TAG = "bt_hid";
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* HID Report Descriptor - Combo Keyboard + Mouse */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/*
|
|
* This descriptor defines a combo device with:
|
|
* - Report ID 1: Keyboard (8-byte boot protocol compatible)
|
|
* - Report ID 2: Mouse (3-byte: buttons, X, Y)
|
|
*/
|
|
static const uint8_t hid_descriptor[] = {
|
|
/* Keyboard */
|
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
|
0x09, 0x06, // Usage (Keyboard)
|
|
0xA1, 0x01, // Collection (Application)
|
|
0x85, 0x01, // Report ID (1)
|
|
0x05, 0x07, // Usage Page (Key Codes)
|
|
0x19, 0xE0, // Usage Minimum (224) - Left Control
|
|
0x29, 0xE7, // Usage Maximum (231) - Right GUI
|
|
0x15, 0x00, // Logical Minimum (0)
|
|
0x25, 0x01, // Logical Maximum (1)
|
|
0x75, 0x01, // Report Size (1)
|
|
0x95, 0x08, // Report Count (8)
|
|
0x81, 0x02, // Input (Data, Variable, Absolute) - Modifier byte
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x01, // Input (Constant) - Reserved byte
|
|
0x95, 0x06, // Report Count (6)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x15, 0x00, // Logical Minimum (0)
|
|
0x25, 0x65, // Logical Maximum (101)
|
|
0x05, 0x07, // Usage Page (Key Codes)
|
|
0x19, 0x00, // Usage Minimum (0)
|
|
0x29, 0x65, // Usage Maximum (101)
|
|
0x81, 0x00, // Input (Data, Array) - Key array (6 keys)
|
|
0xC0, // End Collection
|
|
|
|
/* Mouse */
|
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
|
0x09, 0x02, // Usage (Mouse)
|
|
0xA1, 0x01, // Collection (Application)
|
|
0x85, 0x02, // Report ID (2)
|
|
0x09, 0x01, // Usage (Pointer)
|
|
0xA1, 0x00, // Collection (Physical)
|
|
0x05, 0x09, // Usage Page (Buttons)
|
|
0x19, 0x01, // Usage Minimum (1)
|
|
0x29, 0x03, // Usage Maximum (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, Variable, Absolute) - 3 buttons
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x05, // Report Size (5)
|
|
0x81, 0x01, // Input (Constant) - Padding
|
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
|
0x09, 0x30, // Usage (X)
|
|
0x09, 0x31, // Usage (Y)
|
|
0x15, 0x81, // Logical Minimum (-127)
|
|
0x25, 0x7F, // Logical Maximum (127)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x95, 0x02, // Report Count (2)
|
|
0x81, 0x06, // Input (Data, Variable, Relative) - X, Y
|
|
0xC0, // End Collection
|
|
0xC0, // End Collection
|
|
};
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* State */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static struct {
|
|
bool enabled;
|
|
bool registered;
|
|
bool connected;
|
|
char remote_addr[18];
|
|
esp_hidd_protocol_mode_t protocol_mode;
|
|
} hid_state = {0};
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Helper: BD address to string */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void bd_addr_to_str(const esp_bd_addr_t bda, char *str)
|
|
{
|
|
snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* HID Device Callback */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void hidd_callback(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
|
|
{
|
|
char addr_str[18];
|
|
|
|
switch (event) {
|
|
case ESP_HIDD_INIT_EVT:
|
|
if (param->init.status == ESP_HIDD_SUCCESS) {
|
|
ESP_LOGI(TAG, "HID device initialized");
|
|
} else {
|
|
ESP_LOGE(TAG, "HID device init failed: %d", param->init.status);
|
|
}
|
|
break;
|
|
|
|
case ESP_HIDD_DEINIT_EVT:
|
|
ESP_LOGI(TAG, "HID device deinitialized");
|
|
hid_state.enabled = false;
|
|
hid_state.registered = false;
|
|
break;
|
|
|
|
case ESP_HIDD_REGISTER_APP_EVT:
|
|
if (param->register_app.status == ESP_HIDD_SUCCESS) {
|
|
ESP_LOGI(TAG, "HID app registered");
|
|
hid_state.registered = true;
|
|
if (param->register_app.in_use) {
|
|
bd_addr_to_str(param->register_app.bd_addr, addr_str);
|
|
ESP_LOGI(TAG, "Virtual cable to: %s", addr_str);
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "HID app register failed: %d", param->register_app.status);
|
|
}
|
|
break;
|
|
|
|
case ESP_HIDD_UNREGISTER_APP_EVT:
|
|
ESP_LOGI(TAG, "HID app unregistered");
|
|
hid_state.registered = false;
|
|
break;
|
|
|
|
case ESP_HIDD_OPEN_EVT:
|
|
if (param->open.status == ESP_HIDD_SUCCESS &&
|
|
param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTED) {
|
|
bd_addr_to_str(param->open.bd_addr, addr_str);
|
|
strncpy(hid_state.remote_addr, addr_str, sizeof(hid_state.remote_addr) - 1);
|
|
hid_state.connected = true;
|
|
ESP_LOGI(TAG, "HID connected to: %s", addr_str);
|
|
|
|
cJSON *d = cJSON_CreateObject();
|
|
cJSON_AddStringToObject(d, "address", addr_str);
|
|
cJSON_AddStringToObject(d, "transport", "hid");
|
|
event_report(EVT_HID_CONNECT, d);
|
|
}
|
|
break;
|
|
|
|
case ESP_HIDD_CLOSE_EVT:
|
|
ESP_LOGI(TAG, "HID disconnected (status=%d, conn_status=%d)",
|
|
param->close.status, param->close.conn_status);
|
|
{
|
|
cJSON *d = cJSON_CreateObject();
|
|
if (hid_state.remote_addr[0] != '\0') {
|
|
cJSON_AddStringToObject(d, "address", hid_state.remote_addr);
|
|
}
|
|
cJSON_AddStringToObject(d, "transport", "hid");
|
|
event_report(EVT_HID_DISCONNECT, d);
|
|
}
|
|
hid_state.connected = false;
|
|
hid_state.remote_addr[0] = '\0';
|
|
break;
|
|
|
|
case ESP_HIDD_SEND_REPORT_EVT:
|
|
if (param->send_report.status != ESP_HIDD_SUCCESS) {
|
|
ESP_LOGW(TAG, "Send report failed: %d (reason=%d)",
|
|
param->send_report.status, param->send_report.reason);
|
|
}
|
|
break;
|
|
|
|
case ESP_HIDD_SET_PROTOCOL_EVT:
|
|
hid_state.protocol_mode = param->set_protocol.protocol_mode;
|
|
ESP_LOGI(TAG, "Protocol mode set to: %s",
|
|
hid_state.protocol_mode == ESP_HIDD_BOOT_MODE ? "boot" : "report");
|
|
break;
|
|
|
|
case ESP_HIDD_INTR_DATA_EVT:
|
|
/* Host sent data to us (e.g., LED state for keyboard) */
|
|
ESP_LOGI(TAG, "Received data from host: report_id=%d, len=%d",
|
|
param->intr_data.report_id, param->intr_data.len);
|
|
{
|
|
cJSON *d = cJSON_CreateObject();
|
|
cJSON_AddNumberToObject(d, "report_id", param->intr_data.report_id);
|
|
cJSON_AddNumberToObject(d, "length", param->intr_data.len);
|
|
/* Encode as hex */
|
|
if (param->intr_data.len > 0 && param->intr_data.len < 64) {
|
|
char hex[129];
|
|
for (int i = 0; i < param->intr_data.len; i++) {
|
|
sprintf(hex + i * 2, "%02x", param->intr_data.data[i]);
|
|
}
|
|
hex[param->intr_data.len * 2] = '\0';
|
|
cJSON_AddStringToObject(d, "data_hex", hex);
|
|
}
|
|
event_report(EVT_HID_DATA, d);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ESP_LOGD(TAG, "HID event: %d", event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Initialization */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void bt_hid_init(void)
|
|
{
|
|
ESP_LOGI(TAG, "HID device module ready");
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Command: hid_enable */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void cmd_hid_enable(const char *id, cJSON *params)
|
|
{
|
|
if (hid_state.enabled) {
|
|
uart_send_response(id, STATUS_OK, cJSON_CreateString("already enabled"));
|
|
return;
|
|
}
|
|
|
|
/* Register callback */
|
|
esp_err_t err = esp_bt_hid_device_register_callback(hidd_callback);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to register HID callback: %s", esp_err_to_name(err));
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString(esp_err_to_name(err)));
|
|
return;
|
|
}
|
|
|
|
/* Initialize HID device */
|
|
err = esp_bt_hid_device_init();
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to init HID device: %s", esp_err_to_name(err));
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString(esp_err_to_name(err)));
|
|
return;
|
|
}
|
|
|
|
/* Register app with HID descriptor */
|
|
esp_hidd_app_param_t app_param = {
|
|
.name = "ESP32 HID",
|
|
.description = "Keyboard/Mouse Combo",
|
|
.provider = "mcbluetooth-esp32",
|
|
.subclass = ESP_HID_CLASS_COM, /* Combo keyboard/mouse */
|
|
.desc_list = (uint8_t *)hid_descriptor,
|
|
.desc_list_len = sizeof(hid_descriptor),
|
|
};
|
|
|
|
esp_hidd_qos_param_t qos = {
|
|
.service_type = 0x01, /* Best effort */
|
|
.token_rate = 0,
|
|
.token_bucket_size = 0,
|
|
.peak_bandwidth = 0,
|
|
.access_latency = 0,
|
|
.delay_variation = 0,
|
|
};
|
|
|
|
err = esp_bt_hid_device_register_app(&app_param, &qos, &qos);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to register HID app: %s", esp_err_to_name(err));
|
|
esp_bt_hid_device_deinit();
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString(esp_err_to_name(err)));
|
|
return;
|
|
}
|
|
|
|
hid_state.enabled = true;
|
|
hid_state.protocol_mode = ESP_HIDD_REPORT_MODE;
|
|
|
|
ESP_LOGI(TAG, "HID device enabled (keyboard/mouse combo)");
|
|
|
|
cJSON *data = cJSON_CreateObject();
|
|
cJSON_AddBoolToObject(data, "enabled", true);
|
|
cJSON_AddStringToObject(data, "device_class", "combo");
|
|
uart_send_response(id, STATUS_OK, data);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Command: hid_disable */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void cmd_hid_disable(const char *id, cJSON *params)
|
|
{
|
|
(void)params;
|
|
|
|
if (!hid_state.enabled) {
|
|
uart_send_response(id, STATUS_OK, cJSON_CreateString("already disabled"));
|
|
return;
|
|
}
|
|
|
|
if (hid_state.connected) {
|
|
esp_bt_hid_device_disconnect();
|
|
}
|
|
|
|
if (hid_state.registered) {
|
|
esp_bt_hid_device_unregister_app();
|
|
}
|
|
|
|
esp_bt_hid_device_deinit();
|
|
hid_state.enabled = false;
|
|
hid_state.registered = false;
|
|
hid_state.connected = false;
|
|
|
|
ESP_LOGI(TAG, "HID device disabled");
|
|
uart_send_response(id, STATUS_OK, NULL);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Command: hid_connect - Initiate connection to a host */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void cmd_hid_connect(const char *id, cJSON *params)
|
|
{
|
|
if (!hid_state.enabled) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("HID not enabled"));
|
|
return;
|
|
}
|
|
|
|
cJSON *j_addr = cJSON_GetObjectItem(params, "address");
|
|
if (!cJSON_IsString(j_addr)) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("missing 'address'"));
|
|
return;
|
|
}
|
|
|
|
/* Parse address */
|
|
esp_bd_addr_t bd_addr;
|
|
unsigned int addr[6];
|
|
if (sscanf(j_addr->valuestring, "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
&addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) != 6) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("invalid address format"));
|
|
return;
|
|
}
|
|
for (int i = 0; i < 6; i++) bd_addr[i] = (uint8_t)addr[i];
|
|
|
|
esp_err_t err = esp_bt_hid_device_connect(bd_addr);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "HID connect failed: %s", esp_err_to_name(err));
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString(esp_err_to_name(err)));
|
|
return;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "HID connecting to %s", j_addr->valuestring);
|
|
uart_send_response(id, STATUS_OK, cJSON_CreateString("connecting"));
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Command: hid_disconnect */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void cmd_hid_disconnect(const char *id, cJSON *params)
|
|
{
|
|
(void)params;
|
|
|
|
if (!hid_state.enabled) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("HID not enabled"));
|
|
return;
|
|
}
|
|
|
|
if (!hid_state.connected) {
|
|
uart_send_response(id, STATUS_OK, cJSON_CreateString("not connected"));
|
|
return;
|
|
}
|
|
|
|
esp_err_t err = esp_bt_hid_device_disconnect();
|
|
if (err != ESP_OK) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString(esp_err_to_name(err)));
|
|
return;
|
|
}
|
|
|
|
uart_send_response(id, STATUS_OK, NULL);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Command: hid_send_keyboard - Send keyboard report */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void cmd_hid_send_keyboard(const char *id, cJSON *params)
|
|
{
|
|
if (!hid_state.enabled) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("HID not enabled"));
|
|
return;
|
|
}
|
|
|
|
if (!hid_state.connected) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("not connected"));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Keyboard report format (8 bytes):
|
|
* [0] Modifier keys (bit flags: Ctrl, Shift, Alt, GUI - left/right)
|
|
* [1] Reserved (always 0)
|
|
* [2-7] Key codes (up to 6 simultaneous keys)
|
|
*
|
|
* Modifier bits: 0x01=LCtrl, 0x02=LShift, 0x04=LAlt, 0x08=LGUI,
|
|
* 0x10=RCtrl, 0x20=RShift, 0x40=RAlt, 0x80=RGUI
|
|
*/
|
|
uint8_t report[8] = {0};
|
|
|
|
cJSON *j_modifier = cJSON_GetObjectItem(params, "modifier");
|
|
if (cJSON_IsNumber(j_modifier)) {
|
|
report[0] = (uint8_t)j_modifier->valuedouble;
|
|
}
|
|
|
|
cJSON *j_keys = cJSON_GetObjectItem(params, "keys");
|
|
if (cJSON_IsArray(j_keys)) {
|
|
int key_count = cJSON_GetArraySize(j_keys);
|
|
if (key_count > 6) key_count = 6;
|
|
for (int i = 0; i < key_count; i++) {
|
|
cJSON *key = cJSON_GetArrayItem(j_keys, i);
|
|
if (cJSON_IsNumber(key)) {
|
|
report[2 + i] = (uint8_t)key->valuedouble;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Single key shorthand */
|
|
cJSON *j_key = cJSON_GetObjectItem(params, "key");
|
|
if (cJSON_IsNumber(j_key)) {
|
|
report[2] = (uint8_t)j_key->valuedouble;
|
|
}
|
|
|
|
esp_err_t err = esp_bt_hid_device_send_report(
|
|
ESP_HIDD_REPORT_TYPE_INTRDATA, 0x01, sizeof(report), report);
|
|
|
|
if (err != ESP_OK) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString(esp_err_to_name(err)));
|
|
return;
|
|
}
|
|
|
|
cJSON *data = cJSON_CreateObject();
|
|
cJSON_AddNumberToObject(data, "modifier", report[0]);
|
|
cJSON *keys_arr = cJSON_CreateArray();
|
|
for (int i = 0; i < 6; i++) {
|
|
if (report[2 + i] != 0) {
|
|
cJSON_AddItemToArray(keys_arr, cJSON_CreateNumber(report[2 + i]));
|
|
}
|
|
}
|
|
cJSON_AddItemToObject(data, "keys", keys_arr);
|
|
uart_send_response(id, STATUS_OK, data);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Command: hid_send_mouse - Send mouse report */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void cmd_hid_send_mouse(const char *id, cJSON *params)
|
|
{
|
|
if (!hid_state.enabled) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("HID not enabled"));
|
|
return;
|
|
}
|
|
|
|
if (!hid_state.connected) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString("not connected"));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Mouse report format (3 bytes):
|
|
* [0] Buttons (bit 0=left, bit 1=right, bit 2=middle)
|
|
* [1] X movement (-127 to +127)
|
|
* [2] Y movement (-127 to +127)
|
|
*/
|
|
uint8_t report[3] = {0};
|
|
|
|
cJSON *j_buttons = cJSON_GetObjectItem(params, "buttons");
|
|
if (cJSON_IsNumber(j_buttons)) {
|
|
report[0] = (uint8_t)j_buttons->valuedouble;
|
|
}
|
|
|
|
cJSON *j_x = cJSON_GetObjectItem(params, "x");
|
|
if (cJSON_IsNumber(j_x)) {
|
|
int x = (int)j_x->valuedouble;
|
|
if (x < -127) x = -127;
|
|
if (x > 127) x = 127;
|
|
report[1] = (int8_t)x;
|
|
}
|
|
|
|
cJSON *j_y = cJSON_GetObjectItem(params, "y");
|
|
if (cJSON_IsNumber(j_y)) {
|
|
int y = (int)j_y->valuedouble;
|
|
if (y < -127) y = -127;
|
|
if (y > 127) y = 127;
|
|
report[2] = (int8_t)y;
|
|
}
|
|
|
|
esp_err_t err = esp_bt_hid_device_send_report(
|
|
ESP_HIDD_REPORT_TYPE_INTRDATA, 0x02, sizeof(report), report);
|
|
|
|
if (err != ESP_OK) {
|
|
uart_send_response(id, STATUS_ERROR, cJSON_CreateString(esp_err_to_name(err)));
|
|
return;
|
|
}
|
|
|
|
cJSON *data = cJSON_CreateObject();
|
|
cJSON_AddNumberToObject(data, "buttons", report[0]);
|
|
cJSON_AddNumberToObject(data, "x", (int8_t)report[1]);
|
|
cJSON_AddNumberToObject(data, "y", (int8_t)report[2]);
|
|
uart_send_response(id, STATUS_OK, data);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Command: hid_status */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void cmd_hid_status(const char *id, cJSON *params)
|
|
{
|
|
(void)params;
|
|
|
|
cJSON *data = cJSON_CreateObject();
|
|
cJSON_AddBoolToObject(data, "enabled", hid_state.enabled);
|
|
cJSON_AddBoolToObject(data, "registered", hid_state.registered);
|
|
cJSON_AddBoolToObject(data, "connected", hid_state.connected);
|
|
if (hid_state.connected && hid_state.remote_addr[0] != '\0') {
|
|
cJSON_AddStringToObject(data, "remote_address", hid_state.remote_addr);
|
|
}
|
|
cJSON_AddStringToObject(data, "protocol_mode",
|
|
hid_state.protocol_mode == ESP_HIDD_BOOT_MODE ? "boot" : "report");
|
|
uart_send_response(id, STATUS_OK, data);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* State queries */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
bool bt_hid_is_enabled(void)
|
|
{
|
|
return hid_state.enabled;
|
|
}
|
|
|
|
bool bt_hid_is_connected(void)
|
|
{
|
|
return hid_state.connected;
|
|
}
|