How can I use an external 32.768 kHz square wave (RTC FOUT on GPIO0) for BLE light sleep on ESP32-C6 without NimBLE downgrading to legacy advertising due to low clock accuracy?

Hi everyone,

I am developing a battery-powered alarm device using an ESP32-C6 (WROOM C6 N8) running ESP-IDF v6.0.1 (via PlatformIO).

The device stays in automatic light sleep while keeping a low-duty BLE peripheral running. During light sleep, the current draw is ~230 µA, which fits my power budget. However, I am unable to maintain a BLE connection because the controller flags the clock accuracy or falls back to the main XTAL.

Hardware Setup:
MCU: ESP32-C6 WROOM
External RTC: FRTC8900CE (with built-in TCXO / 32.768 kHz oscillator)
Wiring:

  1. RTC FOUT pin is connected directly to ESP32-C6 GPIO0 (XTAL_32K_P)
  2. FOE is tied to 3.3V through a 10k resistor.
  3. GPIO1 (XTAL_32K_N) is left floating.

The problem:

  1. When using CONFIG_RTC_CLK_SRC_EXT_OSC=y: The RTC driver measures the clock accurately (~32767 Hz), but the BLE controller refuses connection roles and logs:
    BLE_INIT: Using 32 kHz oscillator as clock source, can only run legacy ADV or SCAN due to low clock accuracy!
    When trying to connect via a phone app (LightBlue), it disconnects immediately:
    BLE: Disconnected, reason=520

  2. When switching to CONFIG_RTC_CLK_SRC_EXT_CRYS=y: Because FOUT is a direct 3.3V square wave (not a bare quartz crystal), the bootloader’s amplitude/startup checks fail:
    W (1693) clk: 32 kHz clock not found, switching to internal 150 kHz oscillator … W (7052) BLE_INIT: 32.768kHz XTAL not detected, fall back to main XTAL as Bluetooth sleep clock I (7062) BLE_INIT: Using main XTAL as clock source, chip ver: 2
    This allows connections, but keeps the 40 MHz XTAL active, driving the sleep current from ~230 µA up to ~4 mA.

How can I make this work (if it is even possible)? Meaning I have ~230 µA during light sleep and I am able to connect using BLE.

My sdkconfig.defaults file:

CONFIG_IDF_TARGET=“esp32c6”

CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=“partitions.csv”

CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
CONFIG_ESP_PHY_MAC_BB_PD=y

CONFIG_RTC_CLK_SRC_EXT_CRYS=y
CONFIG_RTC_CLK_SRC_EXT_OSC=y
CONFIG_BT_LE_LP_CLK_SRC_DEFAULT=y

CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_LE_SLEEP_ENABLE=y
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED=y
CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED=y
CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED=y
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=n

CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y

CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=4
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=8
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=8
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n

Since the info / error message comes from the ESP-IDF bluetooth stack, please open an issue with Espressif.

https://github.com/espressif/esp-idf/issues

Will do that as well, thank you. But I just wanted to ask if there is an existing Kconfig flag, hardware setup or something else I missed. If someone knows, please let me know. Thanks guys

This is a documented BLE controller restriction in ESP-IDF v6.0.1. Under CONFIG_BT_LE_LP_CLK_SRC_DEFAULT, Espressif lists support for only legacy advertising/scanning when the system RTC source is anything other than the external 32.768 kHz crystal. That matches your warning; it isn’t necessarily a measurement showing that your TCXO is inaccurate. See the C6 controller configuration documentation.

There are two things to straighten out:

  1. Select only one RTC source. EXT_CRYS and EXT_OSC are mutually exclusive choices. Check the generated sdkconfig, because changing sdkconfig.defaults does not necessarily replace existing settings. Espressif explains that behaviour in its build documentation.

  2. A driven clock and a passive crystal require different configurations. Selecting EXT_CRYS does not make FOUT electrically equivalent to a crystal. PCBWay’s active oscillator versus passive crystal explanation gives some background on that distinction.

For the documented low-power connection path, disconnect FOUT and fit a suitable 32.768 kHz crystal between GPIO0 and GPIO1, with the load capacitors specified for that crystal and layout. Follow Espressif’s C6 hardware guidance, then use:

CONFIG_RTC_CLK_SRC_EXT_CRYS=y
# CONFIG_RTC_CLK_SRC_EXT_OSC is not set
CONFIG_BT_LE_LP_CLK_SRC_DEFAULT=y
CONFIG_BT_LE_SLEEP_ENABLE=y

Rebuild and flash both the bootloader and application, then confirm that the clock no longer falls back.

Also, don’t apply older ESP32 external-clock amplitude limits to the C6: its external-oscillator documentation describes a digital input. The 3.3 V amplitude alone doesn’t explain the controller restriction.

Keeping FOUT would require an Espressif-supported solution for that controller/version; I haven’t verified a configuration-only workaround. Finally, 230 µA between radio events won’t necessarily mean 230 µA averaged over a connection. This low-power overview provides general background, but its classic-ESP32 current figures and examples should not be transferred directly to the C6.