# ESP32 ULP program to read ADC when in deep sleep

**URL:** <https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884>\
**Category:** Programming\
**Created:** [May 3, 2022, 1:57pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884 "2022-05-03T13:57:33Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 3, 2022, 1:57pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/1 "2022-05-03T13:57:33Z")

</div>

Hi all,

For a project I want to read analog values from IO32 when the main processor is in deep sleep. So I use ULP for this.

I received this code from someone that is helping me:

```cpp
#include <WiFi.h> // This is the most basic WiFi implimentation
#include "esp32/ulp.h"
#include "driver/adc.h"         
#include <esp_bt.h>             
#include <esp_wifi.h>           
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
#define uS_TO_S_FACTOR 1000000  
#define TIME_TO_SLEEP 300

void power_off(){
  setCpuFrequencyMhz(80); //Save power by stepping down CPU freq. NOTE: Test this, as it can affect some sensor reading
  btStop(); // Power down BT for best power saving
  esp_wifi_stop(); // Power down the Wifi radios for best power saving
  esp_deep_sleep_start(); 
  }
  
void ulp_adc_wake_up(unsigned int low_adc_treshold, unsigned int high_adc_treshold)
{
   adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
   adc1_config_width(ADC_WIDTH_BIT_10);
   adc1_ulp_enable();

   rtc_gpio_init(GPIO_NUM_32);

   const ulp_insn_t program[] = {
      I_DELAY(32000), // Wait until ESP32 goes to deep sleep
      M_LABEL(1), // LABEL 1
        I_MOVI(R0, 0), // Set reg. R0 to initial 0
        I_MOVI(R2, 0), // Set reg. R2 to initial 0
      M_LABEL(2), // LABEL 2
        I_ADDI(R0, R0, 1), // Increment cycle counter (reg. R0)
        I_ADC(R1, 0, 0), // Read ADC value to reg. R1
        I_ADDR(R2, R2, R1), // Add ADC value from reg R1 to reg. R2
      M_BL(2, 4), // If cycle counter is less than 4, go to LABEL 2
      I_RSHI(R0, R2, 2), // Divide accumulated ADC value in reg. R2 by 4 and save it to reg. R0
      M_BGE(3, high_adc_treshold), // If average ADC value from reg. R0 is higher or equal than high_adc_treshold, go to LABEL 3
      M_BL(3, low_adc_treshold), // If average ADC value from reg. R0 is lower than low_adc_treshold, go to LABEL 3
      M_BX(1), // Go to LABEL 1
      M_LABEL(3), // LABEL 3
      I_WAKE(), // Wake up ESP32
      I_END(), // Stop ULP program timer
      I_HALT() // Halt the coprocessor
   };

   size_t size = sizeof(program)/sizeof(ulp_insn_t);
   ulp_process_macros_and_load(0, program, &size);

   ulp_run(0);
   esp_sleep_enable_ulp_wakeup();
   btStart();
   esp_wifi_start();
}

void setup() {

    
  Serial.begin(115200);
  adc_power_on();    
  power_off();
 
 
}

void loop()
{
  ulp_adc_wake_up(0, 3500);
}

```

The code compiles perfect, so that is a start 🙂

But is it correct that **ulp\_adc\_wake\_up(0, 3500);** is in the main-loop? I want to check the ADC as many times as possible, is this the correct way?

---

<div class="post-metadata">

**Author:** ![Idahowalker](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/idahowalker/32/238858_2.png) [@Idahowalker](https://forum.arduino.cc/u/Idahowalker)\
**Post date:** [May 3, 2022, 3:01pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/2 "2022-05-03T15:01:10Z")

</div>

I'd put ulp wake in setup() and leave loop empty. Also ulp wakie needs to be done before the esp32 is put to sleep. Once the ESP32 is put to sleep loop() does not run.

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 3, 2022, 3:38pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/3 "2022-05-03T15:38:12Z")

</div>

Thank you Idahowalker, that is exactly what I was thinking. I paid someone for this code so I was doubting myself. 🙂

I already had this code:

```cpp
void ulp_adc_wake_up(unsigned int low_adc_treshold, unsigned int high_adc_treshold)
{
   adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11); //adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
   adc1_config_width(ADC_WIDTH_BIT_10);
   adc1_ulp_enable();

   rtc_gpio_init(GPIO_NUM_32); //36 RTC_GPIO9 //GPIO_NUM_32
   
   gpio_deep_sleep_hold_en();
   

   const ulp_insn_t program[] = {
      I_DELAY(32000), // Wait until ESP32 goes to deep sleep
      M_LABEL(1), // LABEL 1
        I_MOVI(R0, 0), // Set reg. R0 to initial 0
        I_MOVI(R2, 0), // Set reg. R2 to initial 0
      M_LABEL(2), // LABEL 2
        I_ADDI(R0, R0, 1), // Increment cycle counter (reg. R0)
        I_ADC(R1, 0, 0), // Read ADC value to reg. R1
        I_ADDR(R2, R2, R1), // Add ADC value from reg R1 to reg. R2
      M_BL(2, 4), // If cycle counter is less than 4, go to LABEL 2
      I_RSHI(R0, R2, 2), // Divide accumulated ADC value in reg. R2 by 4 and save it to reg. R0
      M_BGE(3, high_adc_treshold), // If average ADC value from reg. R0 is higher or equal than high_adc_treshold, go to LABEL 3
      M_BL(3, low_adc_treshold), // If average ADC value from reg. R0 is lower than low_adc_treshold, go to LABEL 3
      M_BX(1), // Go to LABEL 1
      M_LABEL(3), // LABEL 3
      I_WAKE(), // Wake up ESP32
      I_END(), // Stop ULP program timer
      I_HALT() // Halt the coprocessor
   };

   size_t size = sizeof(program)/sizeof(ulp_insn_t);
   ulp_process_macros_and_load(0, program, &size);

   ulp_run(0);
   
   esp_sleep_enable_ulp_wakeup();
   esp_deep_sleep_start();
}

```

But unfortunately with this the ESP32 won't wake up anymore...

I run this code every time when I push a button like this:

`if (digitalRead(BUTTON1) == LOW){ulp_adc_wake_up(225,1023);}`

Can you see what I do wrong here?

Thanks again!

---

<div class="post-metadata">

**Author:** ![Idahowalker](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/idahowalker/32/238858_2.png) [@Idahowalker](https://forum.arduino.cc/u/Idahowalker)\
**Post date:** [May 3, 2022, 3:40pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/4 "2022-05-03T15:40:59Z")

</div>

post all the code, please.

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 4, 2022, 7:18am UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/5 "2022-05-04T07:18:39Z")

</div>

The code is very, very long (1.38MB when compiled). What exactly do you want to see?

I use these references:

> #include "esp32/ulp.h"  
> #include "soc/rtc\_cntl\_reg.h"  
> #include "driver/rtc\_io.h"  
> #include "driver/adc.h"

And in the main loop I do nothing else with this ULP code.

Please let me know if you need to see more specific code 🙂

Thanks again!

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 5, 2022, 5:56am UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/6 "2022-05-05T05:56:12Z")

</div>

I can't find any loop in the ULP (Assembly) code. Isn't that needed?

> [@Atmoz](#):
>
> ```arduino
> const ulp_insn_t program[] = {
> I_DELAY(32000), // Wait until ESP32 goes to deep sleep
> M_LABEL(1), // LABEL 1
> I_MOVI(R0, 0), // Set reg. R0 to initial 0
> I_MOVI(R2, 0), // Set reg. R2 to initial 0
> M_LABEL(2), // LABEL 2
> I_ADDI(R0, R0, 1), // Increment cycle counter (reg. R0)
> I_ADC(R1, 0, 0), // Read ADC value to reg. R1
> I_ADDR(R2, R2, R1), // Add ADC value from reg R1 to reg. R2
> M_BL(2, 4), // If cycle counter is less than 4, go to LABEL 2
> I_RSHI(R0, R2, 2), // Divide accumulated ADC value in reg. R2 by 4 and save it to reg. R0
> M_BGE(3, high_adc_treshold), // If average ADC value from reg. R0 is higher or equal than high_adc_treshold, go to LABEL 3
> M_BL(3, low_adc_treshold), // If average ADC value from reg. R0 is lower than low_adc_treshold, go to LABEL 3
> M_BX(1), // Go to LABEL 1
> M_LABEL(3), // LABEL 3
> I_WAKE(), // Wake up ESP32
> I_END(), // Stop ULP program timer
> I_HALT() // Halt the coprocessor
> };
> 
> ```

I mean, the ADC-reading has to be done VERY often, but then you would expect a loop somewhere, right?

I don't understand this exactly, can someone help me with this please?

---

<div class="post-metadata">

**Author:** ![Idahowalker](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/idahowalker/32/238858_2.png) [@Idahowalker](https://forum.arduino.cc/u/Idahowalker)\
**Post date:** [May 5, 2022, 8:57am UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/7 "2022-05-05T08:57:03Z")

</div>

The docs, [API Reference - ESP32 - — ESP-IDF Programming Guide latest documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/index.html)

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 6, 2022, 2:29pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/8 "2022-05-06T14:29:51Z")

</div>

Thanks.

Unfortunately this assembly (and ULP) code is too difficult for me.  
I'm just happy/proud that everything else works perfectly. I hope that I can get this last thing also working, but I definitely need help here 🙂

I also don't understand why the example that I posted earlies just don't work. On that forum they say it has to work so that's very strange isn't it? ☹

---

<div class="post-metadata">

**Author:** ![Idahowalker](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/idahowalker/32/238858_2.png) [@Idahowalker](https://forum.arduino.cc/u/Idahowalker)\
**Post date:** [May 6, 2022, 3:52pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/9 "2022-05-06T15:52:51Z")

</div>

> [@Atmoz](#):
>
> if (digitalRead(BUTTON1) == LOW){ulp\_adc\_wake\_up(225,1023);}

The code above will not work for many reasons.

When the ESP32 is sleeping the GPIO pins are shut down. When the ESP32 is sleeping the RTC\_GPIO pins become active but become inactive when then main CPU is awake, except when coded not to do that behavior.

The ULP passes info into the RTC\_RAM FAST and SLOW. The Main cpu cores can read the RTC ram if coded to do so.

Here is a link to the docs, [API Reference - ESP32 - — ESP-IDF Programming Guide latest documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/index.html)

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 7, 2022, 6:03am UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/10 "2022-05-07T06:03:47Z")

</div>

Wow, okay.... It is not the intention that this piece of code (_reading the button_) is executed during sleep. I just put this piece in the main loop (_of the main processor_). With this I THOUGHT to start the ULP/assembly code (_when I push that button_), and thereby put the main-processor into sleep...

But that's not right you say? Where should I call "**ulp\_adc\_wake\_up(225,1023);**" then? And how often? Does it have to be REALLY OFTEN (_in a loop_) because I actually want to read the ADC as often as possible, or just once "per sleep cycle"?

Thanks again for your input. It is really helpful 🙂

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 11, 2022, 6:10am UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/11 "2022-05-11T06:10:31Z")

</div>

Okay, I got it working now 🙂  
So I can read ADC in ULP and wake the processor up when the threshold is reached.

The code I use is:

```cpp
if (EnableSleep){
  esp_sleep_wakeup_cause_t WakkerMaakReden = esp_sleep_get_wakeup_cause();
  if (WakkerMaakReden != ESP_SLEEP_WAKEUP_ULP){
    SendUDP("Reden van wakker maken was niet ULP"); 
    delay(1000);  
    init_ulp_program();
  } else {
    ulp_ADC_reading &= UINT16_MAX;
    SendUDP("ulp_ADC_reading: " + String(ulp_ADC_reading));  
    delay(1000);
    init_ulp_program(); //Dit moet hier staan anders werkt het niet
  }
  
  SendUDP("Slaapstand starten"); 
  delay(1000);
  start_ulp_program();
  ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());

  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ALL_LOW);

  esp_sleep_enable_timer_wakeup(rtc_timer_in_seconds * convert_to_microseconds); //Iedere 20 sec. even wakker maken (werkt i.c.m. ADC uitlezen)
  esp_deep_sleep_start();

}

```

But now a new problem pops up when I also want to wake from deep-sleep with a push-button ☹  
This line of code:

`esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ALL_LOW);`

works only half: it wakes the processor nicely, but once pushed I can't wake up the processor with the ADC reading anymore.

So (wake up with) ADC reading works perfectly until I push that button for waking up...

Very strange behavior, isn't it?

Hopefully someone here can help me with this? 👍

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 12, 2022, 10:30am UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/12 "2022-05-12T10:30:34Z")

</div>

I can't understand why the push of a button does change the behavior in this case ☹

Normally I write code so that the button does exactly what I want, but in this case I didn't program anything. I just want it to wake the main processor. That works, but why does the ADC stops working then?

Is there something else that I can test/check in order to fix this?

Any help is highly appreciated 🙂

Best regards,

Atmoz

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 14, 2022, 2:46pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/13 "2022-05-14T14:46:26Z")

</div>

Bump ^^

Is there someone with a good idea that I can try? 😇 🙂

---

<div class="post-metadata">

**Author:** ![Atmoz](https://avatars.discourse-cdn.com/v4/letter/a/9de053/32.png) [@Atmoz](https://forum.arduino.cc/u/Atmoz)\
**Post date:** [May 18, 2022, 6:30am UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/14 "2022-05-18T06:30:40Z")

</div>

Nobody with an idea what this strange problem can be or how to solve it? 😔

---

<div class="post-metadata">

**Author:** ![FB2016X](https://avatars.discourse-cdn.com/v4/letter/f/958977/32.png) [@FB2016X](https://forum.arduino.cc/u/FB2016X)\
**Post date:** [May 18, 2022, 7:29pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/15 "2022-05-18T19:29:57Z")

</div>

Did not read all but, 🙂  
look at this  
esp\_sleep\_enable\_ulp\_wakeup();  
ulp\_set\_wakeup\_period(0, ULPSLEEP);

and more then init alone rtc\_gpio\_init(GPIO\_NUM\_X);  
rtc\_gpio\_set\_direction();  
rtc\_gpio\_set\_level();  
etc

I will look at your code this week, I'm using the ULP also for the first time,  
there not a lot of info.  
Try to use only English in your code, it's better to understand for everybody,  
snap je...

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [November 14, 2022, 7:30pm UTC](https://forum.arduino.cc/t/esp32-ulp-program-to-read-adc-when-in-deep-sleep/987884/16 "2022-11-14T19:30:52Z")

</div>

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.
