fix(nginx): 调整企业微信回调代理路径 feat(gateway): 添加企业微信消息处理功能 docs: 更新项目规划文档和企业微信配置详情 refactor(XCamera): 重构LED检测和图像处理逻辑 test: 添加ONVIF抓图测试功能
Espressif's Additions to TinyUSB
This component extends TinyUSB with features that simplify integration into ESP-IDF applications.
It provides both default and customizable configurations for TinyUSB, enabling USB device functionality on ESP chips with USB-OTG support.
Run-time configuration
During configuration, the following parameters can be set when installing the driver:
- Descriptors
- Peripheral port
- Task parameters (size, priority, and CPU affinity)
- USB PHY parameters
Default configuration
Run-time default configuration for Device Stack is managed internally via the TINYUSB_DEFAULT_CONFIG() macros.
Custom configuration
Manual configuration for Device Stack: descriptors, peripheral port, task, and USB PHY parameters can be set as needed.
Build-Time configuration
Configure the Device Stack using menuconfig:
- TinyUSB log verbosity
- Default device/string descriptor used by the default configuration macros
- Class-specific options (CDC-ACM, MSC, MIDI, HID, DFU, BTH, ECM/NCM/RNDIS, Vendor etc.)
Supported classes
- USB Serial Device (CDC-ACM) with optional Virtual File System support.
- Input and output streams through the USB Serial Device (available only when Virtual File System support is enabled).
- Mass Storage Device Class (MSC): create USB flash drives using SPI Flash or SD/MMC as storage media.
- Support for other USB classes (MIDI, HID, etc.) directly via TinyUSB.
How to use?
This component is distributed via IDF component manager. Just add idf_component.yml file to your main component with the following content:
## IDF Component Manager Manifest File
dependencies:
esp_tinyusb: "~2.0.0"
Or simply run:
idf.py add-dependency esp_tinyusb~2.0.0
Breaking changes migration guides
USB Device Stack: usage, installation & configuration
Structure overview
The Device Stack is built on top of TinyUSB and provides:
- Custom USB descriptor support
- Serial device (CDC-ACM) support
- Standard stream redirection through the serial device
- Storage media support (SPI Flash and SD Card) for the USB MSC class
- A dedicated task for TinyUSB servicing
Installation
Install the Device Stack by calling tinyusb_driver_install with a tinyusb_config_t structure.
A default configuration is available using the TINYUSB_DEFAULT_CONFIG() macro.
The default installation automatically configures the port (High-speed if supported by the hardware, otherwise Full-speed), task (with default parameters), USB PHY, Device Event callback and descriptors.
Default descriptors are provided for the following USB classes: CDC, MSC, and NCM.
⚠️ Important: For demonstration purposes, all error handling logic has been removed from the code examples. Do not ignore proper error handling in actual development.
#include "tinyusb_default_config.h"
void main(void) {
const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tinyusb_driver_install(&tusb_cfg);
}
Device Event callback
USB Device Event callback allows to get the following events during USB Device lifecycle:
TINYUSB_EVENT_ATTACHED: Device attached to the USB HostTINYUSB_EVENT_DETACHED: Device detached from the USB HostTINYUSB_EVENT_SUSPENDED: Device entered suspended stateTINYUSB_EVENT_RESUMED: Device was resumed from suspended state
To configure the USB Device Event Callback, provide the callback to the TINYUSB_DEFAULT_CONFIG() macros:
#include "tinyusb_default_config.h"
static void device_event_handler(tinyusb_event_t *event, void *arg)
{
switch (event->id) {
case TINYUSB_EVENT_ATTACHED:
case TINYUSB_EVENT_DETACHED:
case TINYUSB_EVENT_SUSPENDED:
case TINYUSB_EVENT_RESUMED:
default:
break;
}
}
void main(void) {
const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(device_event_handler);
tinyusb_driver_install(&tusb_cfg);
}
User Argument could be passed to the USB Device Event callback as a second argument (optional):
#include "tinyusb_default_config.h"
static context_t *context;
static void device_event_handler(tinyusb_event_t *event, void *arg)
{
context_t *context = (context_t*) arg;
switch (event->id) {
case TINYUSB_EVENT_ATTACHED:
case TINYUSB_EVENT_DETACHED:
case TINYUSB_EVENT_SUSPENDED:
case TINYUSB_EVENT_RESUMED:
default:
break;
}
}
void main(void) {
const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(device_event_handler, (void*) context);
tinyusb_driver_install(&tusb_cfg);
}
Suspend / Resume Device Events
Suspend and resume device events are optional and are disabled by default. Users can choose one of the following approaches:
Option 1 — Use esp_tinyusb device events (recommended for integration)
Enable the following Kconfig options:
CONFIG_TINYUSB_SUSPEND_CALLBACK→ enablesTINYUSB_EVENT_SUSPENDEDCONFIG_TINYUSB_RESUME_CALLBACK→ enablesTINYUSB_EVENT_RESUMED
When enabled:
- esp_tinyusb provides strong implementations of:
tud_suspend_cb()tud_resume_cb()
- esp_tinyusb dispatches suspend/resume events via the device event callback.
⚠️ Important:
When these options are enabled, user applications MUST NOT define
tud_suspend_cb() or tud_resume_cb() themselves. Doing so will result
in a linker error due to multiple definitions.
Option 2 — Use TinyUSB callbacks directly (default behavior)
If the Kconfig options are disabled (default):
- esp_tinyusb does NOT handle suspend/resume events
- Users may implement TinyUSB callbacks directly in their application:
void tud_suspend_cb(bool remote_wakeup_en)
{
// User suspend handling
}
void tud_resume_cb(void)
{
// User resume handling
}
Peripheral port
When several peripheral ports are available by the hardware, the specific port could be configured manually:
#include "tinyusb_default_config.h"
void main(void) {
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
#if (TUD_OPT_HIGH_SPEED)
tusb_cfg.port = TINYUSB_PORT_HIGH_SPEED_0;
#else
tusb_cfg.port = TINYUSB_PORT_FULL_SPEED_0;
#endif
tinyusb_driver_install(&tusb_cfg);
}
Task configuration
When the default parameters of the internal task should be changed:
#include "tinyusb_default_config.h"
void main(void) {
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tusb_cfg.task = TINYUSB_TASK_CUSTOM(4096 /*size */, 4 /* priority */, 0 /* affinity: 0 - CPU0, 1 - CPU1 ... */);
tinyusb_driver_install(&tusb_cfg);
}
USB Descriptors configuration
Configure USB descriptors using the tinyusb_config_t structure:
descriptor.devicedescriptor.stringdescriptor.full_speed_configdescriptor.high_speed_configdescriptor.qualifier
If any descriptor field is set to NULL, default descriptor will be assigned during installation. Values of default descriptors could be configured via menuconfig.
#include "tinyusb_default_config.h"
void main(void) {
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tusb_cfg.descriptor.device = &custom_device_descriptor;
tusb_cfg.descriptor.full_speed_config = custom_full_speed_configuration;
#if (TUD_OPT_HIGH_SPEED)
tusb_cfg.descriptor.high_speed_config = custom_high_speed_configuration;
#endif // TUD_OPT_HIGH_SPEED
tinyusb_driver_install(&tusb_cfg);
}
USB PHY configuration & Self-Powered Device
USB 2.0 requires self‑powered devices to sense VBUS and only present the pull‑up (attach) when VBUS is valid. If VBUS falls out of range, the device must detach and must not back‑power the bus. TinyUSB uses VBUS presence to drive connect/disconnect events in the DCD layer, so the VBUS sense signal must be correct. More information is available here.
In esp_tinyusb, enable this by setting self_powered = true and providing a GPIO that reflects VBUS validity. The PHY is configured with that GPIO (via USB_PHY_SELF_POWERED_DEVICE()), and the TinyUSB stack will correctly detect attach/detach.
Hardware requirements for VBUS sense:
- VBUS valid range is 4.40 V to 5.25 V at the device port (USB 2.0). ESP pins are not 5 V tolerant, so the sensing circuit must output a clean logic level at Vdd (typically 3.3 V).
- If using a resistor divider, the divider output should be at least 0.75 * Vdd when VBUS is 4.4 V.
- If using a comparator, use hysteresis with thresholds around 4.75 V (rising) and 4.35 V (falling).
#include "tinyusb_default_config.h"
void main(void)
{
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tusb_cfg.phy.self_powered = true;
tusb_cfg.phy.vbus_monitor_io = GPIO_NUM_0; // GPIO that reflects VBUS validity
tinyusb_driver_install(&tusb_cfg);
}
If you use the external PHY (ESP32-S3 only), you must initialize the PHY explicitly before installing esp_tinyusb driver:
#include "tinyusb_default_config.h"
#include "esp_private/usb_phy.h"
void main(void)
{
// Configuring VBUS monitor IO is mandatory only for self-powered devices
const usb_phy_otg_io_conf_t otg_io_conf = USB_PHY_SELF_POWERED_DEVICE(vbus_monitor_io);
// Initialize the external USB PHY
usb_phy_config_t phy_conf = { ... };
phy_conf.otg_io_conf = &otg_io_conf;
usb_new_phy(&phy_conf, ...);
// Skip PHY initialization in esp_tinyusb
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tusb_cfg.phy.skip_setup = true;
tinyusb_driver_install(&tusb_cfg);
}
USB Device Classes: usage, installation & configuration
USB Serial Device (CDC-ACM)
To enable USB Serial Device:
- select the option from
menuconfig. - initialize the USB Serial Device with
tusb_cdc_acm_initand atinyusb_config_cdcacm_tstructure
const tinyusb_config_cdcacm_t acm_cfg = {
.cdc_port = TINYUSB_CDC_ACM_0,
.rx_unread_buf_sz = 64,
.callback_rx = NULL,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = NULL,
.callback_line_coding_changed = NULL
};
tusb_cdc_acm_init(&acm_cfg);
Redirect standard I/O streams to USB with esp_tusb_init_console and revert with esp_tusb_deinit_console.
USB Mass Storage Device (MSC)
To enable Mass Storage Device:
- select the option from
menuconfig - configure storage for MSC Device class: SPI Flash or SD/MMC (when supported by the hardware).
SPI-Flash Storage
static wl_handle_t storage_init_spiflash(void)
{
wl_handle_t wl;
// Find partition
// Mount Wear Levelling
return wl;
}
void main(void)
{
wl_handle_t wl_handle = storage_init_spiflash();
tinyusb_msc_storage_handle_t storage_hdl;
const tinyusb_msc_storage_config_t cfg = {
.medium.wl_handle = wl_handle,
};
tinyusb_msc_new_storage_spiflash(&cfg, &storage_hdl);
// After installing TinyUSB driver, MSC Class will have one LUN, mapped to SPI/Flash storage
}
SD-Card Storage
static sdmmc_card_t *storage_init_sdmmc(void)
{
sdmmc_card_t *card;
// Config sdmmc
// Init sdmmc_host
// Init sdmmc slot
// Init sdmmc card
return card;
}
void main(void)
{
sdmmc_card_t *card = storage_init_sdmmc();
tinyusb_msc_storage_handle_t storage_hdl;
const tinyusb_msc_storage_config_t cfg = {
.medium.card = card,
};
tinyusb_msc_new_storage_sdmmc(&cfg, &storage_hdl);
// After installing TinyUSB driver, MSC Class will have one LUN, mapped to SD/MMC storage
}
Dual Storage
static wl_handle_t storage_init_spiflash(void)
{
wl_handle_t wl;
// Find partition
// Mount Wear Levelling
return wl;
}
static sdmmc_card_t *storage_init_sdmmc(void)
{
sdmmc_card_t *card;
// Config sdmmc
// Init sdmmc_host
// Init sdmmc slot
// Init sdmmc card
return card;
}
void main(void)
{
tinyusb_msc_storage_handle_t storage1_hdl;
tinyusb_msc_storage_handle_t storage2_hdl;
tinyusb_msc_storage_config_t cfg;
sdmmc_card_t *card = storage_init_sdmmc();
wl_handle_t wl_handle = storage_init_spiflash();
// Create SPI/Flash storage
cfg.medium.wl_handle = wl_handle;
tinyusb_msc_new_storage_spiflash(&cfg, &storage_hdl);
// Create SD/MMC storage
cfg.medium.card = card;
tinyusb_msc_new_storage_sdmmc(&cfg, &storage_hdl);
// After installing TinyUSB driver, MSC Class will have two LUNs, mapped to SPI/Flash and SD/MMC storages accordingly
}
Storage callback
Storage event callback is called, when one of the following events occurred:
TINYUSB_MSC_EVENT_MOUNT_START: Start mount from APP to USB or from USB to APPTINYUSB_MSC_EVENT_MOUNT_COMPLETE: Complete mount from USB to APP or from APP to USBTINYUSB_MSC_EVENT_FORMAT_REQUIRED: Filesystem not found, format neededTINYUSB_MSC_EVENT_MOUNT_FAILED: Error occurred during mounting filesystem
To use or enable storage callback there are two options available.
Set the callback with specific call after creating the storage:
static void storage_event_handle(tinyusb_msc_storage_handle_t handle, tinyusb_msc_event_t *event, void *arg)
{
switch (event->id) {
case TINYUSB_MSC_EVENT_MOUNT_START:
case TINYUSB_MSC_EVENT_MOUNT_COMPLETE:
case TINYUSB_MSC_EVENT_MOUNT_FAILED:
case TINYUSB_MSC_EVENT_FORMAT_REQUIRED:
default:
break;
}
}
void main(void)
{
sdmmc_card_t *card = storage_init_sdmmc();
tinyusb_msc_storage_handle_t storage_hdl;
const tinyusb_msc_storage_config_t cfg = {
.medium.card = card,
};
tinyusb_msc_new_storage_sdmmc(&cfg, &storage_hdl);
tinyusb_msc_set_storage_callback(storage_event_handle, NULL /* user argument for the event callback */);
}
Install the TinyUSB MSC Storage driver explicitly and provide the storage event via configuration:
void main(void)
{
sdmmc_card_t *card = storage_init_sdmmc();
const tinyusb_msc_driver_config_t driver_cfg = {
.storage_event_cb = storage_event_handle,
.storage_event_arg = NULL /* user argument for the storage event callback */,
};
tinyusb_msc_install_driver(&driver_cfg);
tinyusb_msc_storage_handle_t storage_hdl;
const tinyusb_msc_storage_config_t cfg = {
.medium.card = card,
};
tinyusb_msc_new_storage_sdmmc(&cfg, &storage_hdl);
}
MSC Performance Optimization
- Single-buffer approach: Buffer size is set via
CONFIG_TINYUSB_MSC_BUFSIZE. - Performance: SD cards offer higher throughput than internal SPI flash due to architectural constraints.
Performance Table (ESP32-S3):
| FIFO Size | Read Speed | Write Speed |
|---|---|---|
| 512 B | 0.566 MB/s | 0.236 MB/s |
| 8192 B | 0.925 MB/s | 0.928 MB/s |
Performance Table (ESP32-P4):
| FIFO Size | Read Speed | Write Speed |
|---|---|---|
| 512 B | 1.174 MB/s | 0.238 MB/s |
| 8192 B | 4.744 MB/s | 2.157 MB/s |
| 32768 B | 5.998 MB/s | 4.485 MB/s |
Performance Table (ESP32-S2, SPI Flash):
| FIFO Size | Write Speed |
|---|---|
| 512 B | 5.59 KB/s |
| 8192 B | 21.54 KB/s |
Note: Internal SPI flash is for demonstration only; use SD cards or external flash for higher performance.
Examples
You can find examples in ESP-IDF on GitHub.