Глубокий сон ESP32 и источники пробуждения

Глубокий сон ESP32 и источники пробуждения

Когда ваш IoT-проект питается от сетевого адаптера, вы не слишком беспокоитесь о потреблении энергии. Но если вы собираетесь питать свой проект от батареи, каждый мА на счету.

ESP32 может быть довольно энергоёмким устройством в зависимости от того, в каком состоянии он находится. Обычно он потребляет около 75 мА при нормальной работе и около 240 мА при передаче данных по WiFi.

Решение здесь заключается в снижении энергопотребления ESP32 за счёт использования режима глубокого сна (Deep Sleep).

Чтобы узнать больше о других режимах сна ESP32 и их энергопотреблении, пожалуйста, ознакомьтесь с руководством ниже.

Обзор режимов сна ESP32 и их энергопотребление

ESP32 — впечатляющий кусок кремния: он содержит два мощных процессорных ядра, которые могут обрабатывать данные на сотнях мегагерц, подключаться по Wi-Fi и…

/lastminuteengineers/esp32-sleep-modes-power-consumption/index

Глубокий сон ESP32

В режиме глубокого сна процессоры, большая часть оперативной памяти и все цифровые периферийные устройства отключаются. Единственные части чипа, которые остаются работоспособными:

  • ULP-сопроцессор

  • RTC-контроллер

  • RTC-периферия

  • Быстрая и медленная память RTC

Чип потребляет около 0,15 мА (если ULP-сопроцессор включён) до 10 мкА.

Функциональная блок-схема глубокого сна ESP32 и потребление тока

Во время режима глубокого сна основной процессор выключен, в то время как сопроцессор сверхнизкого энергопотребления (ULP) может снимать показания датчиков и пробуждать процессор при необходимости. Этот режим сна известен как шаблон мониторинга датчиков ULP. Это полезно для проектирования приложений, в которых процессор должен быть разбужен внешним событием, таймером или комбинацией обоих, при сохранении минимального энергопотребления.

Вместе с процессором основная память чипа также отключается. В результате всё, что хранилось в этой памяти, стирается и не может быть прочитано.

Поскольку память RTC остаётся включённой, её содержимое сохраняется даже во время глубокого сна и может быть восстановлено после пробуждения чипа. Именно поэтому чип сохраняет данные подключения Wi-Fi и Bluetooth в памяти RTC перед переходом в глубокий сон.

Если вы хотите использовать данные после перезагрузки, сохраняйте их в памяти RTC, определив глобальную переменную с атрибутом RTC_DATA_ATTR. Например, RTC_DATA_ATTR int myVar = 0;

После выхода из глубокого сна чип перезапускается с полным сбросом и начинает выполнение программы с самого начала.

Примечание

ESP32 поддерживает запуск заглушки пробуждения из глубокого сна при выходе из глубокого сна. Эта функция выполняется сразу же, как только чип просыпается — до того, как будет запущена какая-либо обычная инициализация, загрузчик или код ESP-IDF. После выполнения заглушки пробуждения чип может вернуться в спящий режим или продолжить нормальный запуск ESP-IDF.

В отличие от других режимов сна, система не может автоматически перейти в режим глубокого сна. Функция esp_deep_sleep_start() используется для немедленного перехода в глубокий сон после настройки источников пробуждения.

Источники пробуждения из глубокого сна ESP32

ESP32 может быть разбужен из режима глубокого сна с использованием нескольких источников. Эти источники:

  • Таймер

  • Сенсорная панель (Touch pad)

  • Внешнее пробуждение (ext0 и ext1)

Несколько источников пробуждения могут быть скомбинированы, в этом случае чип проснётся, когда один из источников сработает.

Предупреждение

Можно перевести ESP32 в глубокий сон без настроенных источников пробуждения, в этом случае чип останется в режиме глубокого сна на неопределённое время, пока не будет выполнен внешний сброс.

Источник пробуждения ESP32: Таймер

RTC-контроллер ESP32 имеет встроенный таймер, который можно использовать для пробуждения ESP32 через заданное время.

Эта функция особенно полезна в проектах, требующих временных меток или ежедневных задач при сохранении низкого энергопотребления.

Функция esp_sleep_enable_timer_wakeup(time_in_us) используется для настройки таймера в качестве источника пробуждения. Эта функция принимает время в микросекундах (мкс).

Пример кода

Давайте посмотрим, как это работает, используя пример из библиотеки. Откройте вашу Arduino IDE и перейдите в File > Examples > ESP32 > Deep Sleep, затем откройте скетч TimerWakeUp.

Этот скетч демонстрирует самый простой пример глубокого сна с таймером в качестве источника пробуждения и то, как сохранять данные в памяти RTC для использования после перезагрузки.

#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up every 5 seconds
  */
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

  /*
  Next we decide what all peripherals to shut down/keep on
  By default, ESP32 will automatically power down the peripherals
  not needed by the wakeup source, but if you want to be a poweruser
  this is for you. Read in detail at the API docs
  http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
  Left the line commented as an example of how to configure peripherals.
  The line below turns off all RTC peripherals in deep sleep.
  */
  //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
  //Serial.println("Configured all RTC Peripherals to be powered down in sleep");

  /*
  Now that we have setup a wake cause and if needed setup the
  peripherals state in deep sleep, we can now start going to
  deep sleep.
  In the case that no wake up sources were provided but deep
  sleep was started, it will sleep forever unless hardware
  reset occurs.
  */
  Serial.println("Going to sleep now");
  Serial.flush();
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

После загрузки скетча откройте монитор порта и установите скорость 115200 бод.

ESP32 просыпается каждые 5 секунд, выводит причину пробуждения и значение bootCount в монитор порта, а затем снова переходит в глубокий сон.

Вывод пробуждения ESP32 из глубокого сна по таймеру

Теперь попробуйте сбросить ESP32, нажав кнопку EN — это должно сбросить bootCount обратно в 1, что указывает на то, что память RTC полностью очищена.

Объяснение кода

Первые две строки кода определяют время, в течение которого ESP32 будет находиться в спящем режиме.

В этом примере используется коэффициент пересчёта из микросекунд в секунды, поэтому вы можете установить время сна в секундах в переменной TIME_TO_SLEEP. Здесь ESP32 переводится в режим глубокого сна на 5 секунд.

#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

Как указано ранее, вы можете сохранять данные в памяти RTC ESP32 (8 кБ SRAM), которая не стирается при глубоком сне. Однако она стирается при сбросе ESP32.

Чтобы сохранить данные в памяти RTC, вам просто нужно добавить атрибут RTC_DATA_ATTR перед определением переменной. В этом примере переменная bootCount сохраняется в памяти RTC. Она будет подсчитывать, сколько раз ESP32 просыпался из глубокого сна.

RTC_DATA_ATTR int bootCount = 0;

Далее определяется функция print_wakeup_reason(), которая выводит причину, по которой ESP32 был разбужен из глубокого сна.

void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

В функции setup мы сначала инициализируем последовательную связь с ПК.

Serial.begin(115200);

Затем переменная bootCount увеличивается на единицу и выводится в монитор порта, чтобы показать количество раз, которое ESP32 просыпался из глубокого сна.

++bootCount;
Serial.println("Boot number: " + String(bootCount));

Затем вызывается функция print_wakeup_reason(), но вы можете вызвать любую функцию для выполнения нужной задачи, например, считывание значения датчика.

print_wakeup_reason();

Далее мы настраиваем источник пробуждения по таймеру с помощью функции esp_sleep_enable_timer_wakeup(time_in_us). Здесь ESP32 настроен на пробуждение каждые 5 секунд.

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

Наконец, ESP32 переводится в спящий режим вызовом функции esp_deep_sleep_start().

esp_deep_sleep_start();

В этом скетче ESP32 переходит в глубокий сон в самой функции setup(), поэтому он никогда не доходит до функции loop(). Следовательно, функция loop() оставлена пустой.

void loop(){
  //This is not going to be called
}

Источник пробуждения ESP32: Сенсорная панель

Вы можете разбудить ESP32 из глубокого сна, используя следующие сенсорные пины.

Сенсорные пины ESP32

Включение пробуждения ESP32 с помощью сенсорного пина очень просто. В Arduino IDE вам достаточно использовать функцию esp_sleep_enable_touchpad_wakeup().

Подключение

Давайте подключим провод к GPIO#15 (Touch#3), который будет выступать в качестве источника пробуждения по касанию. Вы можете прикрепить к сенсорному пину любой проводящий объект — провод, алюминиевую фольгу, проводящую ткань, проводящую краску и т.д. — и превратить его в сенсорную панель.

Подключение провода к ESP32 для источника пробуждения по касанию

Пример кода

Давайте посмотрим, как это работает, используя пример из библиотеки. Откройте вашу Arduino IDE и перейдите в File > Examples > ESP32 > Deep Sleep, затем откройте скетч TouchWakeUp.

Этот скетч демонстрирует самый простой пример глубокого сна с касанием в качестве источника пробуждения и то, как сохранять данные в памяти RTC для использования после перезагрузки.

#define Threshold 40 /* Greater the value, more the sensitivity */

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

/*
Method to print the touchpad by which ESP32
has been awaken from sleep
*/
void print_wakeup_touchpad(){
  touchPin = esp_sleep_get_touchpad_wakeup_status();

  switch(touchPin)
  {
    case 0  : Serial.println("Touch detected on GPIO 4"); break;
    case 1  : Serial.println("Touch detected on GPIO 0"); break;
    case 2  : Serial.println("Touch detected on GPIO 2"); break;
    case 3  : Serial.println("Touch detected on GPIO 15"); break;
    case 4  : Serial.println("Touch detected on GPIO 13"); break;
    case 5  : Serial.println("Touch detected on GPIO 12"); break;
    case 6  : Serial.println("Touch detected on GPIO 14"); break;
    case 7  : Serial.println("Touch detected on GPIO 27"); break;
    case 8  : Serial.println("Touch detected on GPIO 33"); break;
    case 9  : Serial.println("Touch detected on GPIO 32"); break;
    default : Serial.println("Wakeup not by touchpad"); break;
  }
}

void callback(){
  //placeholder callback function
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32 and touchpad too
  print_wakeup_reason();
  print_wakeup_touchpad();

  //Setup interrupt on Touch Pad 3 (GPIO15)
  touchAttachInterrupt(T3, callback, Threshold);

  //Configure Touchpad as wakeup source
  esp_sleep_enable_touchpad_wakeup();

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This will never be reached
}

После загрузки скетча откройте монитор порта и установите скорость 115200 бод.

Теперь, когда вы касаетесь пина, ESP32 выведет количество загрузок, причину пробуждения и какой GPIO был затронут в мониторе порта.

Вывод пробуждения ESP32 из глубокого сна по касанию

Объяснение кода

Первая строка кода устанавливает пороговое значение для сенсорного пина равным 40. Чем выше пороговое значение, тем выше чувствительность. Вы можете изменить это значение в соответствии с вашим проектом.

#define Threshold 40 /* Greater the value, more the sensitivity */

Как указано ранее, вы можете сохранять данные в памяти RTC ESP32 (8 кБ SRAM), которая не стирается при глубоком сне. Однако она стирается при сбросе ESP32.

Чтобы сохранить данные в памяти RTC, вам просто нужно добавить атрибут RTC_DATA_ATTR перед определением переменной. В этом примере переменная bootCount сохраняется в памяти RTC. Она будет подсчитывать, сколько раз ESP32 просыпался из глубокого сна.

RTC_DATA_ATTR int bootCount = 0;

После этого определяется переменная touchPin типа touch_pad_t (тип enum), которая позже поможет нам вывести номер GPIO, по которому ESP32 был разбужен из сна.

touch_pad_t touchPin;

Далее определяется функция print_wakeup_reason(), которая выводит причину, по которой ESP32 был разбужен из глубокого сна.

void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

Также определяется функция print_wakeup_touchpad(), которая выводит номер GPIO, по которому ESP32 был разбужен из глубокого сна.

void print_wakeup_touchpad(){
  touchPin = esp_sleep_get_touchpad_wakeup_status();

  switch(touchPin)
  {
    case 0  : Serial.println("Touch detected on GPIO 4"); break;
    case 1  : Serial.println("Touch detected on GPIO 0"); break;
    case 2  : Serial.println("Touch detected on GPIO 2"); break;
    case 3  : Serial.println("Touch detected on GPIO 15"); break;
    case 4  : Serial.println("Touch detected on GPIO 13"); break;
    case 5  : Serial.println("Touch detected on GPIO 12"); break;
    case 6  : Serial.println("Touch detected on GPIO 14"); break;
    case 7  : Serial.println("Touch detected on GPIO 27"); break;
    case 8  : Serial.println("Touch detected on GPIO 33"); break;
    case 9  : Serial.println("Touch detected on GPIO 32"); break;
    default : Serial.println("Wakeup not by touchpad"); break;
  }
}

Далее определяется функция callback(). Это не что иное, как подпрограмма обработки прерывания (ISR), которая будет вызываться каждый раз при срабатывании прерывания по касанию. Но, к сожалению, эта функция не выполняется, если ESP32 находится в глубоком сне. Поэтому эта функция оставлена пустой.

void callback(){
  //placeholder callback function
}

В функции setup мы сначала инициализируем последовательную связь с ПК.

Serial.begin(115200);

Затем переменная bootCount увеличивается на единицу и выводится в монитор порта, чтобы показать количество раз, которое ESP32 просыпался из глубокого сна.

++bootCount;
Serial.println("Boot number: " + String(bootCount));

Затем вызываются функции print_wakeup_reason() и print_wakeup_touchpad(), но вы можете вызвать любую функцию для выполнения нужной задачи, например, считывание значения датчика.

print_wakeup_reason();
print_wakeup_touchpad();

Теперь нужно привязать прерывание к одному из сенсорных пинов с желаемым порогом чувствительности. Здесь прерывание привязано к сенсорной панели 3 (GPIO15).

touchAttachInterrupt(T3, callback, Threshold);

Далее мы настраиваем источник пробуждения по касанию с помощью функции esp_sleep_enable_touchpad_wakeup().

esp_sleep_enable_touchpad_wakeup();

Наконец, ESP32 переводится в спящий режим вызовом функции esp_deep_sleep_start().

esp_deep_sleep_start();

В этом скетче ESP32 переходит в глубокий сон в самой функции setup(), поэтому он никогда не доходит до функции loop(). Следовательно, функция loop() оставлена пустой.

void loop(){
  //This is not going to be called
}

Источник пробуждения ESP32: Внешнее пробуждение

Существует два вида внешних триггеров для пробуждения ESP32 из глубокого сна.

  • ext0 — используйте, когда хотите разбудить чип только определённым GPIO-пином.

  • ext1 — используйте, когда хотите разбудить чип несколькими GPIO-пинами.

Если вы хотите использовать пин прерывания для пробуждения ESP32 из глубокого сна, вам нужно использовать так называемые RTC_GPIO-пины. Эти GPIO подключены к низковольтной подсистеме RTC, поэтому их можно использовать, когда ESP32 находится в глубоком сне.

Пины RTC_GPIO:

Пины RTC GPIO ESP32

Внешний источник пробуждения ext0

ESP32 можно настроить на пробуждение из глубокого сна, когда один из пинов RTC_GPIO изменяет свой логический уровень.

Функция esp_sleep_enable_ext0_wakeup(GPIO_PIN, LOGIC_LEVEL) используется для включения этого источника пробуждения. Эта функция принимает два параметра. Первый — это номер GPIO-пина, а второй — логический уровень (LOW или HIGH), по которому мы хотим вызвать пробуждение.

Поскольку ext0 использует RTC IO для пробуждения ESP32, периферия RTC остаётся включённой во время глубокого сна.

А поскольку модуль RTC IO включён, вы можете воспользоваться внутренними подтягивающими резисторами (pullup или pulldown). Их необходимо настроить с помощью функций rtc_gpio_pullup_en() и rtc_gpio_pulldown_en() перед вызовом esp_deep_sleep_start().

Подключение

Давайте подключим кнопку к GPIO#33, используя понижающий резистор 10 кОм.

Подключение кнопки к ESP32 для внешнего источника пробуждения ext0

Пример кода

Давайте посмотрим, как это работает, используя пример из библиотеки. Откройте вашу Arduino IDE и перейдите в File > Examples > ESP32 > Deep Sleep, затем откройте скетч ExternalWakeUp.

Этот скетч демонстрирует самый простой пример глубокого сна с ext0 в качестве источника пробуждения.

#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.
  There are two types for ESP32, ext0 and ext1 .
  ext0 uses RTC_IO to wakeup thus requires RTC peripherals
  to be on while ext1 uses RTC Controller so doesnt need
  peripherals to be powered on.
  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low

  //If you were to use ext1, you would use it like
  //esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

После загрузки скетча откройте монитор порта и установите скорость 115200 бод.

Теперь, когда вы нажимаете кнопку, ESP32 выведет количество загрузок и причину пробуждения в монитор порта. Попробуйте нажать несколько раз и наблюдайте, как увеличивается счётчик загрузок с каждым нажатием кнопки. Также обратите внимание, что ext0 использует RTC IO для пробуждения ESP32.

Вывод пробуждения ESP32 из глубокого сна через ext0

Объяснение кода

Первая строка кода задаёт битовую маску. Она не нужна для внешнего пробуждения ext0, поэтому пока можете её проигнорировать. Мы узнаем о ней при объяснении кода внешнего пробуждения ext1.

#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex

Как указано ранее, вы можете сохранять данные в памяти RTC ESP32 (8 кБ SRAM), которая не стирается при глубоком сне. Однако она стирается при сбросе ESP32.

Чтобы сохранить данные в памяти RTC, вам просто нужно добавить атрибут RTC_DATA_ATTR перед определением переменной. В этом примере переменная bootCount сохраняется в памяти RTC. Она будет подсчитывать, сколько раз ESP32 просыпался из глубокого сна.

RTC_DATA_ATTR int bootCount = 0;

Далее определяется функция print_wakeup_reason(), которая выводит причину, по которой ESP32 был разбужен из глубокого сна.

void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

В функции setup мы сначала инициализируем последовательную связь с ПК.

Serial.begin(115200);

Затем переменная bootCount увеличивается на единицу и выводится в монитор порта, чтобы показать количество раз, которое ESP32 просыпался из глубокого сна.

++bootCount;
Serial.println("Boot number: " + String(bootCount));

Затем вызывается функция print_wakeup_reason(), но вы можете вызвать любую функцию для выполнения нужной задачи, например, считывание значения датчика.

print_wakeup_reason();

Теперь настраивается внешний источник пробуждения ext0 с помощью функции esp_sleep_enable_ext0_wakeup(GPIO_PIN, LOGIC_LEVEL). Эта функция принимает два параметра. Первый — это номер GPIO-пина, а второй — логический уровень (LOW или HIGH), по которому мы хотим вызвать пробуждение. В этом примере ESP32 настроен на пробуждение, когда логический уровень GPIO#33 становится HIGH.

esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1);

Наконец, ESP32 переводится в спящий режим вызовом функции esp_deep_sleep_start().

esp_deep_sleep_start();

В этом скетче ESP32 переходит в глубокий сон в самой функции setup(), поэтому он никогда не доходит до функции loop(). Следовательно, функция loop() оставлена пустой.

void loop(){
  //This is not going to be called
}

Внешний источник пробуждения ext1

ESP32 можно настроить на пробуждение из глубокого сна с использованием нескольких пинов. Помните, что эти пины должны быть среди пинов RTC GPIO.

Поскольку источник пробуждения ext1 использует RTC-контроллер, ему не требуется, чтобы периферия RTC и память RTC были включены. В этом случае внутренние подтягивающие резисторы (pullup и pulldown) будут недоступны.

Чтобы использовать внутренние подтягивающие резисторы (pullup или pulldown), нам нужно запросить, чтобы периферия RTC оставалась включённой во время сна, и настроить резисторы pullup/pulldown с помощью функций rtc_gpio_pullup_en() и rtc_gpio_pulldown_en() перед переходом в сон.

Функция esp_sleep_enable_ext1_wakeup(BUTTON_PIN_MASK, LOGIC_LEVEL) используется для включения этого источника пробуждения. Эта функция принимает два параметра. Первый — это битовая маска, которая указывает ESP32, какие пины мы хотим использовать, а второй параметр может быть одним из двух логических уровней, перечисленных ниже, для запуска пробуждения:

  • Пробуждение, если один из выбранных пинов в HIGH (ESP_EXT1_WAKEUP_ANY_HIGH)

  • Пробуждение, если все выбранные пины в LOW (ESP_EXT1_WAKEUP_ALL_LOW)

Битовая маска

Самый простой способ понять битовую маску — записать её в двоичном формате. Вы можете видеть, что нумерация битов основана на обычной нумерации GPIO. Младший значащий бит (LSB) представляет GPIO#0, а старший значащий бит (MSB) представляет GPIO#39.

Представление битовой маски пинов ext1 в двоичном формате
  • 0 — замаскированные пины

  • 1 — пины, которые будут включены как источник пробуждения

Итак, если вы хотите включить GPIO для пробуждения, вам нужно записать 1 в соответствующую позицию и 0 для каждого из оставшихся пинов. И наконец, нужно преобразовать результат в HEX.

Например, если вы хотите использовать GPIO#32 и GPIO#33 в качестве внешних источников пробуждения, битовая маска будет выглядеть так:

Представление битовой маски пинов внешнего источника пробуждения ext1 в двоичном формате

Подключение

Давайте подключим две кнопки к GPIO#33 и GPIO#32, используя понижающие резисторы 10 кОм.

Подключение нескольких кнопок к ESP32 для внешнего источника пробуждения ext1

Пример кода

Давайте посмотрим, как это работает, используя тот же пример ExternalWakeUp из библиотеки. Снова откройте вашу Arduino IDE и перейдите в File > Examples > ESP32 > Deep Sleep, затем откройте скетч ExternalWakeup.

Давайте внесём три изменения в скетч, чтобы он работал для нас:

  1. Изменим константу BUTTON_PIN_BITMASK

  2. Закомментируем код для ext0

  3. Раскомментируем код для ext1

Изменения в скетче выделены зелёным цветом.

#define BUTTON_PIN_BITMASK 0x300000000

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.
  There are two types for ESP32, ext0 and ext1 .
  ext0 uses RTC_IO to wakeup thus requires RTC peripherals
  to be on while ext1 uses RTC Controller so doesnt need
  peripherals to be powered on.
  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
  //esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low

  //If you were to use ext1, you would use it like
  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

После загрузки скетча откройте монитор порта и установите скорость 115200 бод.

Теперь, когда вы нажимаете кнопку, вы получите примерно следующее в мониторе порта. Также обратите внимание, что ext1 использует RTC-контроллер для пробуждения ESP32.

Вывод пробуждения ESP32 из глубокого сна через ext1

Объяснение кода

Этот код идентичен коду ext0, за исключением двух изменений.

В начале кода определяется битовая маска. Поскольку в примере мы используем пины GPIO#32 и GPIO#33, маска содержит единицы в соответствующих позициях, с 32 нулями справа и 6 нулями слева.

00000011 00000000 00000000 00000000 00000000 BIN = 0x300000000 HEX

#define BUTTON_PIN_BITMASK 0x300000000

И наконец, ext1 включается как источник пробуждения.

esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);