I am trying to use this arduino library to get an esp32-s3 (t-display-s3 to be exact) in host mode: GitHub - esp32beans/ESP32_USB_Host_HID: ESP32 USB Host HID Keyboard for Arduino
I have used their hid_host_example.ino to test some keyboard and mice when i connect it but I get nothing in the logs when i connect the USB device to the esp32. The esp32-s3 is being powered by a 5v 20amp power supply on the 5v and ground pins of the esp32. Then a usb c to usb a adapter is connected and then the keyboard dongle is connected. When i try to type anything on the keyboard I get nothing in the logs.
Attached is my hid_host_example.ino. I am using webserial to view the logs because the usb port is taken from the esp32.
How do i get this to work. I've been trying to get this to work for several days and no luck.
Here is the code:
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "driver/gpio.h"
#include "usb/usb_host.h"
#include "hid_host.h"
#include "hid_usage_keyboard.h"
#include "hid_usage_mouse.h"
/* GPIO Pin number for quit from example logic */
#define APP_QUIT_PIN GPIO_NUM_0
#include <Arduino.h>
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(ESP32)
#include <WiFi.h>
#include <AsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
#include <WebSerial.h>
AsyncWebServer server(80);
const char* ssid = "blank"; // Your WiFi SSID
const char* password = "blank"; // Your WiFi Password
unsigned long last_print_time = millis();
static const char *TAG = "example";
QueueHandle_t hid_host_event_queue;
bool user_shutdown = false;
/**
* @brief HID Host event
*
* This event is used for delivering the HID Host event from callback to a task.
*/
typedef struct {
hid_host_device_handle_t hid_device_handle;
hid_host_driver_event_t event;
void *arg;
} hid_host_event_queue_t;
/**
* @brief HID Protocol string names
*/
static const char *hid_proto_name_str[] = {"NONE", "KEYBOARD", "MOUSE"};
/**
* @brief Key event
*/
typedef struct {
enum key_state { KEY_STATE_PRESSED = 0x00, KEY_STATE_RELEASED = 0x01 } state;
uint8_t modifier;
uint8_t key_code;
} key_event_t;
/* Main char symbol for ENTER key */
#define KEYBOARD_ENTER_MAIN_CHAR '\r'
/* When set to 1 pressing ENTER will be extending with LineFeed during serial
* debug output */
#define KEYBOARD_ENTER_LF_EXTEND 1
/**
* @brief Scancode to ascii table
*/
const uint8_t keycode2ascii[57][2] = {
{0, 0}, /* HID_KEY_NO_PRESS */
{0, 0}, /* HID_KEY_ROLLOVER */
{0, 0}, /* HID_KEY_POST_FAIL */
{0, 0}, /* HID_KEY_ERROR_UNDEFINED */
{'a', 'A'}, /* HID_KEY_A */
{'b', 'B'}, /* HID_KEY_B */
{'c', 'C'}, /* HID_KEY_C */
{'d', 'D'}, /* HID_KEY_D */
{'e', 'E'}, /* HID_KEY_E */
{'f', 'F'}, /* HID_KEY_F */
{'g', 'G'}, /* HID_KEY_G */
{'h', 'H'}, /* HID_KEY_H */
{'i', 'I'}, /* HID_KEY_I */
{'j', 'J'}, /* HID_KEY_J */
{'k', 'K'}, /* HID_KEY_K */
{'l', 'L'}, /* HID_KEY_L */
{'m', 'M'}, /* HID_KEY_M */
{'n', 'N'}, /* HID_KEY_N */
{'o', 'O'}, /* HID_KEY_O */
{'p', 'P'}, /* HID_KEY_P */
{'q', 'Q'}, /* HID_KEY_Q */
{'r', 'R'}, /* HID_KEY_R */
{'s', 'S'}, /* HID_KEY_S */
{'t', 'T'}, /* HID_KEY_T */
{'u', 'U'}, /* HID_KEY_U */
{'v', 'V'}, /* HID_KEY_V */
{'w', 'W'}, /* HID_KEY_W */
{'x', 'X'}, /* HID_KEY_X */
{'y', 'Y'}, /* HID_KEY_Y */
{'z', 'Z'}, /* HID_KEY_Z */
{'1', '!'}, /* HID_KEY_1 */
{'2', '@'}, /* HID_KEY_2 */
{'3', '#'}, /* HID_KEY_3 */
{'4', '$'}, /* HID_KEY_4 */
{'5', '%'}, /* HID_KEY_5 */
{'6', '^'}, /* HID_KEY_6 */
{'7', '&'}, /* HID_KEY_7 */
{'8', '*'}, /* HID_KEY_8 */
{'9', '('}, /* HID_KEY_9 */
{'0', ')'}, /* HID_KEY_0 */
{KEYBOARD_ENTER_MAIN_CHAR, KEYBOARD_ENTER_MAIN_CHAR}, /* HID_KEY_ENTER */
{0, 0}, /* HID_KEY_ESC */
{'\b', 0}, /* HID_KEY_DEL */
{0, 0}, /* HID_KEY_TAB */
{' ', ' '}, /* HID_KEY_SPACE */
{'-', '_'}, /* HID_KEY_MINUS */
{'=', '+'}, /* HID_KEY_EQUAL */
{'[', '{'}, /* HID_KEY_OPEN_BRACKET */
{']', '}'}, /* HID_KEY_CLOSE_BRACKET */
{'\\', '|'}, /* HID_KEY_BACK_SLASH */
{'\\', '|'},
/* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat
// HID_KEY_BACK_SLASH
{';', ':'}, /* HID_KEY_COLON */
{'\'', '"'}, /* HID_KEY_QUOTE */
{'`', '~'}, /* HID_KEY_TILDE */
{',', '<'}, /* HID_KEY_LESS */
{'.', '>'}, /* HID_KEY_GREATER */
{'/', '?'} /* HID_KEY_SLASH */
};
/**
* @brief Makes new line depending on report output protocol type
*
* @param[in] proto Current protocol to output
*/
static void hid_print_new_device_report_header(hid_protocol_t proto) {
static hid_protocol_t prev_proto_output = HID_PROTOCOL_MAX;
if (prev_proto_output != proto) {
prev_proto_output = proto;
printf("\r\n");
if (proto == HID_PROTOCOL_MOUSE) {
printf("Mouse\r\n");
} else if (proto == HID_PROTOCOL_KEYBOARD) {
printf("Keyboard\r\n");
} else {
printf("Generic\r\n");
}
fflush(stdout);
}
}
/**
* @brief HID Keyboard modifier verification for capitalization application
* (right or left shift)
*
* @param[in] modifier
* @return true Modifier was pressed (left or right shift)
* @return false Modifier was not pressed (left or right shift)
*
*/
static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier) {
if (((modifier & HID_LEFT_SHIFT) == HID_LEFT_SHIFT) ||
((modifier & HID_RIGHT_SHIFT) == HID_RIGHT_SHIFT)) {
return true;
}
return false;
}
/**
* @brief HID Keyboard get char symbol from key code
*
* @param[in] modifier Keyboard modifier data
* @param[in] key_code Keyboard key code
* @param[in] key_char Pointer to key char data
*
* @return true Key scancode converted successfully
* @return false Key scancode unknown
*/
static inline bool hid_keyboard_get_char(uint8_t modifier, uint8_t key_code,
unsigned char *key_char) {
uint8_t mod = (hid_keyboard_is_modifier_shift(modifier)) ? 1 : 0;
if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_SLASH)) {
*key_char = keycode2ascii[key_code][mod];
} else {
// All other key pressed
return false;
}
return true;
}
/**
* @brief HID Keyboard print char symbol
*
* @param[in] key_char Keyboard char to stdout
*/
static inline void hid_keyboard_print_char(unsigned int key_char) {
if (!!key_char) {
putchar(key_char);
#if (KEYBOARD_ENTER_LF_EXTEND)
if (KEYBOARD_ENTER_MAIN_CHAR == key_char) {
putchar('\n');
}
#endif // KEYBOARD_ENTER_LF_EXTEND
fflush(stdout);
}
}
/**
* @brief Key Event. Key event with the key code, state and modifier.
*
* @param[in] key_event Pointer to Key Event structure
*
*/
static void key_event_callback(key_event_t *key_event) {
unsigned char key_char;
hid_print_new_device_report_header(HID_PROTOCOL_KEYBOARD);
if (key_event->KEY_STATE_PRESSED == key_event->state) {
if (hid_keyboard_get_char(key_event->modifier, key_event->key_code,
&key_char)) {
hid_keyboard_print_char(key_char);
}
}
}
/**
* @brief Key buffer scan code search.
*
* @param[in] src Pointer to source buffer where to search
* @param[in] key Key scancode to search
* @param[in] length Size of the source buffer
*/
static inline bool key_found(const uint8_t *const src, uint8_t key,
unsigned int length) {
for (unsigned int i = 0; i < length; i++) {
if (src[i] == key) {
return true;
}
}
return false;
}
/**
* @brief USB HID Host Keyboard Interface report callback handler
*
* @param[in] data Pointer to input report data buffer
* @param[in] length Length of input report data buffer
*/
static void hid_host_keyboard_report_callback(const uint8_t *const data,
const int length) {
hid_keyboard_input_report_boot_t *kb_report =
(hid_keyboard_input_report_boot_t *)data;
if (length < sizeof(hid_keyboard_input_report_boot_t)) {
return;
}
static uint8_t prev_keys[HID_KEYBOARD_KEY_MAX] = {0};
key_event_t key_event;
for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
// key has been released verification
if (prev_keys[i] > HID_KEY_ERROR_UNDEFINED &&
!key_found(kb_report->key, prev_keys[i], HID_KEYBOARD_KEY_MAX)) {
key_event.key_code = prev_keys[i];
key_event.modifier = 0;
key_event.state = key_event.KEY_STATE_RELEASED;
key_event_callback(&key_event);
}
// key has been pressed verification
if (kb_report->key[i] > HID_KEY_ERROR_UNDEFINED &&
!key_found(prev_keys, kb_report->key[i], HID_KEYBOARD_KEY_MAX)) {
key_event.key_code = kb_report->key[i];
key_event.modifier = kb_report->modifier.val;
key_event.state = key_event.KEY_STATE_PRESSED;
key_event_callback(&key_event);
}
}
memcpy(prev_keys, &kb_report->key, HID_KEYBOARD_KEY_MAX);
}
/**
* @brief USB HID Host Mouse Interface report callback handler
*
* @param[in] data Pointer to input report data buffer
* @param[in] length Length of input report data buffer
*/
static void hid_host_mouse_report_callback(const uint8_t *const data,
const int length) {
hid_mouse_input_report_boot_t *mouse_report =
(hid_mouse_input_report_boot_t *)data;
if (length < sizeof(hid_mouse_input_report_boot_t)) {
return;
}
static int x_pos = 0;
static int y_pos = 0;
// Calculate absolute position from displacement
x_pos += mouse_report->x_displacement;
y_pos += mouse_report->y_displacement;
hid_print_new_device_report_header(HID_PROTOCOL_MOUSE);
printf("X: %06d\tY: %06d\t|%c|%c|\r", x_pos, y_pos,
(mouse_report->buttons.button1 ? 'o' : ' '),
(mouse_report->buttons.button2 ? 'o' : ' '));
fflush(stdout);
}
/**
* @brief USB HID Host Generic Interface report callback handler
*
* 'generic' means anything else than mouse or keyboard
*
* @param[in] data Pointer to input report data buffer
* @param[in] length Length of input report data buffer
*/
static void hid_host_generic_report_callback(const uint8_t *const data,
const int length) {
hid_print_new_device_report_header(HID_PROTOCOL_NONE);
for (int i = 0; i < length; i++) {
printf("%02X", data[i]);
}
putchar('\r');
putchar('\n');
fflush(stdout);
}
/**
* @brief USB HID Host interface callback
*
* @param[in] hid_device_handle HID Device handle
* @param[in] event HID Host interface event
* @param[in] arg Pointer to arguments, does not used
*/
void hid_host_interface_callback(hid_host_device_handle_t hid_device_handle,
const hid_host_interface_event_t event,
void *arg) {
uint8_t data[64] = {0};
size_t data_length = 0;
hid_host_dev_params_t dev_params;
ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));
switch (event) {
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
ESP_ERROR_CHECK(hid_host_device_get_raw_input_report_data(
hid_device_handle, data, 64, &data_length));
if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) {
if (HID_PROTOCOL_KEYBOARD == dev_params.proto) {
hid_host_keyboard_report_callback(data, data_length);
} else if (HID_PROTOCOL_MOUSE == dev_params.proto) {
hid_host_mouse_report_callback(data, data_length);
}
} else {
hid_host_generic_report_callback(data, data_length);
}
break;
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HID Device, protocol '%s' DISCONNECTED",
hid_proto_name_str[dev_params.proto]);
WebSerial.printf("HID Device, protocol '%s' DISCONNECTED\n",
hid_proto_name_str[dev_params.proto]);
ESP_ERROR_CHECK(hid_host_device_close(hid_device_handle));
break;
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
ESP_LOGI(TAG, "HID Device, protocol '%s' TRANSFER_ERROR",
hid_proto_name_str[dev_params.proto]);
WebSerial.printf("HID Device, protocol '%s' TRANSFER_ERROR\n",
hid_proto_name_str[dev_params.proto]);
break;
default:
ESP_LOGE(TAG, "HID Device, protocol '%s' Unhandled event",
hid_proto_name_str[dev_params.proto]);
WebSerial.printf("HID Device, protocol '%s' Unhandled event\n",
hid_proto_name_str[dev_params.proto]);
break;
}
}
/**
* @brief USB HID Host Device event
*
* @param[in] hid_device_handle HID Device handle
* @param[in] event HID Host Device event
* @param[in] arg Pointer to arguments, does not used
*/
void hid_host_device_event(hid_host_device_handle_t hid_device_handle,
const hid_host_driver_event_t event, void *arg) {
hid_host_dev_params_t dev_params;
ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));
const hid_host_device_config_t dev_config = {
.callback = hid_host_interface_callback, .callback_arg = NULL};
switch (event) {
case HID_HOST_DRIVER_EVENT_CONNECTED:
ESP_LOGI(TAG, "HID Device, protocol '%s' CONNECTED",
hid_proto_name_str[dev_params.proto]);
WebSerial.printf("HID Device, protocol '%s' CONNECTED\n",
hid_proto_name_str[dev_params.proto]);
ESP_ERROR_CHECK(hid_host_device_open(hid_device_handle, &dev_config));
if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) {
ESP_ERROR_CHECK(hid_class_request_set_protocol(hid_device_handle,
HID_REPORT_PROTOCOL_BOOT));
if (HID_PROTOCOL_KEYBOARD == dev_params.proto) {
ESP_ERROR_CHECK(hid_class_request_set_idle(hid_device_handle, 0, 0));
}
}
ESP_ERROR_CHECK(hid_host_device_start(hid_device_handle));
break;
default:
break;
}
}
/**
* @brief Start USB Host install and handle common USB host library events while
* app pin not low
*
* @param[in] arg Not used
*/
static void usb_lib_task(void *arg) {
const gpio_config_t input_pin = {
.pin_bit_mask = BIT64(APP_QUIT_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
};
ESP_ERROR_CHECK(gpio_config(&input_pin));
const usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1,
};
ESP_ERROR_CHECK(usb_host_install(&host_config));
xTaskNotifyGive((TaskHandle_t)arg);
while (gpio_get_level(APP_QUIT_PIN) != 0) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
// Release devices once all clients has deregistered
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
usb_host_device_free_all();
ESP_LOGI(TAG, "USB Event flags: NO_CLIENTS");
WebSerial.println("USB Event flags: NO_CLIENTS");
}
// All devices were removed
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
ESP_LOGI(TAG, "USB Event flags: ALL_FREE");
WebSerial.println("USB Event flags: ALL_FREE");
}
}
// App Button was pressed, trigger the flag
user_shutdown = true;
ESP_LOGI(TAG, "USB shutdown");
WebSerial.println("USB shutdown");
// Clean up USB Host
vTaskDelay(10); // Short delay to allow clients clean-up
ESP_ERROR_CHECK(usb_host_uninstall());
vTaskDelete(NULL);
}
/**
* @brief HID Host main task
*
* Creates queue and get new event from the queue
*
* @param[in] pvParameters Not used
*/
void hid_host_task(void *pvParameters) {
hid_host_event_queue_t evt_queue;
// Create queue
hid_host_event_queue = xQueueCreate(10, sizeof(hid_host_event_queue_t));
// Wait queue
while (!user_shutdown) {
if (xQueueReceive(hid_host_event_queue, &evt_queue, pdMS_TO_TICKS(50))) {
hid_host_device_event(evt_queue.hid_device_handle, evt_queue.event,
evt_queue.arg);
}
}
xQueueReset(hid_host_event_queue);
vQueueDelete(hid_host_event_queue);
vTaskDelete(NULL);
}
/**
* @brief HID Host Device callback
*
* Puts new HID Device event to the queue
*
* @param[in] hid_device_handle HID Device handle
* @param[in] event HID Device event
* @param[in] arg Not used
*/
void hid_host_device_callback(hid_host_device_handle_t hid_device_handle,
const hid_host_driver_event_t event, void *arg) {
const hid_host_event_queue_t evt_queue = {
.hid_device_handle = hid_device_handle, .event = event, .arg = arg};
xQueueSend(hid_host_event_queue, &evt_queue, 0);
}
void app_main(void) {
BaseType_t task_created;
ESP_LOGI(TAG, "HID Host example");
WebSerial.println("HID Host example");
/*
* Create usb_lib_task to:
* - initialize USB Host library
* - Handle USB Host events while APP pin in in HIGH state
*/
task_created =
xTaskCreatePinnedToCore(usb_lib_task, "usb_events", 4096,
xTaskGetCurrentTaskHandle(), 2, NULL, 0);
assert(task_created == pdTRUE);
// Wait for notification from usb_lib_task to proceed
ulTaskNotifyTake(false, 1000);
/*
* HID host driver configuration
* - create background task for handling low level event inside the HID driver
* - provide the device callback to get new HID Device connection event
*/
const hid_host_driver_config_t hid_host_driver_config = {
.create_background_task = true,
.task_priority = 5,
.stack_size = 4096,
.core_id = 0,
.callback = hid_host_device_callback,
.callback_arg = NULL};
ESP_ERROR_CHECK(hid_host_install(&hid_host_driver_config));
// Task is working until the devices are gone (while 'user_shutdown' if false)
user_shutdown = false;
/*
* Create HID Host task process for handle events
* IMPORTANT: Task is necessary here while there is no possibility to interact
* with USB device from the callback.
*/
task_created =
xTaskCreate(&hid_host_task, "hid_task", 4 * 1024, NULL, 2, NULL);
assert(task_created == pdTRUE);
}
void setup() {
app_main();
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.printf("WiFi Failed!\n");
return;
}
// Once connected, print IP
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hi! This is WebSerial demo. You can access webserial interface at http://" + WiFi.localIP().toString() + "/webserial");
});
// WebSerial is accessible at "<IP Address>/webserial" in browser
WebSerial.begin(&server);
/* Attach Message Callback */
WebSerial.onMessage([&](uint8_t *data, size_t len) {
Serial.printf("Received %u bytes from WebSerial: ", len);
Serial.write(data, len);
Serial.println();
WebSerial.println("Received Data...");
String d = "";
for(size_t i=0; i < len; i++){
d += char(data[i]);
}
WebSerial.println(d);
});
// Start server
server.begin();
}
void loop() {
WebSerial.loop();
}

