Urządzenia z ESPHome zasilane z baterii zwykle działają zrywami: budzą się, odczytują czujnik, wysyłają wynik i wracają do głębokiego uśpienia. Nowy komponent esp32_ulp daje im trzecią możliwość. 21 października 2026 trafił do gałęzi rozwojowej ESPHome 2026.11. Kompiluje mały program w C dla energooszczędnego koprocesora ESP32, dołącza go do oprogramowania i uruchamia. Koprocesor pracuje dalej, gdy główny procesor śpi, więc może sprawdzać stan pinów, odczytywać przetwornik ADC albo rozmawiać z czujnikiem i budzić główny procesor dopiero wtedy, gdy coś wymaga jego uwagi.

Autorem komponentu jest Jonathan Swoboda, a zmiana zamyka prośbę o nową funkcję numer 826.

Na jakich układach

Rodzaj koprocesora zależy od wersji układu:

  • ESP32-S2 i ESP32-S3 mają rdzeń ULP RISC-V.
  • ESP32-C5, ESP32-C6 i ESP32-P4 mają rdzeń LP.

W obu przypadkach to małe rdzenie RISC-V, które programuje się w C, korzystając z funkcji ESP-IDF ulp_riscv_* albo ulp_lp_core_*. Pierwszy ESP32 ma koprocesor innego typu (FSM) i nie jest obsługiwany. Potrzebne jest też środowisko ESP-IDF z natywnym zestawem narzędzi, czyli ustawienie domyślne. Z narzędziami PlatformIO komponent nie zadziała.

Na S2 i S3 trzeba raz zapłacić za wygodę. Program dla ich koprocesora wymaga kompilatora RISC-V, którego ESPHome normalnie nie instaluje dla tych układów z rdzeniami Xtensa. Pierwsza kompilacja z tym komponentem pobiera go więc dodatkowo, a to około 290 MB. Kompilacje bez komponentu niczym się nie różnią.

Jak to wygląda w konfiguracji

W kluczu sources podajesz pliki programu (.c, .S i .h), a w wakeup_period to, jak często licznik czasu ma go uruchamiać: od 1 µs do mniej więcej 71 minut. Program za każdym razem wykonuje się do końca. Na rdzeniu LP klucz wakeup_sources dodaje inne wyzwalacze: główny procesor, przerwanie z pinu LP IO, ruch na porcie LP UART albo zdarzenie ETM. Pozostałe opcje określają, ile pamięci RTC zarezerwować dla programu (domyślnie 4096 bajtów), czy ma ruszać przy starcie i czy może budzić główny procesor.

Każda zmienna globalna z programu jest dostępna w wyrażeniach lambda pod nazwą ulp_<nazwa>. Zmienna counter w kodzie C staje się więc ulp_counter w czujniku szablonowym. ESP-IDF deklaruje te wspólne symbole jako 32-bitowe liczby bez znaku, dlatego dokumentacja radzi trzymać wspólne wartości w typach uint32_t albo int32_t, a liczby ze znakiem rzutować z powrotem. Inaczej -1 odczytasz jako 4294967295.

Dla urządzeń na baterie najważniejsze jest to, co dzieje się podczas uśpienia. Po wybudzeniu z głębokiego uśpienia działający program nie jest ładowany od nowa, więc jego zmienne zachowują wartości. Swoboda sprawdził to na płytkach ESP32-S3-DevKitC-1 i ESP32-P4 Function EV. Licznik zwiększany co 100 ms robił na obu dokładnie 10 kroków na sekundę, a polecenie wybudzenia wysłane z programu wyprowadzało główny rdzeń z głębokiego uśpienia bez utraty stanu licznika.

O czym pamiętać

  • Program może korzystać tylko z peryferiów domeny RTC na S2 i S3 (piny RTC, przetwornik SAR ADC, magistrala RTC I2C) albo z peryferiów LP na C5, C6 i P4.
  • Koprocesor i główny procesor dzielą przetwornik ADC. Jeśli program z niego czyta, nie używaj komponentu ADC z ESPHome na żadnym kanale tego samego przetwornika.
  • Komponent deep_sleep z ESPHome nie kompiluje się jeszcze dla ESP32-P4. Na tym układzie trzeba wywołać funkcję ESP-IDF esp_deep_sleep_start() z wyrażenia lambda.