feat: 添加第三屏幕及其音量控制功能

- 在 CMakeLists.txt 中添加 ui_Screen3.c 源文件以支持新屏幕
- 新增 ui_events.c 和 ui_events.h,定义音量控制按钮的点击事件处理函数
- 实现 ui_Screen3.c,构建第三屏幕的 UI 组件和事件回调
- 更新 ui.c 以初始化和加载第三屏幕
- 修改 ble_hid.c 和 ble_hid.h,添加消费者控制功能以支持音量调节
- 更新相关头文件,确保新屏幕与现有结构兼容
This commit is contained in:
2026-07-02 22:23:20 +08:00
parent e056a113a2
commit 8254bc7d50
10 changed files with 513 additions and 33 deletions
+241 -32
View File
@@ -16,22 +16,126 @@
static const char *TAG = "ble_hid";
#define HID_KEYBOARD_REPORT_ID 1
#define HID_KEYBOARD_REPORT_LEN 7
#define HID_KEYBOARD_REPORT_ID 1
#define HID_CONSUMER_REPORT_ID 2
#define HID_KEYBOARD_REPORT_LEN 8
#define HID_CONSUMER_REPORT_LEN 2
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,
/* Bit positions for ESP-IDF mediaReportMap2 (Windows-compatible). */
#define HID_CC_RPT_VOLUME_BITS 0x3F
#define HID_CC_RPT_BUTTON_BITS 0xF0
#define HID_CC_RPT_VOLUME_UP 0x40
#define HID_CC_RPT_VOLUME_DOWN 0x80
#define HID_CC_RPT_MUTE 1
#define HID_CC_RPT_PLAY 5
#define HID_CC_RPT_SCAN_NEXT_TRK 10
#define HID_CC_RPT_SCAN_PREV_TRK 11
/*
* Keyboard (Report ID 1) + Consumer Control (Report ID 2, mediaReportMap2).
* Windows 11 expects the bit-packed consumer report from ESP-IDF examples,
* not a raw 16-bit HID usage in the input report.
*/
static const unsigned char s_hid_report_map[] = {
/* Keyboard - Report ID 1 */
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, HID_KEYBOARD_REPORT_ID,
0x05, 0x07, // Usage Page (Keyboard/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, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x03, // Input (Constant, Variable, Absolute)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x05, // Usage Maximum (0x05)
0x91, 0x02, // Output (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x03, // Output (Constant, Variable, Absolute)
0x95, 0x05, // Report Count (5)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Keyboard/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x29, 0x65, // Usage Maximum (0x65)
0x81, 0x00, // Input (Data, Array, Absolute)
0xC0, // End Collection
/* Consumer Control - Report ID 2 (mediaReportMap2 from ESP-IDF) */
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, HID_CONSUMER_REPORT_ID,
0x09, 0x02, // Usage (Numeric Key Pad)
0xA1, 0x02, // Collection (Logical)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0A, // Usage Maximum (0x0A)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x0A, // Logical Maximum (10)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x00, // Input (Data, Array, Absolute)
0xC0, // End Collection
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x86, // Usage (Channel)
0x15, 0xFF, // Logical Minimum (-1)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x02, // Report Size (2)
0x95, 0x01, // Report Count (1)
0x81, 0x46, // Input (Data, Variable, Relative)
0x09, 0xE9, // Usage (Volume Increment)
0x09, 0xEA, // Usage (Volume Decrement)
0x15, 0x00, // Logical Minimum (0)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x09, 0xE2, // Usage (Mute)
0x09, 0x30, // Usage (Power)
0x09, 0x83, // Usage (Recall Last)
0x09, 0x81, // Usage (Assign Selection)
0x09, 0xB0, // Usage (Play)
0x09, 0xB1, // Usage (Pause)
0x09, 0xB2, // Usage (Record)
0x09, 0xB3, // Usage (Fast Forward)
0x09, 0xB4, // Usage (Rewind)
0x09, 0xB5, // Usage (Scan Next Track)
0x09, 0xB6, // Usage (Scan Previous Track)
0x09, 0xB7, // Usage (Stop)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x0C, // Logical Maximum (12)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x00, // Input (Data, Array, Absolute)
0x09, 0x80, // Usage (Selection)
0xA1, 0x02, // Collection (Logical)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x03, // Usage Maximum (0x03)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x03, // Logical Maximum (3)
0x75, 0x02, // Report Size (2)
0x81, 0x00, // Input (Data, Array, Absolute)
0xC0, // End Collection
0x81, 0x03, // Input (Constant, Variable, Absolute)
0xC0, // End Collection
};
static esp_hid_raw_report_map_t s_report_maps[] = {
{
.data = s_keyboard_report_map,
.len = sizeof(s_keyboard_report_map),
.data = s_hid_report_map,
.len = sizeof(s_hid_report_map),
},
};
@@ -50,9 +154,56 @@ 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_cc_set_volume_up(uint8_t *buffer)
{
buffer[0] &= HID_CC_RPT_VOLUME_BITS;
buffer[0] |= HID_CC_RPT_VOLUME_UP;
}
static void ble_hid_cc_set_volume_down(uint8_t *buffer)
{
buffer[0] &= HID_CC_RPT_VOLUME_BITS;
buffer[0] |= HID_CC_RPT_VOLUME_DOWN;
}
static void ble_hid_cc_set_button(uint8_t *buffer, uint8_t button)
{
buffer[1] &= HID_CC_RPT_BUTTON_BITS;
buffer[1] |= button;
}
static bool ble_hid_encode_consumer_report(uint16_t usage, uint8_t buffer[HID_CONSUMER_REPORT_LEN])
{
memset(buffer, 0, HID_CONSUMER_REPORT_LEN);
switch (usage) {
case HID_CONSUMER_VOLUME_UP:
ble_hid_cc_set_volume_up(buffer);
return true;
case HID_CONSUMER_VOLUME_DOWN:
ble_hid_cc_set_volume_down(buffer);
return true;
case HID_CONSUMER_MUTE:
ble_hid_cc_set_button(buffer, HID_CC_RPT_MUTE);
return true;
case HID_CONSUMER_PLAY_PAUSE:
ble_hid_cc_set_button(buffer, HID_CC_RPT_PLAY);
return true;
case HID_CONSUMER_SCAN_NEXT:
ble_hid_cc_set_button(buffer, HID_CC_RPT_SCAN_NEXT_TRK);
return true;
case HID_CONSUMER_SCAN_PREVIOUS:
ble_hid_cc_set_button(buffer, HID_CC_RPT_SCAN_PREV_TRK);
return true;
default:
ESP_LOGW(TAG, "unsupported consumer usage 0x%04X", usage);
return false;
}
}
static void ble_hid_log_buffer(const char *label, const uint8_t *data, size_t len)
{
char hex[32];
char hex[40];
size_t pos = 0;
for (size_t i = 0; i < len && pos + 3 < sizeof(hex); i++) {
@@ -62,17 +213,33 @@ static void ble_hid_log_buffer(const char *label, const uint8_t *data, size_t le
ESP_LOGI(TAG, "%s len=%u: %s", label, (unsigned)len, hex);
}
static void ble_hid_send_idle_report(void)
static int ble_hid_send_input_report(uint8_t report_id, const uint8_t *buffer, size_t len,
const char *label)
{
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(label, buffer, len);
ret = esp_hidd_dev_input_set(s_hid_dev, 0, report_id, (uint8_t *)buffer, len);
return (ret == ESP_OK) ? 0 : ret;
}
static void ble_hid_send_idle_reports(void)
{
uint8_t keyboard[HID_KEYBOARD_REPORT_LEN] = {0};
uint8_t consumer[HID_CONSUMER_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));
(void)ble_hid_send_input_report(HID_KEYBOARD_REPORT_ID, keyboard, sizeof(keyboard),
"keyboard idle");
(void)ble_hid_send_input_report(HID_CONSUMER_REPORT_ID, consumer, sizeof(consumer),
"consumer idle");
}
static void ble_hid_idle_task(void *arg)
@@ -80,7 +247,7 @@ 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();
ble_hid_send_idle_reports();
vTaskDelete(NULL);
}
@@ -157,15 +324,15 @@ int ble_hid_init(ble_hid_status_cb_t status_cb)
return ret;
}
ESP_LOGI(TAG, "esp_hidd keyboard ready (report payload=%u bytes)",
(unsigned)HID_KEYBOARD_REPORT_LEN);
ESP_LOGI(TAG, "esp_hidd ready (keyboard=%u bytes, consumer=%u bytes)",
(unsigned)HID_KEYBOARD_REPORT_LEN, (unsigned)HID_CONSUMER_REPORT_LEN);
return ESP_OK;
}
void ble_hid_on_input_notify(bool enabled)
{
if (enabled) {
ble_hid_send_idle_report();
ble_hid_send_idle_reports();
}
}
@@ -177,7 +344,6 @@ bool ble_hid_ready(void)
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;
@@ -185,26 +351,18 @@ int ble_hid_key_press(uint8_t modifier, uint8_t keycode)
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;
return ble_hid_send_input_report(HID_KEYBOARD_REPORT_ID, buffer, sizeof(buffer), "press");
}
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;
return ble_hid_send_input_report(HID_KEYBOARD_REPORT_ID, buffer, sizeof(buffer), "release");
}
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode)
@@ -220,6 +378,51 @@ int ble_hid_send_combo(uint8_t modifier, uint8_t keycode)
return ble_hid_key_release();
}
int ble_hid_consumer_press(uint16_t usage)
{
uint8_t buffer[HID_CONSUMER_REPORT_LEN] = {0};
if (!ble_hid_ready()) {
return ESP_ERR_INVALID_STATE;
}
if (usage == 0) {
return ESP_ERR_INVALID_ARG;
}
if (!ble_hid_encode_consumer_report(usage, buffer)) {
return ESP_ERR_NOT_SUPPORTED;
}
return ble_hid_send_input_report(HID_CONSUMER_REPORT_ID, buffer, sizeof(buffer),
"consumer press");
}
int ble_hid_consumer_release(void)
{
uint8_t buffer[HID_CONSUMER_REPORT_LEN] = {0};
if (!ble_hid_ready()) {
return ESP_ERR_INVALID_STATE;
}
return ble_hid_send_input_report(HID_CONSUMER_REPORT_ID, buffer, sizeof(buffer),
"consumer release");
}
int ble_hid_send_consumer(uint16_t usage)
{
int rc;
rc = ble_hid_consumer_press(usage);
if (rc != 0) {
return rc;
}
vTaskDelay(pdMS_TO_TICKS(100));
return ble_hid_consumer_release();
}
#else
int ble_hid_init(ble_hid_status_cb_t status_cb)
@@ -252,4 +455,10 @@ int ble_hid_send_combo(uint8_t modifier, uint8_t keycode)
return -1;
}
int ble_hid_send_consumer(uint16_t usage)
{
(void)usage;
return -1;
}
#endif /* BOARD_BLE_ENABLED */
+8
View File
@@ -13,6 +13,13 @@
#define HID_KEY_X 0x1B
#define HID_KEY_F5 0x3E
#define HID_CONSUMER_VOLUME_UP 0x00E9
#define HID_CONSUMER_VOLUME_DOWN 0x00EA
#define HID_CONSUMER_MUTE 0x00E2
#define HID_CONSUMER_PLAY_PAUSE 0x00CD
#define HID_CONSUMER_SCAN_NEXT 0x00B5
#define HID_CONSUMER_SCAN_PREVIOUS 0x00B6
typedef void (*ble_hid_status_cb_t)(bool connected);
int ble_hid_init(ble_hid_status_cb_t status_cb);
@@ -21,6 +28,7 @@ bool ble_hid_ready(void);
int ble_hid_key_press(uint8_t modifier, uint8_t keycode);
int ble_hid_key_release(void);
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode);
int ble_hid_send_consumer(uint16_t usage);
#if defined(BOARD_BLE_ENABLED) && BOARD_BLE_ENABLED
void ble_hid_on_input_notify(bool enabled);