ESP32/Sleep and wake-up/73. The ULP low-power core
Your chip
Your language
Sleep and wake-up · 73 of 81

The ULP low-power core

A tiny processor that stays awake while the rest of the chip sleeps, reading an ADC or a pin for about a hundred microamps. It is transformative when the answer is usually nothing happened, and effort for nothing when it is not.

/esp32/the-ulp-low-power-core · arduino · S3

When it is worth the trouble

60 checks a minute, 4% of them matter
24.6× better
Checks per minute60
Checks that find something worth waking for4%
Without the ULP
16 mA
With it
641 µA
Improvement
24.6×
24.6× on the same battery, for a job that is mostly “still nothing”. This is the case the ULP was built for: watch a water level, a light sensor, a reed switch — thousands of times an hour — and only start the expensive part of the chip when the reading crosses a line. It keeps its own variables in RTC memory, which is how the main core reads them after waking.

Before you write any ULP assembly

Check whether the hardware can do it for free. Most "watch a value and wake up" problems are really:

  • A threshold output on the sensor plus ext0 wake. Zero extra current.
  • A touch pad with a hardware threshold. A few microamps.
  • A timer wake every few seconds, if the event is not that urgent.

The ULP earns its place when you genuinely need an analog value compared thousands of times an hour and no sensor will do it for you.

What it is like to write

Very small assembly on the original ESP32 and S2, or restricted C on the S2 and S3. No Wi-Fi, no I2C drivers, no floating point, no debugger, and a handful of registers. It talks to the main core through RTC slow memory and nothing else. That is the cost, and it is why the hit rate above matters so much.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

The main core sets up the ULP program and goes to sleep. The ULP polls, compares, and only wakes the big cores when a threshold is crossed - so the expensive part of the chip runs once an hour instead of once a second.

ulp_threshold.ino
#include <esp32/ulp.h>
#include <driver/rtc_io.h>
#include <esp_sleep.h>

RTC_DATA_ATTR uint32_t crossings = 0;

void setup() {
  Serial.begin(115200);

  if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_ULP) {
    crossings++;
    Serial.printf("the ULP woke us, %u times so far\n", crossings);
    // ... react: send a reading, sound an alarm ...
  } else {
    Serial.println("cold start - loading the ULP program");
    // ulp_load_binary(...) then ulp_run(...) with a program that
    // samples the ADC every 100 ms and wakes us past a threshold
  }

  esp_sleep_enable_ulp_wakeup();
  esp_deep_sleep_start();
}

void loop() {}

RTC_SLOW_MEM is the shared workspace. It is the only memory both processors can see, and it is how the ULP hands over what it measured.

When it does not work

The ULP program does nothing and there is no error

There is no debugger and no serial output. Write intermediate values into RTC_SLOW_MEM and print them from the main core after waking - that is the only visibility you get.

The board wakes constantly

Your threshold is inside the noise. The ULP's ADC readings are noisier than the main core's. Average several samples in the ULP program and add hysteresis.

My chip does not have it

The C3 has no ULP. The original ESP32 and S2 have the assembly one, the S2 and S3 also have a RISC-V ULP you can write C for.

I saved less than I expected

Then the main core was waking often anyway. Check the hit rate first - below a few percent the ULP is dramatic, and above a third it is not worth writing.

Where this goes next

Sleep is the demand side. The supply side is a regulator with a few hundred milliamps in it and a radio that wants a quarter of an amp.

Power and the 3V3 rail

Edit this page — content/esp32/the-ulp-low-power-core.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum