Project 21: TinyUSB Composite MIDI + HID Controller (Custom Descriptor Lab)
Build a single NeoTrellis firmware image that enumerates as MIDI + HID + vendor diagnostics, with endpoint telemetry and cross-OS validation.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 5: Master |
| Time Estimate | 2-3 weeks |
| Main Programming Language | C/C++ with TinyUSB |
| Alternative Programming Languages | Bare-metal USB C (advanced extension) |
| Coolness Level | Level 5: The “WTF, That’s Possible?” |
| Business Potential | 3. The “Open Core” |
| Prerequisites | USB basics, class descriptors, endpoint concepts |
| Key Topics | Composite descriptors, endpoint management, TinyUSB concurrency, host compatibility |
1. Learning Objectives
By completing this project, you will:
- Design and validate coherent composite USB descriptors.
- Implement concurrent MIDI and HID functionality on one device.
- Manage endpoint throughput and latency under burst traffic.
- Apply TinyUSB callback/task context rules safely.
- Produce repeatable host-compatibility evidence across major OSes.
2. All Theory Needed (Project-Scoped)
2.1 Enumeration Pipeline
Control endpoint (EP0) descriptor exchange defines whether host class drivers can bind correctly.
2.2 Composite Layout
Interface numbering, endpoint assignment, and descriptor lengths must remain internally consistent.
2.3 TinyUSB Concurrency Boundaries
Keep callbacks short; route heavy work to event queues and task context.
2.4 Endpoint Service Policy
Latency emerges from queueing and service cadence. Monitor NAK/stall patterns under load.
2.5 Validation Scope
Use descriptor dumps plus functional tests in DAW/media-key contexts on multiple OSes.
3. Project Specification
3.1 What You Will Build
A composite USB device exposing:
- MIDI streaming interface
- HID consumer-control interface
- vendor diagnostics endpoint pair
3.2 Functional Requirements
- Descriptor set passes host parsing and class binding.
- MIDI note/CC messages transmit and receive correctly.
- HID transport controls trigger host media actions.
- Vendor endpoint supports simple command/telemetry exchange.
3.3 Non-Functional Requirements
- Performance: Endpoint p99 latency under target threshold.
- Reliability: No stall loops in 30-minute mixed traffic test.
- Portability: Works on macOS, Windows, Linux.
3.4 Real World Outcome
$ usb_composite_probe --device NeoTrellisPro
Device: VID=0x239A PID=0x80A1 Speed=Full (12 Mbps)
Interfaces:
IF0: MIDI Streaming
IF1: HID Consumer Control
IF2: Vendor Diagnostics
Descriptor validation: PASS
Endpoint latency p99:
MIDI IN=0.82ms HID IN=1.10ms Vendor OUT=1.45ms
Cross-OS matrix: macOS PASS, Windows PASS, Linux PASS
4. Solution Architecture
4.1 High-Level Design
Host <--EP0 control--> descriptor engine
Host <--MIDI EPs-----> MIDI event bridge
Host <--HID EP-------> transport key mapper
Host <--Vendor EPs---> diagnostics/config channel
4.2 Key Components
| Component | Responsibility | Key Decision |
|---|---|---|
| Descriptor builder | produce coherent descriptor tree | strict interface/endpoint map review |
| MIDI bridge | map grid events to USB MIDI packets | priority for note-on/off integrity |
| HID mapper | transport/media controls | report format compatibility |
| Vendor diagnostics | telemetry and config query | low-bandwidth and bounded handlers |
4.3 Descriptor Audit Checklist (Pseudocode)
assert(config_total_length == sum(interface_blocks))
assert(unique_endpoint_numbers_per_direction)
assert(class_subclass_protocol fields match function intent)
assert(max_packet_size valid for full-speed endpoint type)
5. Implementation Guide
5.1 Phases
- Bring up MIDI-only descriptor set.
- Add HID interface and validate host behavior.
- Add vendor channel and endpoint telemetry.
- Stress-test mixed traffic with logs.
5.2 The Core Question You’re Answering
“Can one USB device expose multiple stable personalities without hidden latency or compatibility failures?”
5.3 Questions to Guide Design
- Which endpoints are interrupt vs bulk and why?
- Where are context boundaries between callbacks and main loop?
- What telemetry will reveal endpoint starvation early?
5.4 Thinking Exercise
Draw your full descriptor tree on paper and compute all total-length fields manually before implementing.
5.5 Interview Questions They Will Ask
- Walk through USB enumeration for your composite device.
- Why might a device appear but class binding fail?
- How do you debug intermittent endpoint latency spikes?
- What TinyUSB APIs are unsafe in ISR/callback context?
- How would you evolve this design toward MIDI 2.0?
5.6 Hints in Layers
- Hint 1: Validate descriptors first, logic second.
- Hint 2: Add one interface at a time.
- Hint 3: Keep callbacks enqueue-only.
- Hint 4: Maintain per-endpoint counters and latency summaries.
5.7 Common Pitfalls and Debugging
| Problem | Why | Fix | Quick Test |
|---|---|---|---|
| MIDI missing in DAW | malformed MIDI interface descriptors | repair class-specific descriptor block | compare host descriptor dump |
| HID keys lag | callback path too heavy | defer processing to main loop | high-rate HID spam test |
| Random stalls | endpoint type/size mismatch | align descriptors and transfer policy | sustained mixed transfer test |
5.8 Definition of Done
- Composite descriptors validated and versioned.
- MIDI and HID operate simultaneously for 30 minutes.
- Vendor diagnostics endpoint returns deterministic responses.
- Cross-OS compatibility matrix recorded.