An USB/IP server implementation for ESP32-S3 and ESP32-P4 based on usbipdcpp library. This project serves as a practical example of using usbipdcpp on ESP32 platforms.
Note: USB device compatibility depends on the ESP32 chip's USB PHY speed support:
- ESP32-S3: Supports Full Speed (12Mbps) and Low Speed (1.5Mbps) devices
- ESP32-P4: Supports High Speed (480Mbps), Full Speed (12Mbps) and Low Speed (1.5Mbps) devices
High Speed devices (480Mbps) may not work correctly on ESP32-S3 due to PHY limitations.
English | 中文
- 🔄 Transparent USB Forwarding - Export local USB devices to remote machines via USB/IP protocol
- 🔌 Hot-plug Support - Automatic device detection, enumeration, and cleanup on removal
- 🌐 Multi-device Support - USB hubs supported, multiple devices can be exported simultaneously
- ⚡ Zero-Copy Performance - Direct DMA buffer access eliminates data copying overhead, achieving optimal throughput
- 🛡️ Robust Connection Handling - Automatic cleanup when devices are unplugged during active sessions
- ESP32-S3 or ESP32-P4 development board (USB OTG supported)
- USB devices (keyboards, mice, mass storage, etc.)
- USB hub (optional, for multiple devices)
One Board, Two Roles: Flash this firmware onto your ESP32-S3 dev board and it becomes a USB/IP dongle; flash something else and it's back to being a regular dev board. No hardware modifications needed — just swap the firmware whenever you switch projects.
USB OTG Power: Common ESP32-S3 DevKitC boards and their compatible counterparts have a USB OTG power solder pad on the back. Bridging this pad allows the board to supply power to connected USB devices. If you prefer not to modify the hardware, you can use a self-powered USB hub (one with an external power supply) instead — the hub will power the devices while the ESP32 handles data.
Speed Compatibility: Ensure your ESP32 chip's USB PHY supports the USB speed type of your device. For example, High Speed UVC webcams require ESP32-P4.
Flash Size: Default configuration assumes ESP32-S3 (8MB flash) and ESP32-P4 (32MB flash). Modify via
idf.py menuconfig→Serial flasher config→Flash sizeif needed.
- ESP-IDF v5.5
- Linux machine with
usbiptools installed (client side)
git clone --recursive https://github.com/yunsmall/usbipdcpp_esp32.git
cd usbipdcpp_esp32Set your WiFi credentials via menuconfig:
idf.py menuconfigNavigate to Usbipdcpp WiFi Configuration and set:
Usbipd WiFi SSIDUsbipd WiFi Password
idf.py build flash monitorFor specific chip targets, use the appropriate sdkconfig defaults:
# For ESP32-S3
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults.esp32s3" build flash monitor
# For ESP32-P4
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults.esp32p4" build flash monitorIf the default sdkconfig doesn't take effect, explicitly specify it with
-DSDKCONFIG_DEFAULTS.
On your Linux machine:
# Load USB/IP kernel modules
sudo modprobe vhci-hcd
# List available devices
sudo usbip list -r <ESP32_IP>
# Attach to a device
sudo usbip attach -r <ESP32_IP> -b <BUSID>┌─────────────────────────────────────────────────────────┐
│ Linux Client │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ App A │ │ App B │ │ App C │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌───────┴───────┐ │
│ │ usbip-vhci │ │
│ └───────┬───────┘ │
└────────────────────────────┼────────────────────────────┘
│ TCP/IP Network
┌────────────────────────────┼────────────────────────────┐
│ ┌───────┴───────┐ │
│ │ ESP32-S3 │ │
│ │ USB/IP Server │ │
│ └───────┬───────┘ │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ │ │ │ │
│ ┌────┴────┐ ┌─────┴─────┐ ┌─────┴─────┐ │
│ │ USB Hub │ │ USB Dev 1 │ ... │ USB Dev N │ │
│ └─────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────┘
| Device Type | Status | Notes |
|---|---|---|
| USB Keyboard | ✅ Working | |
| USB Mouse | ✅ Working | |
| USB Flash Drive (MSC) | ✅ Working | Bulk transfer tested |
| USB Audio | 🔄 Testing | |
| USB Webcam (UVC) | 🔄 Testing | Requires ESP32-P4 for High Speed |
Bulk and interrupt transfers have been verified to work correctly. Ensure your ESP32 chip's USB PHY supports the target device's speed type.
This implementation leverages usbipdcpp v1.0.1's zero-copy architecture for maximum throughput:
- Direct DMA Buffer Access: USB transfer buffers are allocated in DMA-capable memory and accessed directly for network I/O, eliminating intermediate data copies
- RAII Transfer Management:
TransferHandleautomatically manages buffer lifecycle, ensuring proper cleanup without manual memory management - ESP32-Specific Optimizations:
- Bulk/Interrupt IN transfers aligned to endpoint Max Packet Size for hardware efficiency
- Control transfer buffers pre-allocated with setup packet space
- Object pooling for callback structures reduces allocation overhead
- usbipdcpp - A cross-platform USB/IP protocol library. This project is an ESP32 implementation using usbipdcpp.
If you use this project in a product, please display the following information prominently in your product documentation or about page:
- Project URL:
https://github.com/yunsmall/usbipdcpp_esp32 - Author:
yunsmall(GitHub) - Contact:
yun_small@163.com
Large USB transfers (e.g. 65536 bytes for firmware flashing) require a single DMA-capable buffer of the same size. When DMA memory is fragmented, a large contiguous allocation can fail even though the total free space is sufficient.
Chunked transfer addresses this by splitting the large allocation into multiple smaller blocks (default: 16384 bytes each), trading one large contiguous allocation for several smaller ones — dramatically increasing the probability of successful allocation.
However, chunking is disabled by default (enable_chunking = false in Esp32DeviceHandler.cpp) due to a persistent timeout issue: when enabled, some bulk transfer scenarios (e.g. remote JLINK firmware flashing) cause the first chunk to NAK indefinitely while waiting for device data. The host times out after ~1 second, sends CMD_UNLINK, and the cycle repeats. The root cause has not been fully identified.
If you know how to fix this, pull requests are welcome.
ESP32-P4 is strongly recommended over ESP32-S3 for USB/IP use. ESP32-S3's internal DMA-capable memory is very limited (~300KB), which can cause large USB transfer buffer allocations to fail under load. ESP32-P4 supports DMA access to PSRAM, which removes the need for chunking in most cases.
Apache License 2.0