3

I am currently running Arduino with this configuration (using atmega328p chip):

enter image description here

Basically, my program wakes up every 2 seconds for 10ms each time. If during those 10ms it finds and event of interest it will perform some actions. Because it only takes 10ms to check for those events the battery lasts for over a year even though it wakes up every 2 seconds.

Anyways now I want to migrate that same project to an esp32. I am not able to do so because the esp32 takes about 40ms to run the startup function plus the 10ms it takes to check for events it will be 50ms. Waking up for 50ms every 2 seconds will drain my battery to fast! Is there a way I can run some code before the setup function? I believe the reason it takes 40ms is because it uses freeRTOS. Is there a way I can run the esp32 without freeRTOS just like the atmega328P? I don't mind losing WiFi/bluetooth functionality. Moreover, the esp32 chip is able to run at lower voltages than the atmega328p and this makes it very convenient when using batteries. For example now I would be able to use 2 AA batteries instead of 3 that I need now.

Gil Sven
  • 167
  • 8
Tono Nam
  • 966
  • 1
  • 10
  • 22

1 Answers1

0

What I am looking for is "Deep Sleep Wake Stubs"

Here is an example that works that I found from the internet:

#include <Arduino.h>

#include "driver/rtc_io.h" #include "rom/ets_sys.h" #include "rom/rtc.h" #include "esp_attr.h" #include "esp_sleep.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" #include "soc/rtc_io_reg.h" #include "soc/timer_group_reg.h" #include "soc/uart_reg.h" #include <stdio.h> #include <string.h>

RTC_IRAM_ATTR void deepsleep_for_us(uint64_t duration_us);

#define S_TO_NS 1000000ULL // Boot counter value, stored in RTC_SLOW_MEM static size_t RTC_DATA_ATTR boots; static size_t RTC_DATA_ATTR max_boots;

// Function which runs after exit from deep sleep static void RTC_IRAM_ATTR wake_stub(); void setup(){

} void loop(void) { if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { printf("I'm back! Wake up from (extended) deep sleep. Should be 60s elapsed.\n"); printf("Boot count=%d\n", boots); while (1) { ; } } else { printf("Not a deep sleep wake up\n"); }

printf("Going to deep sleep for 5x12=60s second\n"); boots = 0; max_boots = 12; // hold high during deep sleep esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);

// Set the wake stub function esp_set_deep_sleep_wake_stub(&wake_stub);

esp_sleep_enable_timer_wakeup(5 * S_TO_NS); esp_deep_sleep_start(); }

static const char RTC_RODATA_ATTR wake_fmt_str[] = "count=%d\n"; static const char RTC_RODATA_ATTR sleep_fmt_str[] = "sleeping\n";

static void RTC_IRAM_ATTR wake_stub() { // Increment the Boot counter boots++; // and print the Boot counter value: ets_printf(wake_fmt_str, boots);

if (boots >= max_boots) { // On revision 0 of ESP32, this function must be called: esp_default_wake_deep_sleep(); // Return from the wake stub function to continue // booting the firmware. return; }

// Print status ets_printf(sleep_fmt_str); // Wait for UART to end transmitting. // feed the watchdog REG_WRITE(TIMG_WDTFEED_REG(0), 1); while (REG_GET_FIELD(UART_STATUS_REG(0), UART_ST_UTX_OUT)) {

} deepsleep_for_us(5 * S_TO_NS);

// Set the pointer of the wake stub function. REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)&wake_stub); // Go to sleep. CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN); SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN); // A few CPU cycles may be necessary for the sleep to start... while (true) { ; } // never reaches here. }

// Comment out this line if you're using the internal RTC RC (150KHz) oscillator. //#define USE_EXTERNAL_RTC_CRYSTAL #ifdef USE_EXTERNAL_RTC_CRYSTAL #define DEEP_SLEEP_TIME_OVERHEAD_US (650 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) #else #define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) #endif // USE_EXTERNAL_RTC_CRYSTAL

RTC_IRAM_ATTR void deepsleep_for_us(uint64_t duration_us) { // Feed watchdog REG_WRITE(TIMG_WDTFEED_REG(0), 1); // Get RTC calibration uint32_t period = REG_READ(RTC_SLOW_CLK_CAL_REG); // Calculate sleep duration in microseconds int64_t sleep_duration = (int64_t)duration_us - (int64_t)DEEP_SLEEP_TIME_OVERHEAD_US; if (sleep_duration < 0) { sleep_duration = 0; } // Convert microseconds to RTC clock cycles int64_t rtc_count_delta = (sleep_duration << RTC_CLK_CAL_FRACT) / period; // Feed watchdog REG_WRITE(TIMG_WDTFEED_REG(0), 1); // Get current RTC time SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE); while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID) == 0) { ets_delay_us(1); } SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, RTC_CNTL_TIME_VALID_INT_CLR); uint64_t now = READ_PERI_REG(RTC_CNTL_TIME0_REG); now |= ((uint64_t)READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32; // Set wakeup time uint64_t future = now + rtc_count_delta; WRITE_PERI_REG(RTC_CNTL_SLP_TIMER0_REG, future & UINT32_MAX); WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, future >> 32); // Start RTC deepsleep timer REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, RTC_TIMER_TRIG_EN); // Wake up on timer WRITE_PERI_REG(RTC_CNTL_SLP_REJECT_CONF_REG, 0); // Clear sleep rejection cause // Go to sleep CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN); SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN); }

I will try to see how hard is to run my code that checks for events on the wake_stub that runs before anything else.

jsotola
  • 1,554
  • 2
  • 12
  • 20
Tono Nam
  • 966
  • 1
  • 10
  • 22