Adafruit TinyUSB gives Arduino sketches a USB stack, tiered by core
Arduino library for TinyUSB
At a glance
- What is it?
- Adafruit_TinyUSB_Arduino wraps the TinyUSB stack for Arduino cores, giving CDC, HID, mass storage, MIDI and WebUSB classes, but the experience differs sharply between cores with built-in TinyUSB support and those that need manual initialisation.
- Who is it for?
- Adafruit_TinyUSB_Arduino suits someone building a USB device on an Arduino core that already ships TinyUSB, or a core maintainer who wants to give their users the same treatment, since the porting guide is specific enough to act on. It is the wrong choice for a sketch that only needs serial output, where the core's own USB stack is enough, and it is the wrong choice to depend on the STM32 and mbed ports, which the README calls experimental.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 49 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
An Arduino-shaped wrapper around the TinyUSB stack
TinyUSB is a USB device and host stack in C, and Adafruit_TinyUSB_Arduino is the Arduino-friendly version of it. The README describes it as designed with structure and APIs that are easily integrated to an Arduino Core, which is the whole point of the project. The stack itself is upstream, at hathach/tinyusb, and this repository is the layer that makes it usable from a sketch.
The device stack classes are listed, and the list reads like the feature set of a USB gadget. Communication (CDC) is there, and it is what implements the Serial monitor. Human Interface Device covers generic input and output reports plus keyboard, mouse and gamepad. Mass Storage Class supports multiple LUNs, so one device can present more than one logical volume. Musical Instrument Digital Interface is supported, Video (UVC) is marked as work in progress, and WebUSB is implemented with a vendor specific class.
The host stack is the other half, and it is the half that is less finished. The README states plainly that the host stack is still work in progress. It is available either by adding MAX3421E hardware, such as the Host FeatherWing, or on an rp2040 core thanks to Pico-PIO-USB, which uses the RP2040's PIO to drive USB. The host class drivers listed are Communication (CDC), including vendor serial adapters such as FTDI, CP210x and CH34x, and MassStorage.
So the device side is the mature half and the host side is the experimental one, and anyone evaluating this library for a host role, say a USB serial adapter reader, should read the work in progress note as a warning.
Cores that already have TinyUSB built in
The library is organised around a two-tier model of Arduino cores, and the first tier is the easy one. The README describes cores with built-in support as having TinyUSB as either their primary USB stack or selectable through a Tools menu, and in that case you only need to include `<Adafruit_TinyUSB.h>` in your sketch to use it.
The listed cores are Adafruit's own nRF52 core, the SAMD core, the arduino-pico core, the Espressif arduino-esp32 core and the openwch arduino_core_ch32 core. Built-in support is described as providing integration that is transparent to the sketch, though the README notes that it requires extra code added to the core's source, and that it is not always easy or possible to make those modifications, which is the reason the second tier exists at all.
The ESP32 core is the one with the most caveats, and the details matter if you are on it. Host mode using the MAX3421E controller should work with all chips, while device mode supports only the S2, S3 and P4, and it needs additional Tools menu settings: USB Mode set to USB-OTG (TinyUSB) for the S3 and P4, USB CDC On Boot enabled, and USB Firmware MSC On Boot and USB DFU On Boot disabled.
There is also a version-specific behaviour worth knowing. The README notes that for the ESP32 port, a version before v3.0 requires all descriptors to be specified in the USB object declaration, that is in the constructors, so all descriptor fields have to be part of the object declaration and the descriptor-related API has no effect afterwards. That limitation does not apply from v3.0 onward. If you are reading older forum posts about this library, that is probably the difference they are talking about.
Without built-in support, you call the stack yourself
The second tier is where the friction is, and the README is direct about it. The cores listed as having no built-in support are mbed_rp2040 and the STM32 core from stm32duino, and for the latter it says the port is still work in progress, only supported and tested with the F4, and only supporting OTG_FS.
Using TinyUSB on such a core is still possible, but it comes with a list of conditions. The device initialisation has to be called manually in setup, the task function and the CDC flush function may also need to be called manually in loop, and the serial monitor is reached through the name SerialTinyUSB rather than Serial. The README closes that list by saying there could be other issues, and that using it on these cores should be considered experimental.
That word carries real weight here. On a core with built-in support, the USB task is scheduled by the core, the descriptors are set up for you and the sketch barely knows the stack exists. On a core without it, the sketch author inherits the job of calling the task often enough, keeping the CDC buffer flushed, and choosing whether the IDE serial monitor and the USB CDC port are the same device. That is the difference between a library that is invisible and one that is part of your program's correctness.
The practical guidance is to pick a core with built-in support if you have the choice, and to treat the second tier as a debugging exercise rather than a default.
The porting guide is written for core maintainers
The porting guide is the most concrete part of the documentation, and it targets people who maintain an Arduino core rather than people writing sketches. It splits the work into core changes and library changes, and the core changes are the long list.
The repository is added as a submodule, or a local copy, at the core's libraries directory in the same way SPI is handled. Because Serial as CDC is considered part of the core, the core's Arduino.h has to include the CDC header, and the platform.txt include path needs the library's arduino directory added. Before setup, the core's main source has to invoke `TinyUSB_Device_Init(rhport)`, which initialises the USB device hardware and the TinyUSB stack, and includes Serial as an instance of the CDC class.
Then there are the calls that have to keep happening. `TinyUSB_Device_Task()` must be invoked whenever there is a new USB event, and the guide gives two ways of arranging that: use the USB interrupt to set a flag and call the function later after leaving the interrupt, or simply invoke it after the loop and inside yield and delay. `TinyUSB_Device_FlushCDC()` should also be called often so that Serial data actually goes out.
One note catches the eye because it is the kind of thing that only shows up on a battery-powered device. On low power platforms that use WFI and WFE, extra care is needed before the MCU sleeps, and the guide points to a CircuitPython pull request as a reference implementation.
The library side is much shorter, since the upstream TinyUSB stack has done the heavy lifting. The port author writes three functions: `TinyUSB_Port_InitDevice()`, which is hardware specific, brings up the clock and PHY and calls tud_init, `TinyUSB_Port_EnterDFU()` for entering DFU mode, and `TinyUSB_Port_GetSerialNumber()` to return the MCU's unique serial identifier for the serial string descriptor.
The example sketches are the API reference
The class driver API section of the README is a placeholder that says more documentation is still to be written, and that is an accurate description of the gap in this repository's own prose. What the project does provide is a set of example sketches, and they map cleanly onto the class drivers.
There is a CDC example, a Composite example for devices with several classes at once, a DualRole example for a device that is both, HID, Host, MIDI, MassStorage, Vendor, Video and WebUSB examples, each in its own directory under examples.
For a user, that set is more informative than a written class driver guide would be, because it shows the object construction pattern that the descriptor discussion in the ESP32 note refers to. If you are on a core with built-in support, opening the example for the class you need and changing it is the shortest route to working code.
The repository also carries the standard Arduino library metadata, library.json and library.properties, which are the files the Arduino tooling uses to list and install a library, along with a docs directory, a tools directory, a changelog.md, an AGENTS.md, a pre-commit configuration and a codespell configuration with its own dictionary directory. The presence of a per-project spell dictionary and a pre-commit setup suggests the project expects contributions, which is a reasonable expectation for a wrapper that many Arduino core maintainers depend on.
Release history, licence and where the risk sits
The library is MIT licensed, with the badge in the README pointing at the Open Source Initiative's MIT page and a LICENSE file in the repository. That is the simplest licence to adopt into a product, with only the notice obligation, and it is consistent with the way the project is used, since the wrapper is a build-time dependency of a sketch rather than something shipped in a binary.
The release history is steady patch-level work. The recent tags are 3.7.7 on 2026-05-12, 3.7.6 on 2026-05-06 and 3.7.5 on 2026-04-09, so the 3.7 line has been getting small fixes every week or two rather than large feature jumps. The last push to the repository was on 2026-09-04.
The risk in adopting this library is not the licence and not the release cadence. It is the dependency on the core doing the right thing. The sketch-level experience on a built-in core is excellent, but the stack is only as reliable as the core's USB task scheduling and descriptor configuration, and those live in somebody else's repository. When something misbehaves, the debugging path leads away from your sketch and into the core, which is worth knowing before you pick a platform.
The second risk is scope creep. The host stack is still work in progress, the video class is work in progress, and the non built-in cores are described as experimental. Adopting the library for device mode on a well supported core is a different proposition from adopting it for host mode or for a platform the author is testing on your behalf.
Editorial conclusion
Adafruit_TinyUSB_Arduino suits someone building a USB device on an Arduino core that already ships TinyUSB, or a core maintainer who wants to give their users the same treatment, since the porting guide is specific enough to act on. It is the wrong choice for a sketch that only needs serial output, where the core's own USB stack is enough, and it is the wrong choice to depend on the STM32 and mbed ports, which the README calls experimental. Before adopting it, check which tier your core falls into, read the porting guide if you are a core maintainer rather than a sketch author, and note that the class driver API documentation is marked as still to be written, so the example sketches are the better reference.
Frequently asked questions
How do I use Adafruit_TinyUSB_Arduino on a core with built-in TinyUSB support?
Include <Adafruit_TinyUSB.h> in your sketch. Cores with built-in support have TinyUSB as either the primary USB stack or selectable through a Tools menu, so no manual initialisation is needed.
What must I do on a core without built-in TinyUSB support?
Call TinyUSB_Device_Init() manually in setup, call TinyUSB_Device_Task() and possibly TinyUSB_Device_FlushCDC() in loop, and use the name SerialTinyUSB instead of Serial for the serial monitor. The README says usage on these cores, mbed_rp2040 and the STM32 core, should be considered experimental.
Which USB classes does the Adafruit TinyUSB library support?
On the device side: CDC for the serial monitor, HID with generic, keyboard, mouse and gamepad, mass storage with multiple LUNs, MIDI, video as UVC which is work in progress, and WebUSB through a vendor specific class. The host stack supports CDC including FTDI, CP210x and CH34x adapters, and mass storage, and is itself work in progress.
What does a core maintainer have to implement to port TinyUSB?
Three functions on the library side: TinyUSB_Port_InitDevice() to bring up the USB hardware and call tud_init, TinyUSB_Port_EnterDFU() for entering DFU mode, and TinyUSB_Port_GetSerialNumber() to supply the MCU serial as the serial string descriptor. The core itself also has to schedule TinyUSB_Device_Task() and call TinyUSB_Device_FlushCDC() often.
What are the ESP32 specific limitations of the Adafruit TinyUSB library?
Device mode supports only the S2, S3 and P4, with extra Tools menu settings, while host mode using the MAX3421E controller should work with all chips. Before ESP32 core version v3.0 all descriptors must be specified in the USB object declaration and the descriptor API has no effect afterwards; from v3.0 that limitation no longer applies.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/adafruit-adafruit-tinyusb-arduino)Was this useful?