ESP32C3 will not wake up after deepsleep time

Hello,

in common you keep all your Serial.begin() and Serial.print(“hello”) statements, when you start to run your ESP battery powered, using the battery pins, or directly the 3.3V pin.

Unfortunately the C3 seems to have a problem to wake up (timed) after deep sleep, as it will crash on the active Serial.xy lines within the code.

Any thoughts?

As soon as you remove all the Serial.xy lines C3 wil work properly again…?!

This is the test-project i created, that will only work without serial.xy lines:

#include <Arduino.h>


# define sensor_power_pin 3      /* power up sensor */
# define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */

uint64_t time_to_sleep = 5;      /* sleep for 5 seconds - short schedule just for testing */


void setup() {

  //Serial.begin();
  //Serial.println("Hello World");
  pinMode(sensor_power_pin, OUTPUT);


  /*power up sensor */
  digitalWrite(sensor_power_pin, HIGH);

  delay(5000);

  digitalWrite(sensor_power_pin, LOW);


  /* GO DEEP SLEEP */

  Serial.println("Done - activating deepsleep mode");
  time_to_sleep = time_to_sleep * uS_TO_S_FACTOR;
  esp_sleep_enable_timer_wakeup(time_to_sleep);
  Serial.flush(); 
  esp_deep_sleep_start();

}

void loop() {
  // put your main code here, to run repeatedly:

}

esp_sleep_enable_timer_wakeup expects an uint64_t ( unsigned long long integer ).

Try
#define uS_TO_S_FACTOR 1000000ULL
instead of
#define uS_TO_S_FACTOR 1000000

Hi,
thanks for the response… just tested your approach without success.

please keep in mind - the problem only occurs when the esp32c3 is battery powered, or connected to a USB cable without data-lines.
It looks like the ESP crashes somehow directly after wake up.
But how to debug? How to do error handling in that case?
Or is it simply a bug?
All other ESP’s do not crash due to missing serial/USB connection

This is the current code that will not work on a battery powered esp32c3 - remove all serial prints and it will work!

#include <Arduino.h>


# define sensor_power_pin 3      /* power up sensor */
# define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */

uint64_t time_to_sleep = 5;      /* sleep for 5 seconds - short schedule just for testing */


void setup() {
  delay(2000);

  Serial.begin();
  Serial.println("Hello World");
  pinMode(sensor_power_pin, OUTPUT);


  /*power up sensor */
  digitalWrite(sensor_power_pin, HIGH);
  delay(1000);
  digitalWrite(sensor_power_pin, LOW);
  delay(1000);
  digitalWrite(sensor_power_pin, HIGH);
  delay(1000);
  digitalWrite(sensor_power_pin, LOW);
  delay(1000);
  digitalWrite(sensor_power_pin, HIGH);
  delay(1000);
  digitalWrite(sensor_power_pin, LOW);  
  /* GO DEEP SLEEP */

  Serial.println("Done - activating deepsleep mode");
  time_to_sleep = time_to_sleep * uS_TO_S_FACTOR;
  esp_sleep_enable_timer_wakeup(time_to_sleep);
  Serial.flush(); 
  esp_deep_sleep_start();

}

void loop() {
  // put your main code here, to run repeatedly:

}```

just found a thread with a similar issue - pointing to comment out the Serial.begin() statement only – https://forum.seeedstudio.com/t/xiao-esp32c3-battery-ussage/266185

…but maybe some has a more elegant solution, than commenting out lines from the code… any idea how to “error” handle it?

I have encountered the same issue on ESP32-C3. I have used this code:

  if(Serial)
  {
    Serial.begin(115200);
  }

Apparently, this is not a problem on ESP32-S3

1 Like

I think the issue is the .flush() command, which is blocking when not connected to a serial port. Seems to be a known problem with the ESPC32/S3:

USBCDC::flush is blocking

Remove the Serial.flush() and it should be working (in case anyone else is experiencing this issue).

Or if you want to make sure that serial buffer is completely flushed before entering deep sleep (not tested):

if(Serial)
{
	Serial.flush();
}
1 Like

I avoid all serial comms if the USB isn’t connected - seems to be working so far :crossed_fingers:

void setup() {
  if(usb_serial_jtag_is_connected())  {
    UseSerial = true;
  }
  else  {
    UseSerial = false;
  }

  if(UseSerial) {
    Serial.begin(115200);
...

Finally claude.code was able to explain the root cause! Many of you already pointed into the correct direction - thanks. This is the explanation:

Yes, this is a well-known issue specific to boards that use native USB CDC for Serial — and the XIAO ESP32-C3 is one of them.

Root Cause

Unlike older ESP8266/ESP32 boards that have a dedicated USB-to-UART chip (CH340, CP2102), the XIAO ESP32-C3 uses the ESP32-C3’s built-in USB CDC as Serial. This changes the behavior fundamentally:

  • When a USB host is connected, Serial.print() writes into a buffer that gets drained by the host

  • When no USB host is present, the CDC TX buffer fills up and blocks, waiting for a consumer that never comes

  • This eventually triggers the watchdog timer, which resets the chip

Why deep sleep specifically breaks

The critical line in your code is main.cpp:35:

Serial.flush();

Serial.flush() explicitly blocks until the TX buffer is empty. Without a USB host, this never completes → watchdog reset fires → esp_deep_sleep_start() on line 36 is never reached. The chip just resets in a hard boot loop instead of entering sleep.

The fix

Wrap all serial output in a USB-connected check. Arduino’s USB CDC Serial object evaluates to true only when a host is connected:

if (Serial) {
  Serial.println("Hello World");
}

and if not needed - remove Serial.flush() or wrap this line as well