Fix: Serial Monitor shows nothing on ESP32-C3 / S3 (USB CDC On Boot)
Upload works but Serial.println() prints nothing. On native-USB boards, enable Tools → USB CDC On Boot, wait for the port in setup(), and reopen the monitor.
Community draft — report a mistake
What you see
Wrote 280576 bytes (157010 compressed) at 0x00010000 in 2.6 seconds (effective 862.4 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
--- Serial Monitor (115200 baud) ---
(nothing)Wrote 280576 bytes (157010 compressed) at 0x00010000 in 2.6 seconds (effective 862.4 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... --- Serial Monitor (115200 baud) --- (nothing)
Boards like the ESP32-C3 SuperMini, XIAO and many ESP32-S3s have no CH340 or CP2102 chip. The ESP32 itself is the USB device. Whether Serial goes to that USB port or to two GPIO pins depends on one menu option, USB CDC On Boot.
With it disabled, uploads still work (the ROM bootloader handles USB by itself), but your sketch prints to pins with nothing attached. Turn it on, add a short delay in setup(), and the text appears.
Quick fix — try in order
- 1
Tools → USB CDC On Boot → Enabled, then upload again
With it disabled,
Serialmeans the UART pins (GPIO20/21 on C3, GPIO43/44 on S3), not the USB socket. Enabling it routes Serial to USB. - 2
Wait for the USB port in setup()
Add
Serial.begin(115200); delay(1500);orwhile (!Serial && millis() < 3000) {}. Native USB takes a moment to appear after reset; anything printed before that is lost. - 3
Reopen Serial Monitor after each upload
The COM port disappears and comes back when the board resets. In Arduino IDE 2 re-select the port if it changed number, then reopen the monitor.
- 4
Two-port S3 boards: use the matching port
On ESP32-S3-DevKitC-1 the UART socket shows Serial with CDC On Boot disabled; the USB socket needs it enabled. Pick one and stick to it.
- 5
Check it with the diagnostic sketch
It waits 1.5 s and prints a report plus a heartbeat every 5 s, so you know whether the board or the monitor is the problem.
Diagnostic sketch
// FlashReady diagnostic: why did my ESP32 reset?
// Prints the reset reason, chip, flash and PSRAM details once at boot, then a heartbeat
// every 5 s. If the uptime keeps going back to 0, the board is rebooting - and the
// "Reset reason" line tells you which kind of reset it was.
// Works on ESP32, ESP32-C3 and ESP32-S3 (on C3/S3 native USB set Tools -> USB CDC On Boot -> Enabled).
#include <esp_system.h>
#include <esp_flash.h>
const char *reasonText(esp_reset_reason_t r) {
switch (r) {
case ESP_RST_POWERON: return "POWERON - power applied or EN/RST pressed";
case ESP_RST_EXT: return "EXT - external reset pin";
case ESP_RST_SW: return "SW - ESP.restart() or a library called esp_restart()";
case ESP_RST_PANIC: return "PANIC - crash (Guru Meditation); scroll up for the backtrace";
case ESP_RST_INT_WDT: return "INT_WDT - interrupt watchdog (an ISR or critical section ran too long)";
case ESP_RST_TASK_WDT: return "TASK_WDT - task watchdog (loop() blocked too long)";
case ESP_RST_WDT: return "WDT - other watchdog (RTC or timer group)";
case ESP_RST_DEEPSLEEP: return "DEEPSLEEP - woke from deep sleep";
case ESP_RST_BROWNOUT: return "BROWNOUT - supply dipped below ~2.4 V; fix the power";
case ESP_RST_SDIO: return "SDIO - reset over SDIO";
default: return "other / unknown";
}
}
void setup() {
Serial.begin(115200);
delay(1500); // give native-USB boards time to enumerate before printing
esp_reset_reason_t r = esp_reset_reason();
Serial.println();
Serial.println("=== FlashReady reset-reason report ===");
Serial.printf("Reset reason : %d (%s)\n", (int)r, reasonText(r));
Serial.printf("Chip : %s rev %d, %d core(s), %lu MHz\n", ESP.getChipModel(), ESP.getChipRevision(),
ESP.getChipCores(), (unsigned long)ESP.getCpuFreqMHz());
uint32_t physical = 0;
esp_flash_get_physical_size(esp_flash_default_chip, &physical);
uint32_t header = ESP.getFlashChipSize();
Serial.printf("Flash (chip) : %lu KB\n", (unsigned long)(physical / 1024));
Serial.printf("Flash (IDE) : %lu KB at %lu MHz\n", (unsigned long)(header / 1024),
(unsigned long)(ESP.getFlashChipSpeed() / 1000000));
if (physical && header > physical) {
Serial.println("WARNING : Tools -> Flash Size is larger than the real chip. Lower it.");
}
Serial.printf("PSRAM : %lu KB%s\n", (unsigned long)(ESP.getPsramSize() / 1024),
ESP.getPsramSize() ? "" : " (none found or PSRAM disabled in Tools)");
Serial.printf("Sketch : %lu KB used, %lu KB free in app partition\n",
(unsigned long)(ESP.getSketchSize() / 1024), (unsigned long)(ESP.getFreeSketchSpace() / 1024));
Serial.printf("Free heap : %lu bytes\n", (unsigned long)ESP.getFreeHeap());
Serial.println("======================================");
}
void loop() {
static uint32_t last = 0;
if (millis() - last >= 5000) {
last = millis();
Serial.printf("alive, uptime %lu s, free heap %lu\n", (unsigned long)(millis() / 1000),
(unsigned long)ESP.getFreeHeap());
}
}// FlashReady diagnostic: why did my ESP32 reset?
// Prints the reset reason, chip, flash and PSRAM details once at boot, then a heartbeat
// every 5 s. If the uptime keeps going back to 0, the board is rebooting - and the
// "Reset reason" line tells you which kind of reset it was.
// Works on ESP32, ESP32-C3 and ESP32-S3 (on C3/S3 native USB set Tools -> USB CDC On Boot -> Enabled).
#include <esp_system.h>
#include <esp_flash.h>
const char *reasonText(esp_reset_reason_t r) {
switch (r) {
case ESP_RST_POWERON: return "POWERON - power applied or EN/RST pressed";
case ESP_RST_EXT: return "EXT - external reset pin";
case ESP_RST_SW: return "SW - ESP.restart() or a library called esp_restart()";
case ESP_RST_PANIC: return "PANIC - crash (Guru Meditation); scroll up for the backtrace";
case ESP_RST_INT_WDT: return "INT_WDT - interrupt watchdog (an ISR or critical section ran too long)";
case ESP_RST_TASK_WDT: return "TASK_WDT - task watchdog (loop() blocked too long)";
case ESP_RST_WDT: return "WDT - other watchdog (RTC or timer group)";
case ESP_RST_DEEPSLEEP: return "DEEPSLEEP - woke from deep sleep";
case ESP_RST_BROWNOUT: return "BROWNOUT - supply dipped below ~2.4 V; fix the power";
case ESP_RST_SDIO: return "SDIO - reset over SDIO";
default: return "other / unknown";
}
}
void setup() {
Serial.begin(115200);
delay(1500); // give native-USB boards time to enumerate before printing
esp_reset_reason_t r = esp_reset_reason();
Serial.println();
Serial.println("=== FlashReady reset-reason report ===");
Serial.printf("Reset reason : %d (%s)\n", (int)r, reasonText(r));
Serial.printf("Chip : %s rev %d, %d core(s), %lu MHz\n", ESP.getChipModel(), ESP.getChipRevision(),
ESP.getChipCores(), (unsigned long)ESP.getCpuFreqMHz());
uint32_t physical = 0;
esp_flash_get_physical_size(esp_flash_default_chip, &physical);
uint32_t header = ESP.getFlashChipSize();
Serial.printf("Flash (chip) : %lu KB\n", (unsigned long)(physical / 1024));
Serial.printf("Flash (IDE) : %lu KB at %lu MHz\n", (unsigned long)(header / 1024),
(unsigned long)(ESP.getFlashChipSpeed() / 1000000));
if (physical && header > physical) {
Serial.println("WARNING : Tools -> Flash Size is larger than the real chip. Lower it.");
}
Serial.printf("PSRAM : %lu KB%s\n", (unsigned long)(ESP.getPsramSize() / 1024),
ESP.getPsramSize() ? "" : " (none found or PSRAM disabled in Tools)");
Serial.printf("Sketch : %lu KB used, %lu KB free in app partition\n",
(unsigned long)(ESP.getSketchSize() / 1024), (unsigned long)(ESP.getFreeSketchSpace() / 1024));
Serial.printf("Free heap : %lu bytes\n", (unsigned long)ESP.getFreeHeap());
Serial.println("======================================");
}
void loop() {
static uint32_t last = 0;
if (millis() - last >= 5000) {
last = millis();
Serial.printf("alive, uptime %lu s, free heap %lu\n", (unsigned long)(millis() / 1000),
(unsigned long)ESP.getFreeHeap());
}
}Why it happens
Why does Serial go to the wrong place?
These chips have no USB-serial converter chip. With CDC On Boot disabled, Serial is UART0 on two GPIO pins and the USB port is only used for flashing. Your text is being printed — on pins nothing is connected to.
It prints a little, then stops
If your sketch uses deep sleep or crashes, the USB port goes away with the CPU. Messages before the crash may be lost; add a delay at the start of setup() to read them.
Using Serial0 / USBSerial
With CDC On Boot enabled, Serial is USB and Serial0 is UART0. Libraries that hard-code Serial0 will print to the pins.
Board-specific notes
ESP32-C3 SuperMini: Always enable USB CDC On Boot on this board — it has no other way to show Serial. See the C3 SuperMini page.
XIAO ESP32C3: Same rule. See the XIAO ESP32C3 page.
LOLIN S2 Mini: The S2 uses TinyUSB: enable USB CDC On Boot and expect the port to change number between bootloader and sketch.
Did this fix it?
Thanks — that helps us keep this page right.