You can watch a video of this in action here.
ESP32-S3 USB serial experiments and host-side tests:
- Echo firmware (Arduino): uppercase-echo any received bytes via USB-CDC
- Echo firmware (ESP-IDF): uppercase-echo using
usb_serial_jtag - Bandwidth TX firmware (Arduino): stream 4 MiB payload over USB-CDC on BOOT press
- Bandwidth TX firmware (ESP-IDF): stream 4 MiB payload via TinyUSB CDC ACM on BOOT press
- Python echo test: iterate common host baud rates and verify echo
- Python throughput receiver: receive payload, verify CRC32, report throughput
- PlatformIO (VS Code extension or
pioCLI) uvfor Python package/runtime management (curl -LsSf https://astral.sh/uv/install.sh | sh)- Python 3.10+
Folder: echo-firmware-arduino/ targeting esp32-s3-devkitc-1 with Arduino USB-CDC enabled.
Build and upload:
cd echo-firmware-arduino
pio run -t uploadBehavior: configures Serial at 115200; echoes incoming bytes back in uppercase.
Folder: echo-firmware-idf/ using the ESP-IDF usb_serial_jtag driver.
Build and upload:
cd echo-firmware-idf
pio run -t uploadBehavior: prints a ready line and uppercase-echoes any incoming data.
Folder: bandwidth-tx-firmware-arduino/.
Sends a 4 MiB payload when the BOOT button (GPIO0) is pressed.
Data format:
- 4 bytes magic:
TXT1 - 4 bytes little-endian payload length
- payload bytes (repeating 0..255 pattern)
- 4 bytes little-endian CRC32 (IEEE/ZIP, poly 0xEDB88320)
Build and upload:
cd bandwidth-tx-firmware-arduino
pio run -t uploadPress BOOT once to start a transfer.
Folder: bandwidth-tx-firmware-idf/ using TinyUSB CDC ACM.
Build and upload:
cd bandwidth-tx-firmware-idf
pio run -t uploadDevice prints a ready message. Press BOOT to start a 4 MiB transfer with the same header/payload/CRC format as above.
Path: test-scripts/echo_baud_test.py. Dependencies are declared in test-scripts/pyproject.toml and will be resolved automatically by uv.
Run (auto-detects an Espressif device when possible):
cd test-scripts
uv run echo_baud_test.pySpecify a port explicitly if auto-detection fails (macOS example):
uv run echo_baud_test.py --port /dev/cu.usbmodemXXXXCustomize the message or baud rates:
uv run echo_baud_test.py --message "hello world\n" --baud-rates 9600 115200 230400 460800 921600Notes:
- The device echoes bytes in uppercase; the script compares case-insensitively by uppercasing both sent/received data.
- Exit code is non-zero if any baud rate fails the echo check.
Path: test-scripts/throughput_read_test.py.
Receive and measure the transfer from either TX firmware (auto-detects port when possible):
cd test-scripts
uv run throughput_read_test.pyExplicit port:
uv run throughput_read_test.py --port /dev/cu.usbmodemXXXXOptions:
--baud: host setting (CDC ignores it), default 115200--timeout: overall timeout for a single transfer (seconds), default 60
Example output:
Received 4194304 bytes in 0.832s => 40.32 Mbit/s
Both scripts prefer devices with USB VID 0x303A (Espressif). If none are found, they fall back to common /dev/cu.* or /dev/tty.* candidates. You can always override with --port.