USB Device Driver


The driver allows users to use ESP32-S2 chips to develop USB devices on top the TinyUSB stack. TinyUSB is integrating with ESP-IDF to provide USB features of the framework. Using this driver the chip works as a composite device supporting to represent several USB devices simultaneously. Currently, only the communications device class (CDC) type of the device with the ACM (Abstract Control Model) subclass is supported.


  • Configuration of device and string USB descriptors

  • USB Serial Device (CDC-ACM)

  • Input and output through USB Serial Device

Hardware USB Connection

  • Any board with the ESP32-S2 chip with USB connectors or with exposed USB’s D+ and D- (DATA+/DATA-) pins.

If the board has no USB connector but has the pins, connect pins directly to the host (e.g. with do-it-yourself cable from any USB connection cable).

On ESP32-S2, connect GPIO 20 and 19 to D+/D- respectively:

Connection of a board to a host ESP chip

Driver Structure

As the basis is used the TinyUSB stack.

On top of it the driver implements:

  • Customization of USB descriptors

  • Serial device support

  • Redirecting of standard streams through the Serial device

  • Encapsulated driver’s task servicing the TinyuSB


Via Menuconfig options you can specify:

  • Several of descriptor’s parameters (see: Descriptors Configuration bellow)

  • USB Serial low-level Configuration

  • The verbosity of the TinyUSB’s log

  • Disable the TinyUSB main task (for the custom implementation)

Descriptors Configuration

The driver’s descriptors are provided by the tinyusb_config_t structure’s descriptor and string_descriptor members. Therefore, users should initialize tinyusb_config_t to their desired descriptor before calling tinyusb_driver_install() to install driver.

However, the driver also provides a default descriptor. The driver can be installed with the default descriptor by setting the descriptor and string_descriptor members of tinyusb_config_t to NULL before calling tinyusb_driver_install(). The driver’s default descriptor is specified using Menuconfig, where the following fields should be configured:

  • PID

  • VID

  • bcdDevice

  • Manufacturer

  • Product name

  • Name of CDC device if it is On

  • Serial number

If you want to use own descriptors with extended modification, you can define them during the driver installation process

Install Driver

To initialize the driver, users should call tinyusb_driver_install(). The driver’s configuration is specified in a tinyusb_config_t structure that is passed as an argument to tinyusb_driver_install().

Note that the tinyusb_config_t structure can be zero initialized (e.g. tinyusb_config_t tusb_cfg = { 0 }) or partially (as shown below). For any member that is initialized to 0 or NULL, the driver will use its default configuration values for that member (see example below)

tinyusb_config_t partial_init = {
    .descriptor = NULL;         //Uses default descriptor specified in Menuconfig
    .string_descriptor = NULL;  //Uses default string specified in Menuconfig
    .external_phy = false;

USB Serial Device (CDC-ACM)

If the CDC option is enabled in Menuconfig, the USB Serial Device could be initialized with tusb_cdc_acm_init() according to the settings from tinyusb_config_cdcacm_t (see example below).

tinyusb_config_cdcacm_t amc_cfg = {
    .usb_dev = TINYUSB_USBDEV_0,
    .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

To specify callbacks you can either set the pointer to your tusb_cdcacm_callback_t function in the configuration structure or call tinyusb_cdcacm_register_callback() after initialization.

USB Serial Console

The driver allows to redirect all standard application strings (stdin/out/err) to the USB Serial Device and return them to UART using esp_tusb_init_console()/esp_tusb_deinit_console() functions.

Application Examples

The table below describes the code examples available in the directory peripherals/usb/.

Code Example



How to set up ESP32-S2 chip to get log output via Serial Device connection


How to set up ESP32-S2 chip to work as a Generic USB Device with a user-defined descriptor


How to set up ESP32-S2 chip to work as a USB Serial Device

API Reference


esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)

This is an all-in-one helper function, including:

  1. USB device driver initialization

  2. Descriptors preparation

  3. TinyUSB stack initialization

  4. Creates and start a task to handle usb events


Don’t change Custom descriptor, but if it has to be done, Suggest to define as follows in order to match the Interface Association Descriptor (IAD): bDeviceClass = TUSB_CLASS_MISC, bDeviceSubClass = MISC_SUBCLASS_COMMON,

  • config: tinyusb stack specific configuration

Return Value
  • ESP_ERR_INVALID_ARG: Install driver and tinyusb stack failed because of invalid argument

  • ESP_FAIL: Install driver and tinyusb stack failed because of internal error

  • ESP_OK: Install driver and tinyusb stack successfully


struct tinyusb_config_t

Configuration structure of the tinyUSB core.

Public Members

tusb_desc_device_t *descriptor

Pointer to a device descriptor

const char **string_descriptor

Pointer to an array of string descriptors

bool external_phy

Should USB use an external PHY



Type Definitions

typedef const char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE]


enum tinyusb_usbdev_t




esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg)

Initialize CDC ACM. Initialization will be finished with the tud_cdc_line_state_cb callback.



  • cfg: - init configuration structure

esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf, cdcacm_event_type_t event_type, tusb_cdcacm_callback_t callback)

Register a callback invoking on CDC event. If the callback had been already registered, it will be overwritten.



  • itf: - number of a CDC object

  • event_type: - type of registered event for a callback

  • callback: - callback function

esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf, cdcacm_event_type_t event_type)

Unregister a callback invoking on CDC event.



  • itf: - number of a CDC object

  • event_type: - type of registered event for a callback

size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch)

Sent one character to a write buffer.


size_t - amount of queued bytes

  • itf: - number of a CDC object

  • ch: - character to send

size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, const uint8_t *in_buf, size_t in_size)

Write data to write buffer from a byte array.


size_t - amount of queued bytes

  • itf: - number of a CDC object

  • in_buf: - a source array

  • in_size: - size to write from arr_src

esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks)

Send all data from a write buffer. Use tinyusb_cdcacm_write_queue to add data to the buffer.

WARNING! TinyUSB can block output Endpoint for several RX callbacks, after will do additional flush after the each trasfer. That can leads to the situation when you requested a flush, but it will fail until ont of the next callbacks ends. SO USING OF THE FLUSH WITH TIMEOUTS IN CALLBACKS IS NOT RECOMENDED - YOU CAN GET A LOCK FOR THE TIMEOUT


esp_err_t - ESP_OK if (timeout_ticks > 0) and and flush was successful, ESP_ERR_TIMEOUT if timeout occurred3 or flush was successful with (timeout_ticks == 0) ESP_FAIL if flush was unsuccessful

  • itf: - number of a CDC object

  • timeout_ticks: - waiting until flush will be considered as failed

esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size)

Read a content to the array, and defines it’s size to the sz_store.



  • itf: - number of a CDC object

  • out_buf: - to this array will be stored the object from a CDC buffer

  • out_buf_sz: - size of buffer for results

  • rx_data_size: - to this address will be stored the object’s size

bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf)

Check if the ACM initialized.


true or false

  • itf: - number of a CDC object


struct cdcacm_event_rx_wanted_char_data_t

Data provided to the input of the callback_rx_wanted_char callback.

Public Members

char wanted_char

Wanted character

struct cdcacm_event_line_state_changed_data_t

Data provided to the input of the callback_line_state_changed callback.

Public Members

bool dtr

Data Terminal Ready (DTR) line state

bool rts

Request To Send (RTS) line state

struct cdcacm_event_line_coding_changed_data_t

Data provided to the input of the line_coding_changed callback.

Public Members

cdc_line_coding_t const *p_line_coding

New line coding value

struct cdcacm_event_t

Describes an event passing to the input of a callbacks.

Public Members

cdcacm_event_type_t type

Event type

cdcacm_event_rx_wanted_char_data_t rx_wanted_char_data

Data input of the callback_rx_wanted_char callback

cdcacm_event_line_state_changed_data_t line_state_changed_data

Data input of the callback_line_state_changed callback

cdcacm_event_line_coding_changed_data_t line_coding_changed_data

Data input of the line_coding_changed callback

struct tinyusb_config_cdcacm_t

Configuration structure for CDC-ACM.

Public Members

tinyusb_usbdev_t usb_dev

Usb device to set up

tinyusb_cdcacm_itf_t cdc_port

CDC port

size_t rx_unread_buf_sz

Amount of data that can be passed to the AMC at once

tusb_cdcacm_callback_t callback_rx

Pointer to the function with the tusb_cdcacm_callback_t type that will be handled as a callback

tusb_cdcacm_callback_t callback_rx_wanted_char

Pointer to the function with the tusb_cdcacm_callback_t type that will be handled as a callback

tusb_cdcacm_callback_t callback_line_state_changed

Pointer to the function with the tusb_cdcacm_callback_t type that will be handled as a callback

tusb_cdcacm_callback_t callback_line_coding_changed

Pointer to the function with the tusb_cdcacm_callback_t type that will be handled as a callback

Type Definitions

typedef void (*tusb_cdcacm_callback_t)(int itf, cdcacm_event_t *event)

CDC-ACM callback type.


enum tinyusb_cdcacm_itf_t

CDC ports available to setup.


enum cdcacm_event_type_t

Types of CDC ACM events.




esp_err_t esp_tusb_init_console(int cdc_intf)

Redirect output to the USB serial.


esp_err_t - ESP_OK, ESP_FAIL or an error code

  • cdc_intf: - interface number of TinyUSB’s CDC

esp_err_t esp_tusb_deinit_console(int cdc_intf)

Switch log to the default output.



  • cdc_intf: - interface number of TinyUSB’s CDC


esp_err_t tusb_run_task(void)

This helper function creates and starts a task which wraps tud_task().

The wrapper function basically wraps tud_task and some log. Default parameters: stack size and priority as configured, argument = NULL, not pinned to any core. If you have more requirements for this task, you can create your own task which calls tud_task as the last step.

Return Value
  • ESP_OK: run tinyusb main task successfully

  • ESP_FAIL: run tinyusb main task failed of internal error

  • ESP_ERR_INVALID_STATE: tinyusb main task has been created before

esp_err_t tusb_stop_task(void)

This helper function stops and destroys the task created by tusb_run_task()

Return Value
  • ESP_OK: stop and destory tinyusb main task successfully

  • ESP_ERR_INVALID_STATE: tinyusb main task hasn’t been created yet


esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path)

Register TinyUSB CDC at VFS with path.


esp_err_t ESP_OK or ESP_FAIL

  • cdc_intf: - interface number of TinyUSB’s CDC

  • path: - path where the CDC will be registered, /dev/tusb_cdc will be used if left NULL.

esp_err_t esp_vfs_tusb_cdc_unregister(char const *path)

Unregister TinyUSB CDC from VFS.


esp_err_t ESP_OK or ESP_FAIL

  • path: - path where the CDC will be unregistered if NULL will be used /dev/tusb_cdc