Visualizzazione post con etichetta Esp32S3. Mostra tutti i post
Visualizzazione post con etichetta Esp32S3. Mostra tutti i post

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);
}

 

 

 

 

lunedì 10 novembre 2025

USB OTG in ESP32-S3

Attenzione: dopo aver flashato un programma che usa OTG si perde la seriale USB, il dispositivo deve essere quindi resettato prima di poter essere riprogrammato

 

Da notare che in StampS3 il tasto e' nascosto sotto l'adesivo

------------------------------------------------------------------  

Ho provato ad usare la porta USB in modalita' OTG in ESP32-S3

Con il modulo Esp32-S3-Wroom esistono due porte USB di cui una di programmazione (a sinistra) ed una OTG (a destra)

 


Provando gli esempi in examples/peripherals/usb/device/hid tutto funziona, la scheda  viene riconosciuta come un device. Il problema e'  che usando gli esempi examples/peripherals/usb/host/hid il dispositivo viene flashato in modo correto ma non funziona

Dopo averle provate tutto ho trovato che la seconda porta USB non porta corrente ma solo dati ...in pratica in modalita' host il dispositivo nemmeno di accende/ Ho provato una alimentazione esterna sul pin 5V e GND ma niente 

Usando pero' dispositivi che hanno una batteria interna (tipo Cardputer basato su Esp32S3) il programma funziona...non riesco a trovare lo schema elettrica del Wroom a questo punto credo che non ci sia proprio connessione elettrica di alimentazione sull'USB Otg ma solo D+ e D- 


Anche la Esp32S3 Feather alimentata via LiPo non alimenta in modo efficace la USb (i 3.7 V della Lipo non passano attraverso un booster)

 per fornire 5V alla porta USB (e per alimentare nello stesso tempo la scheda) si stacca la LiPo si alimenta dall'esterno   sul pin USB +5V e sul pin GND


 

 In altri schede si deve collegare due piazzole come nella scheda sottostante per avere alimentazione su OTG



 

Microfoni Kinect1

Ho trovato quasi per caso su Internet che il Kinect 1 ha un array di 4 microfoni che permettono di determinare la direzione del suono (ed es...