If an ESP32-S3 suddenly disappears from the USB port after uploading a sketch, it is usually recoverable. The important thing is to distinguish between three completely different paths that may all look like “USB” from the outside: an external USB-to-UART bridge, the ESP32-S3’s built-in USB Serial/JTAG controller, and the ESP32-S3 USB-OTG peripheral.
A bad sketch can disable native USB, reconfigure GPIO19/GPIO20, enter deep sleep before USB enumerates or reboot so quickly that the CDC port never appears. That does not normally mean the board is dead. Holding GPIO0/BOOT low while resetting forces the ESP32-S3 into its ROM download bootloader, allowing you to flash known-good firmware again.
This guide starts with the quickest recovery sequence, then explains why USB disappears, which Arduino options matter and how to tell whether you are using native USB or a separate USB-to-serial chip.
Quick Recovery: Try This First
- Disconnect external hardware from GPIO0, GPIO19 and GPIO20.
- Connect the board with a known-good USB data cable.
- Hold the BOOT button.
- While still holding BOOT, press and release RESET/EN.
- Release BOOT.
- Look for a new COM/tty device.
- Select that port and upload a minimal known-good sketch.
- After flashing, press RESET once or power-cycle the board if it remains in download mode.
Espressif documents the same manual download sequence for ESP32-S3: hold BOOT, press RESET once, then release BOOT. GPIO0 low at reset selects the ROM serial/download bootloader instead of normal flash execution.
First Identify Which USB Connection You Actually Have
Many ESP32-S3 troubleshooting guides become confusing because they use “USB” to describe different hardware.
| Connection | What the PC talks to | Typical use | Can bad firmware make it disappear? |
|---|---|---|---|
| USB-to-UART | CP210x / CH340 / FTDI bridge chip | UART flashing and Serial | Bridge usually remains visible even if S3 firmware crashes |
| USB Serial/JTAG | ESP32-S3 built-in fixed-function USB block | Flashing, CDC serial, JTAG | Yes |
| USB-OTG / TinyUSB | ESP32-S3 USB OTG peripheral | CDC, HID, MIDI, MSC, DFU, custom USB device | Yes |
On boards with two USB connectors, one may go through a USB-to-UART bridge while the other goes directly to the S3 native USB pins. On Espressif’s ESP32-S3-DevKitC-1, for example, the dedicated ESP32-S3 USB connector is the native interface while a separate USB-to-UART port is available for conventional serial flashing.
If your board has only one connector, check its schematic or product documentation. A small third-party board may use native USB only, a CH340/CP2102 only, or both functions behind different hardware revisions.
ESP32-S3 Native USB Pins
The native USB data pins are fixed:
GPIO19 = USB D-
GPIO20 = USB D+
If you rely on native USB, treat GPIO19 and GPIO20 as reserved. A sketch that configures them as ordinary GPIO can make the USB device vanish from the computer.
This is one of the easiest ways to “brick” an S3 temporarily: the board flashes normally, resets, your application takes control of GPIO19/GPIO20, and Windows/macOS/Linux immediately loses the USB device. The ROM bootloader is still intact, so BOOT + RESET can recover it.
For the wider GPIO restrictions, see our ESP32-S3 DevKitC-1 pinout and safe GPIO guide.
Why Bad Firmware Can Make Native USB Disappear
Espressif explicitly documents that USB Serial/JTAG can disappear if the application reconfigures the USB pins or disables the USB Serial/JTAG controller. Deep sleep also disconnects the USB device because the required clocks and USB PHY are no longer active.
Typical causes include:
- Using GPIO19 or GPIO20 for an LED, relay or sensor.
- Switching the USB peripheral into another mode.
- Entering deep sleep immediately after boot.
- A crash/reboot loop before CDC has time to enumerate.
- Firmware built with USB CDC disabled.
- TinyUSB/USB-OTG configuration replacing the fixed Serial/JTAG path.
This behaviour is fundamentally different from a board with a CP2102 or CH340. An external bridge can remain visible even if the ESP32-S3 application is completely broken because the USB device the PC sees is the separate bridge chip.
How ROM Download Mode Saves the Board
The ESP32-S3 contains a ROM bootloader that is independent of the application stored in flash. At reset, GPIO0 is sampled as a strapping pin.
| GPIO0 at reset | Boot behaviour |
|---|---|
| HIGH / normal pull-up | Boot application from SPI flash |
| LOW / GND | Enter ROM download bootloader |
The BOOT button normally pulls GPIO0 low. The RESET/EN button restarts the chip so the strapping state is sampled again.
This means even a sketch that continuously crashes or destroys its own USB configuration can be bypassed before it runs:
Hold BOOT
→ press RESET/EN
→ release RESET/EN
→ release BOOT
→ ROM download mode
Manual BOOT/RESET Sequence in Detail
If automatic upload fails with “Failed to connect” or no port is visible:
- Close Serial Monitor and any other program using the port.
- Hold BOOT.
- Tap RESET/EN once.
- Keep BOOT held for another moment.
- Release BOOT.
- Wait for the operating system to enumerate the download-mode device.
- Refresh the Arduino/PlatformIO port list.
- Upload immediately.
If no new device appears, keep BOOT held while starting the upload and only release it after the uploader begins trying to connect. This is especially useful on clone boards with weak or missing automatic reset circuitry.
After Flashing, the Board May Still Stay in Download Mode
When native USB Serial/JTAG is used and download mode was entered manually, the USB controller cannot always perform the kind of reset that re-samples the boot strap automatically.
So if flashing finishes successfully but the new sketch does not start:
Release BOOT
Press RESET once
or power-cycle the board.
Recent esptool documentation also describes a watchdog-reset option that can perform a full reset in some USB modes, but a manual RESET/EN press remains the simplest recovery method.
Arduino IDE: Correct Settings for Native USB CDC
For development over the ESP32-S3’s internal USB CDC path, current Arduino-ESP32 documentation specifies:
- USB CDC On Boot → Enabled
- Upload Mode → UART0 / Hardware CDC
The first time you switch a board to this native CDC workflow, manually enter download mode with BOOT + RESET and select the new USB device. After the first successful flash and reset, normal flashing/monitoring through the internal USB path should work without repeating the manual sequence every time.
If you are uploading through a separate USB-to-UART connector instead, the Arduino troubleshooting guide recommends disabling USB CDC on boot because the serial console is expected on UART rather than native USB.
USB Serial/JTAG vs USB-OTG / TinyUSB
The ESP32-S3 contains two USB controllers, but they share one internal USB PHY. This matters because the two paths are intended for different jobs.
| USB block | Main purpose | Typical Arduino use |
|---|---|---|
| USB Serial/JTAG | Fixed CDC serial + JTAG + flashing | Development console / Hardware CDC |
| USB-OTG | Programmable USB device/host | TinyUSB, HID, MIDI, MSC, DFU |
Because they share the PHY, you cannot assume both can independently own the same native USB connector at the same time. If your project uses TinyUSB for HID or another OTG device class, that changes the USB architecture compared with the normal hardware CDC/JTAG development path.
Arduino USB DFU Recovery
ESP32-S3 also supports USB DFU through the USB-OTG path. Current Arduino-ESP32 documentation uses:
- USB Mode → USB-OTG (TinyUSB)
- USB DFU On Boot → Enabled
DFU is useful for firmware updating, but it is not the same as normal CDC serial monitoring. Arduino’s documentation explicitly notes that DFU itself does not provide the usual USB serial logging path, so CDC is generally the friendlier development workflow.
Why the Port Changes After Upload
It is normal for the port identity to change when moving between ROM download mode and application USB mode.
For example:
COM7 → ROM/download device
flash
reset
COM9 → application USB CDC
On Linux you may see a different /dev/ttyACM* number; on macOS a different /dev/cu.* entry may appear.
After the first native USB upload, refresh the port menu instead of repeatedly trying to upload to the port that existed before reset.
USB Cable Problems Are Still Extremely Common
A power-only cable can light the board perfectly while providing no data connection. Espressif’s own DevKit documentation specifically warns that some USB cables provide charging only and cannot be used for programming.
Before debugging drivers or firmware:
- Use a cable that you have already used for data transfer.
- Try a different USB port on the computer.
- Avoid unpowered hubs during troubleshooting.
- Try a short cable.
- Inspect the USB-C connector for loose solder joints on low-cost boards.
If the board powers up but never produces any device event in Device Manager, dmesg or System Information—even in forced ROM download mode—the cable or hardware becomes much more suspicious.
Windows: Device Manager Checks
Open Device Manager and watch the Ports, Universal Serial Bus and Other Devices sections while plugging and unplugging the board.
Possible results:
| What Windows shows | Meaning |
|---|---|
| COM port appears normally | USB enumeration is working |
| Unknown USB device | Electrical/driver/enumeration problem; hardware is at least being detected |
| CP210x / CH340 COM port | You are using an external USB-UART bridge path |
| ESP32 USB/JTAG/serial device | Native ESP32-S3 USB path is active |
| Nothing changes at all | Cable, connector, power or severe hardware problem |
Espressif documents dedicated driver-installation steps for built-in USB/JTAG debugging on Windows if the correct driver is missing. For ordinary CDC serial, modern operating systems usually install the required support automatically.
Linux and macOS Checks
On Linux, native USB serial commonly appears as:
/dev/ttyACM0
/dev/ttyACM1
Use:
dmesg -w
and then plug/reset the board. You should see a USB connect/disconnect event even before worrying about the Arduino IDE.
On macOS, look for new /dev/cu.* devices or inspect System Information → USB. If the OS sees the board but the IDE does not, the problem is at the development-tool layer rather than the physical USB link.
Boot Loops Can Hide USB CDC
Arduino-ESP32 explicitly warns that USB CDC may not initialise quickly enough to show useful output if the application is in a tight reboot loop.
This is a nasty failure mode because the board may repeatedly:
boot
→ crash
→ reset
→ begin USB init
→ crash again
The PC sees a device rapidly appear/disappear—or nothing stable enough for the IDE to use.
Force ROM download mode, erase or overwrite the broken application, then debug the crash using a simpler transport such as the board’s USB-UART port if available.
Deep Sleep Makes Native USB Disappear
If your sketch immediately enters deep sleep, a disappearing USB port may be expected rather than a fault.
USB Serial/JTAG and the USB PHY require clocks that are disabled during sleep. Espressif documents that native USB can therefore look disconnected from the host while the S3 sleeps.
For development, temporarily increase the awake period:
void setup() {
Serial.begin(115200);
delay(10000); // Give USB time to enumerate while debugging
// ... normal setup ...
// enter deep sleep later
}
Once the project is stable, remove the artificial delay and use OTA/network logging if that suits the final application better.
GPIO0 Wiring Can Prevent Recovery
GPIO0 is not just another GPIO during reset. It selects the boot mode.
External hardware that strongly pulls GPIO0 high can prevent the BOOT button from pulling it low. External hardware that keeps GPIO0 low can trap the chip in download mode.
During troubleshooting, disconnect anything attached to GPIO0. Espressif notes that the S3’s internal pull resistor is relatively weak, so an external circuit can easily dominate the strap state.
GPIO19/GPIO20 External Hardware Can Break USB
Even if your sketch does not reconfigure the USB pins, attached electronics can disturb the signalling.
A sensor module, pull-up, LED, relay input or long cable connected to GPIO19/GPIO20 can load USB D−/D+ enough to stop enumeration. Espressif explicitly warns that the USB pins must not be connected to hardware that interferes with the native USB interface.
If a previously working board fails only after new wiring was added, remove everything from GPIO19/GPIO20 before changing software.
Boards with Two USB Ports: Use the Right One
The ESP32-S3-DevKitC-1 is a good example of a board where two physically similar connectors can serve different purposes.
| Port | Path | Best troubleshooting use |
|---|---|---|
| USB-to-UART | PC → USB bridge → UART0 | Reliable conventional flashing and boot logs |
| ESP32-S3 USB | PC → GPIO19/20 native USB | CDC/JTAG/OTG/DFU/native USB projects |
If native USB is broken by the current application and your board has a separate UART connector, use the UART port to recover the firmware. It bypasses native USB application configuration completely.
A Minimal Recovery Sketch
When recovering an unknown USB problem, do not immediately reflash the full application. Start with something deliberately boring:
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("ESP32-S3 USB recovery test");
}
void loop() {
Serial.println("alive");
delay(1000);
}
Do not use GPIO19, GPIO20, deep sleep, TinyUSB HID/MIDI or custom USB descriptors in the first recovery build. Prove basic flashing and serial enumeration first.
If Upload Works but Serial Monitor Is Blank
Successful flashing proves the download path works. A blank monitor is a separate problem.
- Check USB CDC On Boot.
- Select the new application COM port after reset.
- Confirm the code calls
Serial.begin(). - Open the monitor after the device has enumerated.
- Add a startup delay while debugging.
- Confirm you are not monitoring the separate USB-UART port while logs are routed to native CDC, or vice versa.
The baud-rate setting shown in a CDC serial monitor is less physically meaningful than it is for a real UART, but matching the value used in your code remains the normal Arduino workflow.
If esptool Says “Failed to Connect”
Work through the failure from lowest level upward:
- Close Serial Monitor.
- Use a known data cable.
- Remove external hardware from boot and USB pins.
- Force BOOT + RESET download mode.
- Select the newly appearing port.
- Reduce upload baud rate if the serial path is marginal.
- Try another USB port/computer.
- If available, switch to the dedicated USB-UART port.
If the PC never detects anything in forced ROM download mode but the board is powered correctly, then connector damage, D+/D− routing, the USB cable or the board itself deserves more attention.
Erase Flash as a Last Software Recovery Step
If a stored application or configuration keeps causing immediate failure, you can erase flash from ROM download mode and then upload a clean program.
esptool --chip esp32s3 --port COM7 erase-flash
On Linux or macOS, replace COM7 with the actual serial device.
Erasing flash removes the application and other stored data, so do this deliberately. It does not repair physical USB damage, but it is effective when the board keeps booting into known-bad firmware.
Memory Settings Can Look Like a USB Problem
An ESP32-S3 with the wrong flash or PSRAM configuration can crash very early in boot. The result may look exactly like “USB does not work” because the application never survives long enough to expose its normal USB device.
For example, common WROOM-1 N8R8/N16R8 modules use Quad-SPI flash with Octal-SPI PSRAM. Selecting an incompatible memory configuration can produce early boot failures.
See our ESP32-S3 N8R8 vs N16R8 flash and PSRAM settings guide before assuming the native USB hardware is faulty.
USB OTG Projects Need Extra Care
If your application turns the S3 into a USB keyboard, MIDI device, storage device or another TinyUSB peripheral, the device exposed after reset is deliberately different from the development CDC device.
A successful HID project might enumerate as a keyboard and no longer expose the same serial port you used while developing. That is not necessarily an upload failure.
Keep a known recovery route:
- BOOT + RESET into ROM download mode.
- A separate USB-UART connector if the board provides one.
- Accessible GPIO0 and EN pads on a custom PCB.
For custom hardware, never bury GPIO0 and EN where they cannot be reached. Those two signals are your recovery mechanism.
Custom ESP32-S3 Boards: USB Hardware Checklist
If you designed the PCB yourself, software recovery steps only help if the USB hardware is correct.
- GPIO19 routes to USB D−.
- GPIO20 routes to USB D+.
- D+/D− are not swapped.
- USB-C CC resistors are correct for a device/UFP design.
- ESD protection is suitable and not loading the data lines excessively.
- USB differential traces are short and sensibly routed.
- VBUS/power path is designed correctly.
- GPIO0 and EN remain accessible for manual recovery.
A custom board that powers correctly but never enumerates even in ROM download mode should be debugged electrically before changing Arduino menu settings again.
Troubleshooting Table
| Symptom | Likely cause | First action |
|---|---|---|
| USB disappeared immediately after new sketch | Firmware disabled/reconfigured native USB | BOOT + RESET, flash minimal sketch |
| Board powers but no port ever appears | Charge-only cable or hardware issue | Known data cable and forced download mode |
| Port appears only while BOOT is held | Application USB is broken | Upload recovery firmware |
| Upload succeeds, sketch does not start | Still in ROM download mode | Release BOOT and press RESET |
| Port keeps appearing/disappearing | Boot loop, power problem or loose cable | Force ROM mode and flash minimal sketch |
| Native USB worked until sensor wiring added | GPIO19/20 loaded or repurposed | Remove external hardware from USB pins |
| Device disappears when sleeping | Expected native USB behaviour | Increase awake time while debugging |
| Arduino uploads but Serial Monitor is blank | Wrong CDC setting or wrong port | Enable CDC, select application port |
| CH340/CP210x remains visible but upload fails | UART auto-reset/boot mode issue | Manual BOOT + RESET |
| Only OTG/HID device appears | Application intentionally owns USB as TinyUSB device | Use BOOT mode for flashing/recovery |
| Works after erase but crashes with full project | Application or memory configuration fault | Reintroduce features incrementally |
Recommended Recovery Order
- Remove external wiring from GPIO0, GPIO19 and GPIO20.
- Use a known-good USB data cable.
- Try a direct motherboard USB port.
- Force BOOT + RESET ROM download mode.
- Confirm the OS detects a new USB/COM device.
- Flash a minimal recovery sketch.
- Reset/power-cycle after flashing.
- Check Arduino USB CDC/Upload Mode settings.
- Verify flash/PSRAM configuration.
- Reintroduce deep sleep, TinyUSB and peripherals one at a time.
- Only then suspect physical USB hardware.
In most cases, the board is recovered by step six.
Related ESP32-S3 Guides
- ESP32-S3 DevKitC-1 Pinout and Safe GPIOs — native USB pins, strapping pins and board revisions.
- ESP32-S3 N8R8 vs N16R8 Flash and PSRAM Settings — memory configuration that can cause early boot failures.
- ESP32-S3 Boards, Modules and Variants Compared — USB, memory and module differences.
Official Espressif Resources
- ESP32-S3 Boot Mode Selection — GPIO0 and ROM download mode.
- ESP32-S3 Serial and USB Connection Guide — native USB and USB-UART flashing.
- Arduino-ESP32 USB CDC and DFU Flashing — current Arduino native USB settings.
- ESP32-S3 USB Serial/JTAG Console — native CDC/JTAG behaviour and recovery limitations.
- ESP32-S3 USB Device Stack — USB-OTG architecture and GPIO19/GPIO20.