Investigating ESP32-C3 Power Management

I’m currently working on some electronic jewelry and have decided to use the ESP32 C3 chip. Part of the rationale is that is a member of the Espressif family of MCUs and I’m quite familiar with Espressif’s development platform. Another factor is that the ESP32 C3 chip is available in a small form-factor of QFN28 although I haven’t been able to use the QFN28 parts yet as they don’t seem to be in stock at JLCPCB even though Mouser have them.

The ESP32-C3 is a RISC-V based chip but otherwise has quite a lot in common with the Xtensa based chips (S3, etc) in terms of peripheral support. According to Adafruit it is supposed to have good low-power characteristics at less than 500uA in sleep modes.

The jewelry I’m working on will have LED displays of various kinds as well as some I2C and I2S comms but it is the processor’s own low-power characteristics that I’m trying to gauge right now. The scope of things I’d like to understand include:

  • Base ESP32 C3 power usage with no peripherals active (including no BLE or WiFi) but full CPU operation at normal operating frequency
  • Effect of reducing operating frequency (the lowest I think worth trying is 10MHz)
  • Light-sleep mode power usage
  • Deep-sleep mode power usage
  • Anything else that might be interesting to know as investigations progress

Note that a 40MHz crystal is used in my designs and this results in a default CPU speed (according to the esp_clk_cpu_freq() function) of 160MHz.

Update Dec 2023: I’ve done some additional testing of Bluetooth Low Energy and have added some results for that in the table below as well as more detailed information lower down.

CPU Current Usage Table

The current usage is as follows:

ModeAverage Current (mA)Peak Current (mA)
All off, empty loop()26.834.4
All off, loop() with 10ms delay20.1 **41.0
CPU light-sleep everything off0.350.35
Clock speed set to 10MHz7.4 ***20.2
BLE off, 160MHz clock, no sleep – base case:
(25 x SK6805EC-15 LEDs powered but zero illumination)
31.957.1
BLE advertising WITHOUT modem sleep enabled ***97.2224.0
BLE advertising WITH modem sleep enabled + default advertising interval42.2229.6
BLE advertising WITH modem sleep and 1 second advertising interval35.0216.8
BLE connected (with modem sleep)40.65231.2

** All of these measurements were made using the Arduino IDE. I can’t explain the difference in current consumption with two forms of essentially empty loop and I will be using the ESP IDF in any case for the final firmware so I haven’t looked into this further.

*** Changing the CPU frequency clearly has a significant impact but even at 10MHz the current consumption is a lot higher than I would like so I will definitely be making use of the light-sleep mode. According to the documentation for ESP32 C3 the deep-sleep mode results in a full reset of the CPU on wakeup so that would require some additional startup time and for my purposes the extra 150uA saving that Adafruit reported on in their assessment isn’t really worth the extra hassle.

*** Clearly these figures are huge! I’m not sure why BLE defaults to having modem sleep turned off

A final thought is that even in sleep mode the CPU consumption is well above what is reasonable to draw from a small LiPo battery when the device is not in use. So an additional power management circuit will be required in any case to completely remove power from the ESP32 C3 when it is not operating.

More detail on the tests is included below …

CPU Running Empty loop()

void setup() {
  initAllOff();
}
void loop() {
}

This isn’t totally straightforward, the average current is around 27mA but it spikes up to around 32mA for 500us every 5ms.

CPU Light Sleep

void setup() {
  initAllOff();
  esp_light_sleep_start();
}
void loop() {
}

Light sleep gets us down to 2.2mA average, but again there is this spike (in this case to 7mA) for 500uS every 5ms. I’m not clear why this is happening.

LEDs On

void setup() {
  initAllOff();
  esp_light_sleep_start();
}
void loop() {
  digitalWrite(LEDS_4, HIGH);
  delay(1);
  digitalWrite(LEDS_4, LOW);
  delay(19);
}

4 Inner LEDs …

Turning LEDs on with a 5% duty cycle (1ms on, 19ms off) results in an average 21.1mA which is extremely odd because it is lower than just running in a fast loop.

14 outer LEDs …

This seems odd, the 4 inner LEDs draw more current than the 14 outer ones! Not sure why this is happening.

CPU Running loop() with Delay

A rather complex picture. But it is clear that an empty loop() consumes more power than a loop() with a delay in it! And the peaks in this scenario are higher than the peaks without the delay! I tried different values of delay in the loop (down to 1ms) but it makes little difference.

Base case for BLE tests

This case includes 25x SK6805EC-15 LEDs powered but not illuminated as those are part of my current design.

BLE advertising without modem sleep

Using the following ESP IDF settings to configure BLE:

CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="JewelOS"
CONFIG_BT_NIMBLE_ROLE_CENTRAL=n
CONFIG_BT_NIMBLE_ROLE_OBSERVER=n
CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=n
CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING=y
CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL=y
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=6000

I found that the current consumption is REALLY high

BLE with modem sleep enabled

Adding the lines:

CONFIG_BT_CTRL_MODEM_SLEEP=y
CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y
CONFIG_BT_CTRL_LPCLK_SEL_MAIN_XTAL=y

Significantly reduces power consumption!

BLE advertising with modem sleep and 1s advertising interval

Setting advertising interval to 1s reduces consumption further.

BLE connected

During the connection process current spikes to almost 100mA for around 400ms and then settles to around 40mA.