To monitor the battery pack voltage (4 x AA; max ~6V), I hooked up GPIO32 (maps to ADC1_CHANNEL_4) to a voltage divider consisting of 2 x 10K resistors. This divides the max voltage in half, giving us 3V, which is clear of the max 3.3V input voltage accepted by the ESP32 ADC pins.
To initialize the ADC channel so that it can be used by ULP code, this function is used:
I created 2 new RTC_SLOW_MEM variables:
The ADC reading ranges from 0 to 4095 in a slightly non-linear mapping, so I use this code to figure out the right ADC value for the low voltage threshold for the battery pack (target: 1.05V x 4 = 4.2V).
The ULP code below makes a series of 8 ADC readings, computes the average and writes it to VDD_ADC just in case the main CPU needs to use the value. Then it compares the average reading with VDD_LOW. If lower, it wakes the main CPU with a new wake reason WAKE_VDD_THRESHOLD_LOW. The main CPU will then stop the clock and write the clock time to the EEPROM.
In the main setup() code, all we have to do is to deal with another wake reason:
The test went quite well when I hooked up GPIO32 to a variable voltage source. When the voltage drops below 4.2V, the main CPU is woken up with the correct wake reason.