Skip to content

Commit

Permalink
Merge branch 'feat/add_usb_hid_keyboard_all_key' into 'master'
Browse files Browse the repository at this point in the history
feat(usb hid): Add all key mode

See merge request ae_group/esp-iot-solution!959
  • Loading branch information
leeebo committed Feb 26, 2024
2 parents 48faab5 + 848ca99 commit 272ec0e
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 57 deletions.
9 changes: 6 additions & 3 deletions examples/usb/device/usb_hid_device/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

This example demonstrates how to use ESP32-Sx USB function as the HID device. Buttons are used to trigger such signals as a keyboard or mouse.

* Supports traditional six-key keyboard mode
* Supports full key no-conflict keyboard mode

## How to use the example

### Hardware Required

- Any ESP32-S2 or ESP32-S3 development board with **buttons**
- Hardware Connection:

- Hardware Connection:
- GPIO19 to D
- GPIO20 to D+

Expand Down Expand Up @@ -43,7 +46,7 @@ By default the buttons act as a mouse, you can use `idf.py menuconfig` change `U
## Example Output

```
I (393) gpio: GPIO[14]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (393) gpio: GPIO[14]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (403) HID Example: Button io = 14 created
I (403) HID Example: HID Mouse demo: press button to simulate mouse
I (413) HID Example: Wait Mount through USB interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ tusb_desc_device_t const desc_device = {
};

uint8_t const desc_hid_report[] = {
#if CONFIG_ENABLE_FULL_KEY_KEYBOARD
TUD_HID_REPORT_DESC_FULL_KEY_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)),
#else
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)),
#endif
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE))
};

Expand Down
42 changes: 42 additions & 0 deletions examples/usb/device/usb_hid_device/hid_device/usb_descriptors.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,46 @@ enum {
EPNUM_HID_DATA,
};

// Keyboard Report Descriptor Full Key Template
#define TUD_HID_REPORT_DESC_FULL_KEY_KEYBOARD(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
__VA_ARGS__ \
/* 8 bits Modifier Keys (Shift, Control, Alt) */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
HID_USAGE_MIN ( 224 ) ,\
HID_USAGE_MAX ( 231 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX ( 1 ) ,\
HID_REPORT_COUNT ( 8 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
/* 8 bit reserved */ \
HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_CONSTANT ) ,\
/* Output 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\
HID_USAGE_MIN ( 1 ) ,\
HID_USAGE_MAX ( 5 ) ,\
HID_REPORT_COUNT ( 5 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
/* led padding */ \
HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 3 ) ,\
HID_OUTPUT ( HID_CONSTANT ) ,\
/* 15-byte Keycodes */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
HID_USAGE_MIN ( 4 ) ,\
HID_USAGE_MAX ( 124 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX ( 1 ) ,\
HID_REPORT_COUNT ( 120 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
HID_COLLECTION_END \

#endif
4 changes: 4 additions & 0 deletions examples/usb/device/usb_hid_device/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ menu "USB HID Device Example"
string "Product Name"
default "HID Demo"

config ENABLE_FULL_KEY_KEYBOARD
bool "Enable Full Key Keyboard"
default n

endmenu
169 changes: 127 additions & 42 deletions examples/usb/device/usb_hid_device/main/tinyusb_hid.c
Original file line number Diff line number Diff line change
@@ -1,18 +1,54 @@
/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdint.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "tinyusb_hid.h"
#include "usb_descriptors.h"
#include "device/usbd.h"

static const char *TAG = "tinyusb_hid.h";
static bool s_keyboard_pressed = false;

typedef struct {
uint32_t report_id; // Report identifier
union {
struct {
uint8_t button; // Button value
int8_t x; // X coordinate
int8_t y; // Y coordinate
int8_t vertical; // Vertical scroll
int8_t horizontal; // Horizontal scroll
} mouse_report; // Mouse report
#if CONFIG_ENABLE_FULL_KEY_KEYBOARD
struct {
uint8_t modifier; // Modifier keys
uint8_t reserved; // Reserved byte
uint8_t keycode[15]; // Keycode
} keyboard_report; // Keyboard report
#else
struct {
uint8_t modifier; // Modifier keys
uint8_t reserved; // Reserved byte
uint8_t keycode[6]; // Keycodes
} keyboard_report; // Keyboard report
#endif
};
} tinyusb_hid_report_t;

typedef struct {
TaskHandle_t task_handle;
QueueHandle_t hid_queue;
} tinyusb_hid_t;

static tinyusb_hid_t *s_tinyusb_hid = NULL;

void tinyusb_hid_mouse_move_report(int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
{
Expand All @@ -24,15 +60,13 @@ void tinyusb_hid_mouse_move_report(int8_t x, int8_t y, int8_t vertical, int8_t h
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
} else {
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
// skip if hid is not ready yet
if (!tud_hid_ready()) {
ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__);
return;
}

tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, x, y, vertical, horizontal);

tinyusb_hid_report_t report = {0};
report.report_id = REPORT_ID_MOUSE;
report.mouse_report.x = x;
report.mouse_report.y = y;
report.mouse_report.vertical = vertical;
report.mouse_report.horizontal = horizontal;
xQueueSend(s_tinyusb_hid->hid_queue, &report, portMAX_DELAY);
}
}

Expand All @@ -46,46 +80,101 @@ void tinyusb_hid_mouse_button_report(uint8_t buttons_map)
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
} else {
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
// skip if hid is not ready yet
if (!tud_hid_ready()) {
ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__);
return;
}

tud_hid_mouse_report(REPORT_ID_MOUSE, buttons_map, 0, 0, 0, 0);

tinyusb_hid_report_t report = {0};
report.report_id = REPORT_ID_MOUSE;
report.mouse_report.button = buttons_map;
xQueueSend(s_tinyusb_hid->hid_queue, &report, portMAX_DELAY);
}
}

void tinyusb_hid_keyboard_report(uint8_t keycode[])
#if CONFIG_ENABLE_FULL_KEY_KEYBOARD
void tinyusb_hid_keyboard_report(uint8_t modifier, uint8_t keycode[])
{
ESP_LOGD(TAG, "Keycode array: %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
keycode[0], keycode[1], keycode[2], keycode[3], keycode[4], keycode[5],
keycode[6], keycode[7], keycode[8], keycode[9], keycode[10], keycode[11],
keycode[12], keycode[13], keycode[14]);
// Remote wakeup
if (tud_suspended()) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
} else {
tinyusb_hid_report_t report = {0};
report.report_id = REPORT_ID_KEYBOARD;
report.keyboard_report.modifier = modifier;
memcpy(report.keyboard_report.keycode, keycode, sizeof(report.keyboard_report.keycode));
xQueueSend(s_tinyusb_hid->hid_queue, &report, portMAX_DELAY);
}
}
#else
void tinyusb_hid_keyboard_report(uint8_t modifier, uint8_t keycode[])
{
ESP_LOGD(TAG, "keycode = %u %u %u %u %u %u", keycode[0], keycode[1], keycode[2], keycode[3], keycode[4], keycode[5]);

// Remote wakeup
if (tud_suspended()) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
} else {
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
// skip if hid is not ready yet
if (!tud_hid_ready()) {
ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__);
return;
}
tinyusb_hid_report_t report = {0};
report.report_id = REPORT_ID_KEYBOARD;
report.keyboard_report.modifier = modifier;
memcpy(report.keyboard_report.keycode, keycode, sizeof(report.keyboard_report.keycode));
xQueueSend(s_tinyusb_hid->hid_queue, &report, portMAX_DELAY);
}
}
#endif

uint8_t _keycode[6] = { 0 };
_keycode[0] = keycode[0];
_keycode[1] = keycode[1];
_keycode[2] = keycode[2];
_keycode[3] = keycode[3];
_keycode[4] = keycode[4];
_keycode[5] = keycode[5];
// tinyusb_hid_task function to process the HID reports
static void tinyusb_hid_task(void *arg)
{
(void) arg;
tinyusb_hid_report_t report;
while (1) {
if (xQueueReceive(s_tinyusb_hid->hid_queue, &report, portMAX_DELAY)) {
// Remote wakeup
if (tud_suspended()) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
xQueueReset(s_tinyusb_hid->hid_queue);
} else {
if (report.report_id == REPORT_ID_MOUSE) {
tud_hid_mouse_report(REPORT_ID_MOUSE, report.mouse_report.button, report.mouse_report.x, report.mouse_report.y, report.mouse_report.vertical, report.mouse_report.horizontal);
} else if (report.report_id == REPORT_ID_KEYBOARD) {
tud_hid_n_report(0, REPORT_ID_KEYBOARD, &report.keyboard_report, sizeof(report.keyboard_report));
} else {
// Unknown report
continue;
}
// Wait until report is sent
if (!ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100))) {
ESP_LOGW(TAG, "Report not sent");
}
}
}
}
}

tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, _keycode);
s_keyboard_pressed = true;
esp_err_t tinyusb_hid_init(void)
{
if (s_tinyusb_hid) {
ESP_LOGW(TAG, "tinyusb_hid already initialized");
return ESP_OK;
}
esp_err_t ret = ESP_OK;
s_tinyusb_hid = calloc(1, sizeof(tinyusb_hid_t));
ESP_RETURN_ON_FALSE(s_tinyusb_hid, ESP_ERR_NO_MEM, TAG, "calloc failed");
s_tinyusb_hid->hid_queue = xQueueCreate(10, sizeof(tinyusb_hid_report_t)); // Adjust queue length and item size as per your requirement
ESP_GOTO_ON_FALSE(s_tinyusb_hid->hid_queue, ESP_ERR_NO_MEM, fail, TAG, "xQueueCreate failed");
xTaskCreate(tinyusb_hid_task, "tinyusb_hid_task", 4096, NULL, 5, &s_tinyusb_hid->task_handle);
xTaskNotifyGive(s_tinyusb_hid->task_handle);
return ret;
fail:
free(s_tinyusb_hid);
s_tinyusb_hid = NULL;
return ret;
}

/************************************************** TinyUSB callbacks ***********************************************/
Expand All @@ -96,12 +185,8 @@ void tud_hid_report_complete_cb(uint8_t itf, uint8_t const *report, uint16_t len
{
(void) itf;
(void) len;
uint8_t report_id = report[0];

if (report_id == REPORT_ID_KEYBOARD && s_keyboard_pressed) {
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
s_keyboard_pressed = false;
}
xTaskNotifyGive(s_tinyusb_hid->task_handle);
}

// Invoked when received GET_REPORT control request
Expand Down
17 changes: 14 additions & 3 deletions examples/usb/device/usb_hid_device/main/tinyusb_hid.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -12,6 +12,15 @@ extern "C" {
#include <stdint.h>
#include "tusb.h"

/**
* @brief Initialize tinyusb HID device.
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: No memory
*/
esp_err_t tinyusb_hid_init(void);

/**
* @brief Report delta movement of mouse.
*
Expand All @@ -31,11 +40,13 @@ void tinyusb_hid_mouse_move_report(int8_t x, int8_t y, int8_t vertical, int8_t h
void tinyusb_hid_mouse_button_report(uint8_t buttons_map);

/**
* @brief Report key press in the keyboard, using array here, contains six keys at most.
* @brief Report key press in the keyboard, using array here,
* if open CONFIG_ENABLE_FULL_KEY_KEYBOARD contains all keys at once
* else contains six keys at most.
*
* @param keycode hid keyboard code array
*/
void tinyusb_hid_keyboard_report(uint8_t keycode[]);
void tinyusb_hid_keyboard_report(uint8_t modifier, uint8_t keycode[]);

//--------------------------------------------------------------------+
// HID MOUSE BUTTON BIT MASK
Expand Down
Loading

0 comments on commit 272ec0e

Please sign in to comment.