-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/usb_composite_device_support' into 'master'
usbd: add usb composite device example Closes IDFGH-9035 See merge request espressif/esp-idf!23135
- Loading branch information
Showing
11 changed files
with
308 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
examples/peripherals/usb/device/tusb_composite_msc_serialdevice/CMakeLists.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# For more information about build system see | ||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html | ||
# The following five lines of boilerplate have to be in your project's | ||
# CMakeLists in this exact order for cmake to work correctly | ||
cmake_minimum_required(VERSION 3.16) | ||
|
||
include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||
project(tusb_composite) |
81 changes: 81 additions & 0 deletions
81
examples/peripherals/usb/device/tusb_composite_msc_serialdevice/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
| Supported Targets | ESP32-S2 | ESP32-S3 | | ||
| ----------------- | -------- | -------- | | ||
|
||
# TinyUSB Composite Device (MSC + Serial) Example | ||
|
||
(See the README.md file in the upper level 'examples' directory for more information about examples.) | ||
|
||
A USB device can provide multiple functions that are active simultaneously. Such multi-function devices are also known as composite devices. This example shows how to set up ESP chip to work as a USB Serial Device as well as MSC device (Storage media as SPI-Flash). | ||
|
||
As a USB stack, a TinyUSB component is used. | ||
|
||
## How to use example | ||
|
||
### Hardware Required | ||
|
||
Any ESP board that supports the USB-OTG peripheral. | ||
|
||
### Pin Assignment | ||
|
||
_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. | ||
|
||
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). | ||
|
||
Next, for Self-Powered Devices with VBUS monitoring, user must set ``self_powered`` to ``true`` and ``vbus_monitor_io`` to GPIO number (``VBUS_MONITORING_GPIO_NUM``) that will be used for VBUS monitoring. | ||
|
||
### Build and Flash | ||
|
||
Build the project and flash it to the board, then run monitor tool to view serial output: | ||
|
||
```bash | ||
idf.py -p PORT flash monitor | ||
``` | ||
|
||
(Replace PORT with the name of the serial port to use.) | ||
|
||
(To exit the serial monitor, type ``Ctrl-]``.) | ||
|
||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. | ||
|
||
## Example Output | ||
|
||
After the flashing you should see the output at idf monitor: | ||
|
||
``` | ||
I (344) main_task: Calling app_main() | ||
I (344) example_main: USB Composite initialization | ||
W (354) TinyUSB: The device's configuration descriptor is not provided by user, using default. | ||
W (364) TinyUSB: The device's string descriptor is not provided by user, using default. | ||
W (374) TinyUSB: The device's device descriptor is not provided by user, using default. | ||
I (384) tusb_desc: | ||
┌─────────────────────────────────┐ | ||
│ USB Device Descriptor Summary │ | ||
├───────────────────┬─────────────┤ | ||
│bDeviceClass │ 239 │ | ||
├───────────────────┼─────────────┤ | ||
│bDeviceSubClass │ 2 │ | ||
├───────────────────┼─────────────┤ | ||
│bDeviceProtocol │ 1 │ | ||
├───────────────────┼─────────────┤ | ||
│bMaxPacketSize0 │ 64 │ | ||
├───────────────────┼─────────────┤ | ||
│idVendor │ 0x303a │ | ||
├───────────────────┼─────────────┤ | ||
│idProduct │ 0x4001 │ | ||
├───────────────────┼─────────────┤ | ||
│bcdDevice │ 0x100 │ | ||
├───────────────────┼─────────────┤ | ||
│iManufacturer │ 0x1 │ | ||
├───────────────────┼─────────────┤ | ||
│iProduct │ 0x2 │ | ||
├───────────────────┼─────────────┤ | ||
│iSerialNumber │ 0x3 │ | ||
├───────────────────┼─────────────┤ | ||
│bNumConfigurations │ 0x1 │ | ||
└───────────────────┴─────────────┘ | ||
I (544) TinyUSB: TinyUSB Driver installed | ||
I (554) example_main: Initializing storage... | ||
I (554) example_main: Initializing wear levelling | ||
I (584) example_main: USB Composite initialization DONE | ||
I (584) main_task: Returned from app_main() | ||
``` |
8 changes: 8 additions & 0 deletions
8
examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/CMakeLists.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
set(srcs "tusb_composite_main.c") | ||
set(requires fatfs wear_levelling) | ||
|
||
idf_component_register( | ||
SRCS "${srcs}" | ||
INCLUDE_DIRS . | ||
REQUIRES "${requires}" | ||
) |
4 changes: 4 additions & 0 deletions
4
examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
## IDF Component Manager Manifest File | ||
dependencies: | ||
espressif/esp_tinyusb: "^1.2" | ||
idf: "^5.0" |
149 changes: 149 additions & 0 deletions
149
examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Unlicense OR CC0-1.0 | ||
*/ | ||
|
||
#include <errno.h> | ||
#include <dirent.h> | ||
#include <sys/stat.h> | ||
#include "esp_check.h" | ||
#include "tinyusb.h" | ||
#include "tusb_msc_storage.h" | ||
#include "tusb_cdc_acm.h" | ||
|
||
#define BASE_PATH "/usb" // base path to mount the partition | ||
|
||
static const char *TAG = "example_main"; | ||
static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; | ||
|
||
void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) | ||
{ | ||
/* initialization */ | ||
size_t rx_size = 0; | ||
|
||
/* read */ | ||
esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size); | ||
if (ret == ESP_OK) { | ||
ESP_LOGI(TAG, "Data from channel %d:", itf); | ||
ESP_LOG_BUFFER_HEXDUMP(TAG, buf, rx_size, ESP_LOG_INFO); | ||
} else { | ||
ESP_LOGE(TAG, "Read error"); | ||
} | ||
|
||
/* write back */ | ||
tinyusb_cdcacm_write_queue(itf, buf, rx_size); | ||
tinyusb_cdcacm_write_flush(itf, 0); | ||
} | ||
|
||
void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) | ||
{ | ||
int dtr = event->line_state_changed_data.dtr; | ||
int rts = event->line_state_changed_data.rts; | ||
ESP_LOGI(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr, rts); | ||
} | ||
|
||
static bool file_exists(const char *file_path) | ||
{ | ||
struct stat buffer; | ||
return stat(file_path, &buffer) == 0; | ||
} | ||
|
||
static void file_operations(void) | ||
{ | ||
const char *directory = "/usb/esp"; | ||
const char *file_path = "/usb/esp/test.txt"; | ||
|
||
struct stat s = {0}; | ||
bool directory_exists = stat(directory, &s) == 0; | ||
if (!directory_exists) { | ||
if (mkdir(directory, 0775) != 0) { | ||
ESP_LOGE(TAG, "mkdir failed with errno: %s\n", strerror(errno)); | ||
} | ||
} | ||
|
||
if (!file_exists(file_path)) { | ||
ESP_LOGI(TAG, "Creating file"); | ||
FILE *f = fopen(file_path, "w"); | ||
if (f == NULL) { | ||
ESP_LOGE(TAG, "Failed to open file for writing"); | ||
return; | ||
} | ||
fprintf(f, "Hello World!\n"); | ||
fclose(f); | ||
} | ||
|
||
FILE *f; | ||
ESP_LOGI(TAG, "Reading file"); | ||
f = fopen(file_path, "r"); | ||
if (f == NULL) { | ||
ESP_LOGE(TAG, "Failed to open file for reading"); | ||
return; | ||
} | ||
char line[64]; | ||
fgets(line, sizeof(line), f); | ||
fclose(f); | ||
// strip newline | ||
char *pos = strchr(line, '\n'); | ||
if (pos) { | ||
*pos = '\0'; | ||
} | ||
ESP_LOGI(TAG, "Read from file: '%s'", line); | ||
} | ||
|
||
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle) | ||
{ | ||
ESP_LOGI(TAG, "Initializing wear levelling"); | ||
|
||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); | ||
if (data_partition == NULL) { | ||
ESP_LOGE(TAG, "Failed to find FATFS partition. Check the partition table."); | ||
return ESP_ERR_NOT_FOUND; | ||
} | ||
|
||
return wl_mount(data_partition, wl_handle); | ||
} | ||
|
||
void app_main(void) | ||
{ | ||
ESP_LOGI(TAG, "Initializing storage..."); | ||
|
||
static wl_handle_t wl_handle = WL_INVALID_HANDLE; | ||
ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle)); | ||
|
||
const tinyusb_msc_spiflash_config_t config_spi = { | ||
.wl_handle = wl_handle | ||
}; | ||
ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi)); | ||
ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH)); | ||
file_operations(); | ||
|
||
ESP_LOGI(TAG, "USB Composite initialization"); | ||
const tinyusb_config_t tusb_cfg = { | ||
.device_descriptor = NULL, | ||
.string_descriptor = NULL, | ||
.string_descriptor_count = 0, | ||
.external_phy = false, | ||
.configuration_descriptor = NULL, | ||
}; | ||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); | ||
|
||
tinyusb_config_cdcacm_t acm_cfg = { | ||
.usb_dev = TINYUSB_USBDEV_0, | ||
.cdc_port = TINYUSB_CDC_ACM_0, | ||
.rx_unread_buf_sz = 64, | ||
.callback_rx = &tinyusb_cdc_rx_callback, // the first way to register a callback | ||
.callback_rx_wanted_char = NULL, | ||
.callback_line_state_changed = NULL, | ||
.callback_line_coding_changed = NULL | ||
}; | ||
|
||
ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); | ||
/* the second way to register a callback */ | ||
ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( | ||
TINYUSB_CDC_ACM_0, | ||
CDC_EVENT_LINE_STATE_CHANGED, | ||
&tinyusb_cdc_line_state_changed_callback)); | ||
|
||
ESP_LOGI(TAG, "USB Composite initialization DONE"); | ||
} |
6 changes: 6 additions & 0 deletions
6
examples/peripherals/usb/device/tusb_composite_msc_serialdevice/partitions.csv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Name, Type, SubType, Offset, Size, Flags | ||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap | ||
nvs, data, nvs, 0x9000, 0x6000, | ||
phy_init, data, phy, 0xf000, 0x1000, | ||
factory, app, factory, 0x10000, 1M, | ||
storage, data, fat, , 1M, |
29 changes: 29 additions & 0 deletions
29
...les/peripherals/usb/device/tusb_composite_msc_serialdevice/pytest_usb_device_composite.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||
# SPDX-License-Identifier: CC0-1.0 | ||
from time import sleep | ||
|
||
import pytest | ||
from pytest_embedded import Dut | ||
from serial import Serial | ||
from serial.tools.list_ports import comports | ||
|
||
|
||
@pytest.mark.esp32s2 | ||
@pytest.mark.usb_device | ||
def test_usb_composite_device_serial_example(dut: Dut) -> None: | ||
dut.expect_exact('Hello World!') | ||
dut.expect_exact('USB Composite initialization') | ||
dut.expect_exact('USB Composite initialization DONE') | ||
sleep(2) # Some time for the OS to enumerate our USB device | ||
|
||
# Find device with Espressif TinyUSB VID/PID | ||
ports = comports() | ||
for port, _, hwid in ports: | ||
if '303A:4001' in hwid: | ||
with Serial(port) as s: | ||
s.write('text\r\n'.encode()) # Write dummy text to COM port | ||
dut.expect_exact('Data from channel 0:') # Check ESP log | ||
dut.expect_exact('|text..|') | ||
res = s.readline() # Check COM echo | ||
assert b'text\r\n' in res | ||
return |
16 changes: 16 additions & 0 deletions
16
examples/peripherals/usb/device/tusb_composite_msc_serialdevice/sdkconfig.defaults
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# This file was generated using idf.py save-defconfig. It can be edited manually. | ||
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration | ||
# | ||
CONFIG_TINYUSB=y | ||
CONFIG_TINYUSB_MSC_ENABLED=y | ||
|
||
CONFIG_PARTITION_TABLE_CUSTOM=y | ||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" | ||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" | ||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y | ||
CONFIG_WL_SECTOR_SIZE_512=y | ||
CONFIG_WL_SECTOR_MODE_PERF=y | ||
|
||
CONFIG_TINYUSB_CDC_ENABLED=y | ||
# CONFIG_TINYUSB_DESC_USE_DEFAULT_PID is not set | ||
CONFIG_TINYUSB_DESC_CUSTOM_PID=0x4001 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
## IDF Component Manager Manifest File | ||
dependencies: | ||
idf: ">=4.4" | ||
usb_host_msc: "^1.0.0" | ||
usb_host_msc: "^1.0.4" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters