venerdì 14 novembre 2025

Lettura OTG di dispositivo HID con Esp32S3

 Un programma per leggere i messaggi HID da un volante Logitech con Esp32S3 Otg Host

 

## IDF Component Manager Manifest File
dependencies:
idf: '>=4.4'
usb_host_hid: ^1.0.1
m5stack/m5unified: '*'


 

 

/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include "esp_log.h"
#include "usb/usb_host.h"
#include "errno.h"
#include "driver/gpio.h"

#include "usb/hid_host.h"
#include "usb/hid_usage_keyboard.h"
#include "usb/hid_usage_mouse.h"
#include "m5_wrapper.h"

/* GPIO Pin number for quit from example logic */
#define APP_QUIT_PIN GPIO_NUM_0

/* Logitech device VID:PID */
#define LOGITECH_VID 0x046D
#define LOGITECH_PID 0xC262

static const char *TAG = "logitech_hid";

QueueHandle_t app_event_queue = NULL;
static bool is_logitech_device = false;
static usb_host_client_handle_t usb_client_hdl = NULL;

/**
* @brief APP event group
*
* Application logic can be different. There is a one among other ways to distinguish the
* event by application event group.
* In this example we have two event groups:
* APP_EVENT - General event, which is APP_QUIT_PIN press event (Generally, it is IO0).
* APP_EVENT_HID_HOST - HID Host Driver event, such as device connection/disconnection or input report.
*/
typedef enum {
APP_EVENT = 0,
APP_EVENT_HID_HOST
} app_event_group_t;

/**
* @brief APP event queue
*
* This event is used for delivering the HID Host event from callback to a task.
*/
typedef struct {
app_event_group_t event_group;
/* HID Host - Device related info */
struct {
hid_host_device_handle_t handle;
hid_host_driver_event_t event;
void *arg;
} hid_host_device;
} app_event_queue_t;

/**
* @brief HID Protocol string names
*/
static const char *hid_proto_name_str[] = {
"NONE",
"KEYBOARD",
"MOUSE"
};

/**
* @brief USB HID Host Generic Interface report callback handler
*
* Prints raw data and checks for 10-byte messages from Logitech device
*
* @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)
{
// Print raw data
printf("Data [%d bytes]: ", length);
for (int i = 0; i < length; i++) {
printf("%02X ", data[i]);
}
// Check if it's the Logitech device and message is 10 bytes
if (is_logitech_device && length == 10) {
for (int i = 0; i < length; i++) {
int sterzo = data[5]*256 +data[4];
int acceleratore = data[6];
int freno = data[7];
m5_clear();
m5_set_text_size(2);
m5_set_cursor(5,5);
m5_print_int(sterzo);
m5_set_cursor(5,15);
m5_print_int(acceleratore);
m5_set_cursor(5,25);
m5_print_int(freno);
}
printf("logitech");
}
printf("\r\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));

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]);
ESP_ERROR_CHECK(hid_host_device_close(hid_device_handle));
is_logitech_device = false;
break;
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
ESP_LOGI(TAG, "HID Device, protocol '%s' TRANSFER_ERROR",
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]);
break;
}
}

/**
* @brief USB Host client event callback
* Used to get VID/PID during enumeration
*/
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
{
usb_host_client_handle_t client_hdl = (usb_host_client_handle_t)arg;
if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) {
usb_device_handle_t dev_hdl;
ESP_ERROR_CHECK(usb_host_device_open(client_hdl, event_msg->new_dev.address, &dev_hdl));
const usb_device_desc_t *dev_desc;
ESP_ERROR_CHECK(usb_host_get_device_descriptor(dev_hdl, &dev_desc));
uint16_t vid = dev_desc->idVendor;
uint16_t pid = dev_desc->idProduct;
ESP_LOGI(TAG, "USB Device detected - VID:PID %04X:%04X", vid, pid);
if (vid == LOGITECH_VID && pid == LOGITECH_PID) {
is_logitech_device = true;
ESP_LOGI(TAG, "Logitech device 046D:C262 detected!");
}
usb_host_device_close(client_hdl, dev_hdl);
}
}

/**
* @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));

switch (event) {
case HID_HOST_DRIVER_EVENT_CONNECTED:
ESP_LOGI(TAG, "HID Device CONNECTED - Protocol '%s'",
hid_proto_name_str[dev_params.proto]);

const hid_host_device_config_t dev_config = {
.callback = hid_host_interface_callback,
.callback_arg = NULL
};

ESP_ERROR_CHECK(hid_host_device_open(hid_device_handle, &dev_config));
ESP_ERROR_CHECK(hid_host_device_start(hid_device_handle));
break;
default:
break;
}
}

/**
* @brief USB Host client task
*/
static void usb_host_client_task(void *arg)
{
usb_host_client_handle_t client_hdl;
usb_host_client_config_t client_config = {
.is_synchronous = false,
.max_num_event_msg = 5,
.async = {
.client_event_callback = client_event_cb,
.callback_arg = NULL // Will be set after registration
}
};
ESP_ERROR_CHECK(usb_host_client_register(&client_config, &client_hdl));
// Update callback arg with client handle
client_config.async.callback_arg = (void *)client_hdl;
while (1) {
usb_host_client_handle_events(client_hdl, portMAX_DELAY);
}
}

/**
* @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 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(arg);

while (true) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
// In this example, there is only one client registered
// So, once we deregister the client, this call must succeed with ESP_OK
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
ESP_ERROR_CHECK(usb_host_device_free_all());
break;
}
}

ESP_LOGI(TAG, "USB shutdown");
// Clean up USB Host
vTaskDelay(10); // Short delay to allow clients clean-up
ESP_ERROR_CHECK(usb_host_uninstall());
vTaskDelete(NULL);
}

/**
* @brief BOOT button pressed callback
*
* Signal application to exit the HID Host task
*
* @param[in] arg Unused
*/
static void gpio_isr_cb(void *arg)
{
BaseType_t xTaskWoken = pdFALSE;
const app_event_queue_t evt_queue = {
.event_group = APP_EVENT,
};

if (app_event_queue) {
xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken);
}

if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}

/**
* @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 app_event_queue_t evt_queue = {
.event_group = APP_EVENT_HID_HOST,
// HID Host Device related info
.hid_host_device.handle = hid_device_handle,
.hid_host_device.event = event,
.hid_host_device.arg = arg
};

if (app_event_queue) {
xQueueSend(app_event_queue, &evt_queue, 0);
}
}

void app_main(void)
{
BaseType_t task_created;
app_event_queue_t evt_queue;
ESP_LOGI(TAG, "HID Host for Logitech Device 046D:C262");
m5_init();

// Init BOOT button: Pressing the button simulates app request to exit
// It will disconnect the USB device and uninstall the HID driver and USB Host Lib
const gpio_config_t input_pin = {
.pin_bit_mask = BIT64(APP_QUIT_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE,
};
ESP_ERROR_CHECK(gpio_config(&input_pin));
ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1));
ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_isr_cb, NULL));

/*
* 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);

// Create USB host client task to detect VID/PID
task_created = xTaskCreate(usb_host_client_task,
"usb_client",
4096,
NULL,
3, NULL);
assert(task_created == pdTRUE);

/*
* 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));

// Create queue
app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t));

ESP_LOGI(TAG, "Waiting for HID Device to be connected");

while (1) {
// Wait queue
if (xQueueReceive(app_event_queue, &evt_queue, portMAX_DELAY)) {
if (APP_EVENT == evt_queue.event_group) {
// User pressed button
usb_host_lib_info_t lib_info;
ESP_ERROR_CHECK(usb_host_lib_info(&lib_info));
if (lib_info.num_devices == 0) {
// End while cycle
break;
} else {
ESP_LOGW(TAG, "To shutdown example, remove all USB devices and press button again.");
// Keep polling
}
}

if (APP_EVENT_HID_HOST == evt_queue.event_group) {
hid_host_device_event(evt_queue.hid_host_device.handle,
evt_queue.hid_host_device.event,
evt_queue.hid_host_device.arg);
}
}
}

ESP_LOGI(TAG, "HID Driver uninstall");
ESP_ERROR_CHECK(hid_host_uninstall());
gpio_isr_handler_remove(APP_QUIT_PIN);
xQueueReset(app_event_queue);
vQueueDelete(app_event_queue);
}

 

 

 

 

Nessun commento:

Posta un commento

Kernel Panic QrCode

 In tanti anni ho visto qualche kernel panic, ma in questo formato non mi era mai successo    la cosa curiosa che al riavvio successivo ness...