feat: 添加 BLE HID 键盘支持与配置
- 在 sdkconfig.defaults 中更新 BLE 配置以支持 HID 键盘功能 - 新增 ble_hid.c 和 ble_hid.h 文件,实现 BLE HID 键盘的核心功能 - 更新 CMakeLists.txt,包含新的服务文件以支持 BLE HID - 修改 ui_handlers.c,响应启动按钮事件以发送 HID 按键 - 更新 ble_manager.c,初始化 BLE HID 功能并处理连接状态 - 新增 esp_hid_gap.c 和 esp_hid_gap.h 文件,管理 BLE HID 的 GAP 相关功能
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
#include "ble_hid.h"
|
||||
|
||||
#include "board_config.h"
|
||||
|
||||
#if BOARD_BLE_ENABLED
|
||||
|
||||
#include "esp_hidd.h"
|
||||
#include "esp_hid_common.h"
|
||||
#include "esp_hid_gap.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "ble_hid";
|
||||
|
||||
#define HID_KEYBOARD_REPORT_ID 1
|
||||
#define HID_KEYBOARD_REPORT_LEN 7
|
||||
|
||||
static const unsigned char s_keyboard_report_map[] = {
|
||||
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x85, 0x01, 0x05, 0x07, 0x19, 0xE0,
|
||||
0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02,
|
||||
0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08,
|
||||
0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03,
|
||||
0x95, 0x05, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00,
|
||||
0x29, 0x65, 0x81, 0x00, 0xC0,
|
||||
};
|
||||
|
||||
static esp_hid_raw_report_map_t s_report_maps[] = {
|
||||
{
|
||||
.data = s_keyboard_report_map,
|
||||
.len = sizeof(s_keyboard_report_map),
|
||||
},
|
||||
};
|
||||
|
||||
static esp_hid_device_config_t s_hid_config = {
|
||||
.vendor_id = 0x16C0,
|
||||
.product_id = 0x05DF,
|
||||
.version = 0x0100,
|
||||
.device_name = CONFIG_ESP_BLE_DEVICE_NAME,
|
||||
.manufacturer_name = "HeavenlyHeroStar",
|
||||
.serial_number = "1234567890",
|
||||
.report_maps = s_report_maps,
|
||||
.report_maps_len = 1,
|
||||
};
|
||||
|
||||
static esp_hidd_dev_t *s_hid_dev;
|
||||
static ble_hid_status_cb_t s_status_cb;
|
||||
static bool s_connected;
|
||||
|
||||
static void ble_hid_log_buffer(const char *label, const uint8_t *data, size_t len)
|
||||
{
|
||||
char hex[32];
|
||||
size_t pos = 0;
|
||||
|
||||
for (size_t i = 0; i < len && pos + 3 < sizeof(hex); i++) {
|
||||
pos += (size_t)snprintf(hex + pos, sizeof(hex) - pos, "%02x ", data[i]);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "%s len=%u: %s", label, (unsigned)len, hex);
|
||||
}
|
||||
|
||||
static void ble_hid_send_idle_report(void)
|
||||
{
|
||||
uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0};
|
||||
|
||||
if (!ble_hid_ready()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ble_hid_log_buffer("idle", buffer, sizeof(buffer));
|
||||
(void)esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer,
|
||||
sizeof(buffer));
|
||||
}
|
||||
|
||||
static void ble_hid_idle_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
/* Windows often enables notify a few seconds after connect. */
|
||||
vTaskDelay(pdMS_TO_TICKS(3500));
|
||||
ble_hid_send_idle_report();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void ble_hid_hidd_event(void *handler_args, esp_event_base_t base, int32_t id,
|
||||
void *event_data)
|
||||
{
|
||||
esp_hidd_event_t event = (esp_hidd_event_t)id;
|
||||
esp_hidd_event_data_t *param = (esp_hidd_event_data_t *)event_data;
|
||||
|
||||
(void)handler_args;
|
||||
(void)base;
|
||||
|
||||
switch (event) {
|
||||
case ESP_HIDD_START_EVENT:
|
||||
ESP_LOGI(TAG, "HID stack started");
|
||||
esp_hid_ble_gap_adv_start();
|
||||
break;
|
||||
|
||||
case ESP_HIDD_CONNECT_EVENT:
|
||||
if (param->connect.status == 0) {
|
||||
s_connected = true;
|
||||
ESP_LOGI(TAG, "HID connected");
|
||||
if (s_status_cb != NULL) {
|
||||
s_status_cb(true);
|
||||
}
|
||||
xTaskCreate(ble_hid_idle_task, "ble_hid_idle", 2048, NULL, 5, NULL);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "HID connect failed status=%d", param->connect.status);
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_HIDD_DISCONNECT_EVENT:
|
||||
s_connected = false;
|
||||
ESP_LOGI(TAG, "HID disconnected reason=%d", param->disconnect.reason);
|
||||
if (s_status_cb != NULL) {
|
||||
s_status_cb(false);
|
||||
}
|
||||
esp_hid_ble_gap_adv_start();
|
||||
break;
|
||||
|
||||
case ESP_HIDD_PROTOCOL_MODE_EVENT:
|
||||
ESP_LOGI(TAG, "protocol mode map=%u mode=%s",
|
||||
param->protocol_mode.map_index,
|
||||
param->protocol_mode.protocol_mode ? "REPORT" : "BOOT");
|
||||
break;
|
||||
|
||||
case ESP_HIDD_CONTROL_EVENT:
|
||||
ESP_LOGI(TAG, "control map=%u %sSUSPEND",
|
||||
param->control.map_index,
|
||||
param->control.control ? "EXIT_" : "");
|
||||
break;
|
||||
|
||||
case ESP_HIDD_OUTPUT_EVENT:
|
||||
ESP_LOGD(TAG, "output map=%u id=%u len=%d",
|
||||
param->output.map_index, param->output.report_id, param->output.length);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int ble_hid_init(ble_hid_status_cb_t status_cb)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
s_status_cb = status_cb;
|
||||
s_connected = false;
|
||||
|
||||
ret = esp_hidd_dev_init(&s_hid_config, ESP_HID_TRANSPORT_BLE, ble_hid_hidd_event,
|
||||
&s_hid_dev);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_hidd_dev_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "esp_hidd keyboard ready (report payload=%u bytes)",
|
||||
(unsigned)HID_KEYBOARD_REPORT_LEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ble_hid_on_input_notify(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
ble_hid_send_idle_report();
|
||||
}
|
||||
}
|
||||
|
||||
bool ble_hid_ready(void)
|
||||
{
|
||||
return s_connected && s_hid_dev != NULL;
|
||||
}
|
||||
|
||||
int ble_hid_key_press(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
if (!ble_hid_ready()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
buffer[0] = modifier;
|
||||
buffer[2] = keycode;
|
||||
ble_hid_log_buffer("press", buffer, sizeof(buffer));
|
||||
|
||||
ret = esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer,
|
||||
sizeof(buffer));
|
||||
return (ret == ESP_OK) ? 0 : ret;
|
||||
}
|
||||
|
||||
int ble_hid_key_release(void)
|
||||
{
|
||||
uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
if (!ble_hid_ready()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ble_hid_log_buffer("release", buffer, sizeof(buffer));
|
||||
ret = esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer,
|
||||
sizeof(buffer));
|
||||
return (ret == ESP_OK) ? 0 : ret;
|
||||
}
|
||||
|
||||
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_hid_key_press(modifier, keycode);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
return ble_hid_key_release();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int ble_hid_init(ble_hid_status_cb_t status_cb)
|
||||
{
|
||||
(void)status_cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ble_hid_ready(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int ble_hid_key_press(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
(void)modifier;
|
||||
(void)keycode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ble_hid_key_release(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
(void)modifier;
|
||||
(void)keycode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* BOARD_BLE_ENABLED */
|
||||
Reference in New Issue
Block a user