ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
tinyusb_component.cpp
Go to the documentation of this file.
1#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \
2 defined(USE_ESP32_VARIANT_ESP32S31) || defined(USE_ESP32_VARIANT_ESP32H4)
3#include "tinyusb_component.h"
5#include "esphome/core/log.h"
6#include "tinyusb_default_config.h"
7
8namespace esphome::tinyusb {
9
10ESPHOME_LOG_TAG(TAG, "tinyusb");
11
12// Runs on the TinyUSB task: only wake the main loop, which reads the state and runs
13// the automations.
14static void tinyusb_event_cb(tinyusb_event_t *event, void *arg) {
15 if (event->id == TINYUSB_EVENT_ATTACHED || event->id == TINYUSB_EVENT_DETACHED) {
16 static_cast<TinyUSB *>(arg)->enable_loop_soon_any_context();
17 }
18}
19
21 // Use the device's MAC address as its serial number if no serial number is defined
22 if (this->string_descriptor_[SERIAL_NUMBER] == nullptr) {
23 static char mac_addr_buf[MAC_ADDRESS_BUFFER_SIZE];
24 get_mac_address_into_buffer(mac_addr_buf);
25 this->string_descriptor_[SERIAL_NUMBER] = mac_addr_buf;
26 }
27
28 // Start from esp_tinyusb defaults to keep required task settings valid across esp_tinyusb updates.
29 // Setters write into tusb_cfg_ before setup(), so anything they set has to survive the reset.
30 const uint8_t *full_speed_config = this->tusb_cfg_.descriptor.full_speed_config;
31 this->tusb_cfg_ = TINYUSB_DEFAULT_CONFIG();
32 this->tusb_cfg_.port = TINYUSB_PORT_FULL_SPEED_0;
33 this->tusb_cfg_.phy.skip_setup = false;
34 // Without VBUS monitoring the OTG core only sees a cable pull as the bus going idle
35 // (a suspend), so TinyUSB never reports a detach and stays "mounted".
36 if (this->vbus_monitor_pin_ >= 0) {
37 this->tusb_cfg_.phy.self_powered = true;
38 this->tusb_cfg_.phy.vbus_monitor_io = this->vbus_monitor_pin_;
39 }
40 this->tusb_cfg_.descriptor = {
41 .device = &this->usb_descriptor_,
42 .string = this->string_descriptor_,
43 .string_count = SIZE,
44 .full_speed_config = full_speed_config,
45 };
46
47 // Defense-in-depth: esp_tinyusb's tinyusb_descriptors_set() fails with
48 // ESP_ERR_INVALID_ARG when no configuration descriptor is provided and
49 // no class that has a built-in default (CDC/MSC/NCM) is compiled in. In
50 // that case the internal task exits without notifying us, and
51 // tinyusb_driver_install() blocks 5s on the notify-take -- long enough
52 // to trip the task watchdog. Bail early so the rest of the device can
53 // still boot.
54#if !(CFG_TUD_CDC > 0 || CFG_TUD_MSC > 0 || CFG_TUD_NCM > 0)
55 if (this->tusb_cfg_.descriptor.full_speed_config == nullptr) {
56 ESP_LOGE(TAG, "No USB class configured");
57 this->mark_failed();
58 return;
59 }
60#endif
61
62 this->tusb_cfg_.event_cb = tinyusb_event_cb;
63 this->tusb_cfg_.event_arg = this;
64 esp_err_t result = tinyusb_driver_install(&this->tusb_cfg_);
65 if (result != ESP_OK) {
66 ESP_LOGE(TAG, "tinyusb_driver_install failed: %s", esp_err_to_name(result));
67 this->mark_failed();
68 return;
69 }
70 // loop() only reports mount changes; the mount hooks wake it when one happens.
71 this->disable_loop();
72}
73
75 const bool mounted = tud_mounted();
76 if (mounted != this->last_reported_mounted_) {
77 this->last_reported_mounted_ = mounted;
78 ESP_LOGD(TAG, "USB host %s", mounted ? LOG_STR_LITERAL("mounted") : LOG_STR_LITERAL("unmounted"));
79 this->mount_state_callback_.call(mounted);
80 }
81 this->disable_loop();
82}
83
85 ESP_LOGCONFIG(TAG,
86 "TinyUSB:\n"
87 " Product ID: 0x%04X\n"
88 " Vendor ID: 0x%04X\n"
89 " Manufacturer: '%s'\n"
90 " Product: '%s'\n"
91 " Serial: '%s'",
92 this->usb_descriptor_.idProduct, this->usb_descriptor_.idVendor, this->string_descriptor_[MANUFACTURER],
93 this->string_descriptor_[PRODUCT], this->string_descriptor_[SERIAL_NUMBER]);
94 if (this->vbus_monitor_pin_ >= 0) {
95 ESP_LOGCONFIG(TAG, " VBUS Monitor Pin: GPIO%d", this->vbus_monitor_pin_);
96 }
97}
98
99} // namespace esphome::tinyusb
100#endif // USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 ||
101 // USE_ESP32_VARIANT_ESP32S31 || USE_ESP32_VARIANT_ESP32H4
void mark_failed()
Mark this component as failed.
void disable_loop()
Disable this component's loop.
const char * string_descriptor_[SIZE]
tusb_desc_device_t usb_descriptor_
LazyCallbackManager< void(bool)> mount_state_callback_
ESPHOME_LOG_TAG(TAG, "tinyusb")
void get_mac_address_into_buffer(std::span< char, MAC_ADDRESS_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in lowercase hex notation.
Definition helpers.cpp:848