ESP32 глубокий сон (Deep Sleep) с Arduino IDE и источники пробуждения
Эта статья представляет собой полное руководство по режиму глубокого сна (Deep Sleep) ESP32 с Arduino IDE. Мы покажем, как перевести ESP32 в режим глубокого сна и рассмотрим различные способы его пробуждения: пробуждение по таймеру, пробуждение по касанию и внешнее пробуждение. В руководстве приведены практические примеры с кодом, объяснением кода и схемами подключения.
Обновлено 8 октября 2024 года.
Связанный материал: ESP8266 Deep Sleep с Arduino IDE
Эта статья разделена на 4 части:
Знакомство с режимом глубокого сна
ESP32 может переключаться между различными режимами питания:
Активный режим (Active mode)
Режим сна модема (Modem Sleep mode)
Режим глубокого сна (Deep Sleep mode)
Режим гибернации (Hibernation mode)
Вы можете сравнить пять различных режимов в следующей таблице из технического описания ESP32 от Espressif.
Техническое описание ESP32 от Espressif также содержит таблицу сравнения энергопотребления различных режимов питания.
А вот также Таблица 10 для сравнения энергопотребления в активном режиме:
Зачем нужен режим глубокого сна?
Работа ESP32 в активном режиме от батарей не является оптимальным решением, так как заряд батарей будет расходоваться очень быстро.
Если перевести ESP32 в режим глубокого сна, энергопотребление снизится, и батареи прослужат дольше.
Перевод ESP32 в режим глубокого сна означает отключение действий, потребляющих наибольшее количество энергии, при сохранении минимальной активности, достаточной для пробуждения процессора при наступлении интересующего события.
В режиме глубокого сна ни процессор, ни Wi-Fi не работают, но сопроцессор сверхнизкого энергопотребления (ULP) может оставаться включенным.
Пока ESP32 находится в режиме глубокого сна, память RTC также остается под питанием, поэтому мы можем написать программу для сопроцессора ULP и сохранить ее в памяти RTC для доступа к периферийным устройствам, внутренним таймерам и внутренним датчикам.
Этот режим работы полезен, когда необходимо пробудить основной процессор по внешнему событию, таймеру или обоим условиям, сохраняя при этом минимальное энергопотребление.
Выводы RTC_GPIO
Во время глубокого сна некоторые выводы ESP32 могут использоваться сопроцессором ULP, а именно выводы RTC_GPIO и сенсорные выводы (Touch Pins). Техническое описание ESP32 содержит таблицу с идентификацией выводов RTC_GPIO. Вы можете найти эту таблицу здесь на странице 14.
Вы можете использовать эту таблицу в качестве справки или взглянуть на следующую распиновку, чтобы найти различные выводы RTC_GPIO. Выводы RTC_GPIO выделены оранжевой прямоугольной рамкой.
Рекомендуем также прочитать: Справочник по распиновке ESP32: Какие GPIO выводы следует использовать?
Источники пробуждения
После перевода ESP32 в режим глубокого сна существует несколько способов его пробуждения:
Можно использовать таймер, пробуждая ESP32 через заданные промежутки времени;
Можно использовать сенсорные выводы;
Можно использовать два варианта внешнего пробуждения: одно внешнее пробуждение или несколько различных источников внешнего пробуждения;
Можно использовать сопроцессор ULP для пробуждения – этот вариант не рассматривается в данном руководстве.
Написание скетча для глубокого сна
Чтобы написать скетч для перевода ESP32 в режим глубокого сна и последующего пробуждения, необходимо учитывать следующее:
Сначала нужно настроить источники пробуждения. Это означает определение того, что будет пробуждать ESP32. Можно использовать один или комбинировать несколько источников пробуждения.
Можно решить, какие периферийные устройства отключить или оставить включенными во время глубокого сна. Однако по умолчанию ESP32 автоматически отключает периферийные устройства, которые не нужны для выбранного источника пробуждения.
Наконец, используется функция esp_deep_sleep_start() для перевода ESP32 в режим глубокого сна.
Пробуждение по таймеру
ESP32 может перейти в режим глубокого сна, а затем пробуждаться через заданные промежутки времени. Эта функция особенно полезна при работе с проектами, требующими отметок времени или ежедневных задач, при сохранении низкого энергопотребления.
Контроллер RTC ESP32 имеет встроенный таймер, который можно использовать для пробуждения ESP32 через заданный промежуток времени.
Включение пробуждения по таймеру
Включение пробуждения ESP32 через заданный промежуток времени очень просто. В Arduino IDE достаточно указать время сна в микросекундах в следующей функции:
esp_sleep_enable_timer_wakeup(time_in_us)
Код
Для программирования ESP32 мы будем использовать Arduino IDE. Поэтому убедитесь, что у вас установлено ядро ESP32 для Arduino. Следуйте инструкции по установке дополнения ESP32, если вы еще этого не сделали:
Давайте посмотрим, как это работает, используя пример из библиотеки. Откройте Arduino IDE и перейдите в File > Examples > ESP32 Deep Sleep, откройте скетч TimerWakeUp.
/* Simple Deep Sleep with Timer Wake Up
ESP32 offers a deep sleep mode for effective power saving as power is an important factor for IoT applications. In this mode CPUs, most of the RAM, and all the digital peripherals which are clocked
from APB_CLK are powered off. The only parts of the chip which can still be powered on are: RTC controller, RTC peripherals ,and RTC memories This code displays the most basic deep sleep with a timer to wake it up and how to store data in RTC memory to use it over reboots This code is under Public Domain License.
Author: Pranav Cherukupalli <cherukupallip@gmail.com> */
#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");
delay(1000);
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
// This is not going to be called
}
Давайте рассмотрим этот код. Первый комментарий описывает, что отключается во время глубокого сна с пробуждением по таймеру.
In this mode CPUs, most of the RAM,
and all the digital peripherals which are clocked
from APB_CLK are powered off. The only parts of
the chip which can still be powered on are:
RTC controller, RTC peripherals ,and RTC memories
При использовании пробуждения по таймеру включенными остаются контроллер RTC, периферийные устройства RTC и память RTC.
Определение времени сна
Первые две строки кода определяют период времени, в течение которого ESP32 будет находиться в режиме сна.
#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) */
В этом примере используется коэффициент преобразования из микросекунд в секунды, чтобы можно было задать время сна в переменной TIME_TO_SLEEP в секундах. В данном случае пример переведет ESP32 в режим глубокого сна на 5 секунд.
Сохранение данных в памяти RTC
На ESP32 можно сохранять данные в памяти RTC. ESP32 имеет 8 КБ SRAM в части RTC, называемой быстрой памятью RTC (RTC fast memory). Данные, сохраненные здесь, не стираются во время глубокого сна. Однако они стираются при нажатии кнопки сброса (кнопка с надписью EN на плате ESP32).
Чтобы сохранить данные в памяти RTC, достаточно добавить RTC_DATA_ATTR перед определением переменной. В примере переменная bootCount сохраняется в памяти RTC. Эта переменная считает, сколько раз ESP32 пробуждался из глубокого сна.
RTC_DATA_ATTR int bootCount = 0;
Причина пробуждения
Затем в коде определяется функция print_wakeup_reason(), которая выводит источник, вызвавший пробуждение из глубокого сна.
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()
В функции setup() следует размещать ваш код. Все инструкции нужно записать до вызова функции esp_deep_sleep_start().
Этот пример начинается с инициализации последовательной связи на скорости 115200 бод.
Serial.begin(115200);
Затем переменная bootCount увеличивается на единицу при каждой перезагрузке, и это число выводится в монитор последовательного порта.
++bootCount;
Serial.println("Boot number: " + String(bootCount));
Затем код вызывает функцию print_wakeup_reason(), но вы можете вызвать любую функцию для выполнения нужной задачи. Например, вы можете захотеть пробуждать ESP32 раз в день для считывания значения с датчика.
Далее код определяет источник пробуждения с помощью следующей функции:
esp_sleep_enable_timer_wakeup(time_in_us)
Эта функция принимает в качестве аргумента время сна в микросекундах, как было показано ранее. В нашем случае имеем:
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Затем, после выполнения всех задач, ESP32 переходит в режим сна вызовом следующей функции:
esp_deep_sleep_start()
Как только вызывается функция esp_deep_sleep_start(), ESP32 переходит в режим сна и не выполняет код, написанный после этой функции. При пробуждении из глубокого сна код выполняется с самого начала.
Функция loop()
Функция loop() пуста, потому что ESP32 уснет до достижения этой части кода. Поэтому все задачи нужно писать в setup() перед вызовом функции esp_deep_sleep_start().
Тестирование пробуждения по таймеру
Загрузите пример скетча в ESP32. Убедитесь, что выбрана правильная плата и COM-порт. Откройте монитор последовательного порта на скорости 115200 бод.
Каждые 5 секунд ESP32 просыпается, выводит сообщение в монитор последовательного порта и снова переходит в глубокий сон.
При каждом пробуждении ESP32 переменная bootCount увеличивается. Также выводится причина пробуждения, как показано на рисунке ниже.
Однако обратите внимание, что если нажать кнопку EN на плате ESP32, счетчик загрузок сбросится до 1.
Вы можете изменить предоставленный пример, и вместо вывода сообщения заставить ESP32 выполнять любую другую задачу. Пробуждение по таймеру полезно для выполнения периодических задач с ESP32, например, ежедневных задач, без значительного расхода энергии.
Пробуждение по касанию
Вы можете пробудить ESP32 из глубокого сна с помощью сенсорных выводов. В этом разделе показано, как это сделать с помощью Arduino IDE.
Включение пробуждения по касанию
Включение пробуждения ESP32 по касанию очень просто. В Arduino IDE нужно использовать следующую функцию, передав в качестве аргумента сенсорный вывод и порог срабатывания:
touchSleepWakeUpEnable(TOUCH_PIN, THRESHOLD);
Код
Давайте посмотрим, как это работает, используя пример из библиотеки. Откройте Arduino IDE и перейдите в File > Examples > ESP32 Deep Sleep, откройте скетч TouchWakeUp.
/*
Deep Sleep with Touch Wake Up
=====================================
This code displays how to use deep sleep with
a touch as a wake up source and how to store data in
RTC memory to use it over reboots
ESP32 can have multiple touch pads enabled as wakeup source
ESP32-S2 and ESP32-S3 supports only 1 touch pad as wakeup source enabled
This code is under Public Domain License.
Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/
#if CONFIG_IDF_TARGET_ESP32
#define THRESHOLD 40 /* Greater the value, more the sensitivity */
#else //ESP32-S2 and ESP32-S3 + default for other chips (to be adjusted) */
#define THRESHOLD 5000 /* Lower the value, more the sensitivity */
#endif
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 = (touch_pad_t) esp_sleep_get_touchpad_wakeup_status();
#if CONFIG_IDF_TARGET_ESP32
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;
}
#else
if (touchPin < TOUCH_PAD_MAX) {
Serial.printf("Touch detected on GPIO %d\n", touchPin);
} else {
Serial.println("Wakeup not by touchpad");
}
#endif
}
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();
#if CONFIG_IDF_TARGET_ESP32
//Setup sleep wakeup on Touch Pad 3 + 7 (GPIO15 + GPIO 27)
touchSleepWakeUpEnable(T3, THRESHOLD);
touchSleepWakeUpEnable(T7, THRESHOLD);
#else //ESP32-S2 + ESP32-S3
//Setup sleep wakeup on Touch Pad 3 (GPIO3)
touchSleepWakeUpEnable(T3, THRESHOLD);
#endif
//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
}
Установка порога срабатывания
Первое, что нужно сделать – установить пороговое значение для сенсорных выводов.
#define THRESHOLD 40 // Greater the value, more the sensitivity
Значения, считываемые сенсорным выводом, уменьшаются при касании. Пороговое значение означает, что ESP32 проснется, когда значение, считанное на сенсорном выводе, станет ниже 40. Вы можете настроить это значение в зависимости от желаемой чувствительности.
Важно: если вы используете модель ESP32-S2 или ESP32-S3, все работает немного иначе. Именно поэтому в коде есть отдельный раздел, определяющий другой порог для этих плат. В этом случае, чем ниже значение, тем выше чувствительность.
#if CONFIG_IDF_TARGET_ESP32
#define THRESHOLD 40 // Greater the value, more the sensitivity
#else // ESP32-S2 and ESP32-S3 + default for other chips (to be adjusted)
#define THRESHOLD 5000 // Lower the value, more the sensitivity
#endif
Установка сенсорных выводов как источника пробуждения
Чтобы установить сенсорный вывод как источник пробуждения, можно использовать функцию touchSleepWakeUpEnable(), которая принимает в качестве аргументов сенсорный вывод и пороговое значение, при котором плата пробудится.
В этом примере GPIO 15 (T3) и GPIO 27 (T7) устанавливаются как источники пробуждения с одинаковым пороговым значением.
#if CONFIG_IDF_TARGET_ESP32
// Setup sleep wakeup on Touch Pad 3 + 7 (GPIO15 + GPIO 27)
touchSleepWakeUpEnable(T3, THRESHOLD);
touchSleepWakeUpEnable(T7, THRESHOLD);
Если вы используете модель ESP32-S2 или S3, пример определяет пробуждение только по GPIO 3 (T3). Обратите внимание, что нумерация сенсорных выводов может отличаться в зависимости от используемой модели платы.
#else // ESP32-S2 + ESP32-S3
// Setup sleep wakeup on Touch Pad 3 (GPIO3)
touchSleepWakeUpEnable(T3, THRESHOLD);
Наконец, вызовите esp_deep_sleep_start() для перевода ESP32 в режим глубокого сна.
esp_deep_sleep_start();
Это были общие инструкции по настройке сенсорных выводов как источника пробуждения. Теперь рассмотрим остальные разделы кода.
setup()
Когда ESP32 просыпается, он выполняет код сначала до момента обнаружения функции esp_deep_sleep_start().
В этом конкретном примере есть управляющая переменная bootCount, которая увеличивается между каждым циклом сна, чтобы мы знали, сколько раз ESP32 просыпался.
// Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
Мы также вызываем функции print_wakeup_reason() и print_wakeup_touchpad(), определенные ранее в коде, для вывода причины пробуждения и вывода, вызвавшего пробуждение.
// Print the wakeup reason for ESP32 and touchpad too
print_wakeup_reason();
print_wakeup_touchpad();
Подключение схемы
Для тестирования этого примера подключите провод к GPIO 15, как показано на схеме ниже.
Если вы используете модель ESP32-S2 или ESP32-S3, пожалуйста, проверьте расположение ваших сенсорных выводов.
Тестирование примера
Загрузите код в ESP32 и откройте монитор последовательного порта на скорости 115200 бод.
ESP32 переходит в режим глубокого сна.
Вы можете разбудить его, прикоснувшись к проводу, подключенному к Touch Pin 3.
Когда вы касаетесь вывода, ESP32 отображает в мониторе последовательного порта: номер загрузки, причину пробуждения и какой сенсорный GPIO вызвал пробуждение.
Внешнее пробуждение
Помимо таймера и сенсорных выводов, мы также можем пробудить ESP32 из глубокого сна, изменив значение сигнала на выводе, например, нажатием кнопки. Это называется внешним пробуждением. Существует два варианта внешнего пробуждения: ext0 и ext1.
Внешнее пробуждение (ext0)
Этот источник пробуждения позволяет использовать один вывод для пробуждения ESP32.
Вариант ext0 использует RTC GPIO для пробуждения. Поэтому периферийные устройства RTC будут оставаться включенными во время глубокого сна, если запрошен этот источник пробуждения.
Для использования этого источника пробуждения применяется следующая функция:
esp_sleep_enable_ext0_wakeup(GPIO_NUM_X, level)
Эта функция принимает в качестве первого аргумента вывод, который вы хотите использовать, в формате GPIO_NUM_X, где X представляет номер GPIO этого вывода.
Второй аргумент, level, может быть 1 или 0. Он определяет состояние GPIO, которое вызовет пробуждение.
Примечание: с этим источником пробуждения можно использовать только выводы, являющиеся RTC GPIO.
Внешнее пробуждение (ext1)
Этот источник пробуждения позволяет использовать несколько RTC GPIO. Этот источник пробуждения реализован контроллером RTC. Поэтому периферийные устройства RTC и память RTC могут быть отключены в этом режиме.
Для использования этого источника пробуждения применяется следующая функция:
esp_sleep_enable_ext1_wakeup_io(bitmask, mode);
Эта функция принимает два аргумента:
Битовую маску номеров GPIO, которые будут вызывать пробуждение;
Mode: логику пробуждения ESP32. Варианты:
ESP_EXT1_WAKEUP_ALL_LOW: пробуждение, когда все GPIO переходят в состояние LOW;
ESP_EXT1_WAKEUP_ANY_HIGH: пробуждение, если любой из GPIO переходит в состояние HIGH.
Если вы используете ESP32-S2, ESP32-S3, ESP32-C6 или ESP32-H2, доступны следующие режимы:
ESP_EXT1_WAKEUP_ANY_LOW: пробуждение, когда любой из выбранных GPIO находится в состоянии LOW
ESP_EXT1_WAKEUP_ANY_HIGH: пробуждение, когда любой из выбранных GPIO находится в состоянии HIGH
Примечание: можно использовать только выводы, являющиеся RTC GPIO.
Все подробности об источнике пробуждения ext1 в режиме глубокого сна смотрите в документации Espressif по глубокому сну.
Код
Рассмотрим пример, который поставляется с библиотекой ESP32. Перейдите в File > Examples > ESP32 Deep Sleep > ExternalWakeUp:
Этот пример пробуждает ESP32 при подаче высокого уровня на GPIO 33. Пример кода показывает, как использовать оба метода: ext0 и ext1. Если загрузить код как есть, будет использоваться ext0. Функция для ext1 закомментирована. Мы покажем, как работают оба метода и как их использовать.
Давайте кратко рассмотрим код.
Сохранение данных в памяти RTC
На ESP32 можно сохранять данные в памяти RTC. ESP32 имеет 8 КБ SRAM в части RTC, называемой быстрой памятью RTC (RTC fast memory). Данные, сохраненные здесь, не стираются во время глубокого сна. Однако они стираются при нажатии кнопки сброса (кнопка с надписью EN на плате 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()
В функции setup() сначала инициализируется последовательная связь:
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
Затем переменная bootCount увеличивается на единицу, и её значение выводится в монитор последовательного порта.
++bootCount;
Serial.println("Boot number: " + String(bootCount));
Далее выводится причина пробуждения с помощью ранее определенной функции print_wakeup_reason().
//Print the wakeup reason for ESP32
print_wakeup_reason();
После этого необходимо включить источники пробуждения. Мы протестируем каждый из источников пробуждения, ext0 и ext1, отдельно.
ext0
В этом примере ESP32 просыпается при подаче высокого уровня на GPIO 33:
esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1); // 1 = High, 0 = Low
Вместо GPIO 33 можно использовать любой другой вывод RTC GPIO. Просто определите его в переменной WAKEUP_GPIO в начале кода.
#define WAKEUP_GPIO GPIO_NUM_33 // Only RTC IO are allowed
Внутренние подтягивающие резисторы
Мы также вызываем следующие две строки:
rtc_gpio_pullup_dis(WAKEUP_GPIO);
rtc_gpio_pulldown_en(WAKEUP_GPIO);
Функция rtc_gpio_pullup_dis() отключает внутренний подтягивающий резистор к питанию (pull-up) на GPIO пробуждения – она гарантирует, что вывод не будет непреднамеренно удерживаться в состоянии HIGH. Это важно, потому что подтягивающий резистор к питанию удерживал бы вывод в высоком состоянии, что потенциально могло бы вызвать непреднамеренные пробуждения.
Функция rtc_gpio_pulldown_en() включает внутренний подтягивающий резистор к земле (pull-down) на GPIO пробуждения – она гарантирует, что вывод удерживается в состоянии LOW до получения действительного сигнала пробуждения (HIGH). Настроив вывод с подтягивающим резистором к земле, мы гарантируем, что он остается в стабильном низком состоянии во время глубокого сна. Эта стабильность обеспечивает пробуждение ESP32 только при получении внешнего высокого сигнала на указанный GPIO, соответствующего условию пробуждения, установленному esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1).
Подключение схемы
Для тестирования этого примера подключите кнопку к ESP32 по следующей схеме. Кнопка подключена к GPIO 33 с использованием подтягивающего резистора к земле 10 кОм.
Примечание: в качестве источника пробуждения можно использовать только RTC GPIO. Вместо GPIO 33 можно также использовать любые выводы RTC GPIO для подключения кнопки.
Тестирование примера
Давайте протестируем этот пример. Загрузите код в ESP32. Убедитесь, что выбрана правильная плата и COM-порт. Откройте монитор последовательного порта на скорости 115200 бод.
Нажмите кнопку для пробуждения ESP32.
Попробуйте несколько раз и наблюдайте, как счетчик загрузок увеличивается при каждом нажатии кнопки.
Использование этого метода полезно для пробуждения ESP32 с помощью кнопки, например, для выполнения определенной задачи. Однако с этим методом можно использовать только один GPIO в качестве источника пробуждения.
А что если вы хотите иметь несколько кнопок, каждая из которых пробуждает ESP, но выполняет разные задачи?
ext1
Источник пробуждения ext1 позволяет пробуждать ESP32 с помощью разных GPIO и выполнять разные задачи в зависимости от GPIO, вызвавшего пробуждение.
Вместо функции esp_sleep_enable_ext0_wakeup() используется функция esp_sleep_enable_ext1_wakeup_io().
esp_sleep_enable_ext1_wakeup_io(BUTTON_PIN_BITMASK(WAKEUP_GPIO), ESP_EXT1_WAKEUP_ANY_HIGH);
В данном примере для выполнения этой части кода убедитесь, что переменная USE_EXT0_WAKEUP установлена в 0 в начале кода:
#define USE_EXT0_WAKEUP 0 // 1 = EXT0 wakeup, 0 = EXT1 wakeup
Функция esp_sleep_enable_ext1_wakeup_io()
Первый аргумент функции esp_sleep_enable_ext1_wakeup_io() – битовая маска GPIO, которые вы будете использовать как источник пробуждения, а второй аргумент определяет логику пробуждения ESP32.
Битовая маска GPIO
Для определения битовой маски GPIO можно использовать макрос BUTTON_PIN_BITMASK(), определенный в начале кода.
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex
BUTTON_PIN_BITMASK(GPIO) – это макрос, который создает битовую маску для конкретного GPIO. Это не функция, но он ведет себя подобным образом, принимая аргумент и возвращая значение.
Чтобы получить битовую маску для конкретного GPIO, достаточно вызвать его так:
BUTTON_PIN_BITMASK(WAKEUP_GPIO)
Таким образом, esp_sleep_enable_ext1_wakeup_io() будет выглядеть так:
esp_sleep_enable_ext1_wakeup_io(BUTTON_PIN_BITMASK(WAKEUP_GPIO), ESP_EXT1_WAKEUP_ANY_HIGH);
Битовая маска для нескольких GPIO
Чтобы создать битовую маску для нескольких GPIO с помощью макроса BUTTON_PIN_BITMASK(GPIO), можно использовать побитовое ИЛИ (|) для объединения отдельных битовых масок для каждого GPIO. Вот как это можно сделать:
Допустим, вы хотите создать битовую маску для GPIO 2 и GPIO 15. Это можно сделать, объединив отдельные битовые маски для каждого вывода:
uint64_t bitmask = BUTTON_PIN_BITMASK(GPIO_NUM_2) | BUTTON_PIN_BITMASK(GPIO_NUM_15);
Далее мы рассмотрим пример использования нескольких GPIO в качестве источника пробуждения.
Режим пробуждения
Второй аргумент функции esp_sleep_enable_ext1_wakeup_io() – режим пробуждения. Как мы видели ранее, доступны следующие варианты:
ESP_EXT1_WAKEUP_ALL_LOW: пробуждение, когда все GPIO переходят в состояние LOW;
ESP_EXT1_WAKEUP_ANY_HIGH: пробуждение, когда любой из GPIO переходит в состояние HIGH.
Если вы используете ESP32-S2, ESP32-S3, ESP32-C6 или ESP32-H2, доступны следующие режимы:
ESP_EXT1_WAKEUP_ANY_LOW: пробуждение, когда любой из выбранных GPIO находится в состоянии LOW
ESP_EXT1_WAKEUP_ANY_HIGH: пробуждение, когда любой из выбранных GPIO находится в состоянии HIGH
В нашем конкретном примере мы используем ESP_EXT1_WAKEUP_ANY_HIGH. Таким образом, ESP32 проснется, когда любой из GPIO из битовой маски перейдет в состояние HIGH.
Как и в случае с ext0, мы отключаем внутренние подтягивающие резисторы к питанию и включаем подтягивающие резисторы к земле для обеспечения стабильности считывания GPIO пробуждения.
Внешнее пробуждение – несколько GPIO
Теперь вы сможете пробуждать ESP32 с помощью разных кнопок и определять, какая кнопка вызвала пробуждение. В этом примере мы будем использовать GPIO 2 и GPIO 15 в качестве источников пробуждения.
Схема
Подключите две кнопки к ESP32. В этом примере мы используем GPIO 2 и GPIO 15, но вы можете подключить кнопки к любым RTC GPIO.
Код для нескольких GPIO – внешнее пробуждение
Необходимо внести некоторые изменения в ранее использованный пример кода:
создать битовую маску для GPIO 15 и GPIO 2. Мы уже показывали, как это сделать;
включить ext1 как источник пробуждения;
создать функцию, которая определяет GPIO, вызвавший пробуждение.
Следующий скетч содержит все эти изменения.
Как работает код?
Давайте кратко рассмотрим, как работает код.
Битовая маска GPIO
Определите битовую маску GPIO в начале кода:
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex
#define WAKEUP_GPIO_2 GPIO_NUM_2 // Only RTC IO are allowed -
#define WAKEUP_GPIO_15 GPIO_NUM_15 // Only RTC IO are allowed
// Define bitmask for multiple GPIOs
uint64_t bitmask = BUTTON_PIN_BITMASK(WAKEUP_GPIO_2) | BUTTON_PIN_BITMASK(WAKEUP_GPIO_15);
Определение GPIO, вызвавшего пробуждение
Мы создаем функцию print_GPIO_wake_up(), которая выводит GPIO, вызвавший пробуждение:
void print_GPIO_wake_up(){
int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
Serial.print("GPIO that triggered the wake up: GPIO ");
Serial.println((log(GPIO_reason))/log(2), 0);
}
Функция esp_sleep_get_ext1_wakeup_status() возвращает битовую маску с GPIO, вызвавшим пробуждение. Получить номер GPIO можно следующим образом:
log(GPIO_reason))/log(2)
Вывод причины пробуждения
Мы изменили функцию print_wakeup_reason(), чтобы она выводила GPIO, вызвавший пробуждение, когда источником пробуждения является ext1:
case ESP_SLEEP_WAKEUP_EXT1:
Serial.println("Wakeup caused by external signal using RTC_CNTL");
print_GPIO_wake_up();
break;
Включение ext1 как источника пробуждения
Включите ext1 как источник пробуждения, передав битовую маску GPIO и режим пробуждения.
esp_sleep_enable_ext1_wakeup_io(bitmask, ESP_EXT1_WAKEUP_ANY_HIGH);
Не забудьте отключить внутренние подтягивающие резисторы к питанию и включить подтягивающие резисторы к земле на GPIO, используемых как источник пробуждения.
rtc_gpio_pulldown_en(WAKEUP_GPIO_2);
rtc_gpio_pullup_dis(WAKEUP_GPIO_2);
rtc_gpio_pulldown_en(WAKEUP_GPIO_15);
rtc_gpio_pullup_dis(WAKEUP_GPIO_15);
Включение глубокого сна
Наконец, вызовите esp_deep_sleep_start() для перевода ESP32 в режим глубокого сна.
esp_deep_sleep_start();
Тестирование скетча
После загрузки кода на плату ESP32 перейдет в режим глубокого сна. Вы можете пробудить его нажатием кнопок.
Откройте монитор последовательного порта на скорости 115200 бод. Нажимайте кнопки для пробуждения ESP32. В мониторе последовательного порта должна отображаться причина пробуждения и GPIO, вызвавший пробуждение.
Заключение
В этой статье мы показали, как использовать режим глубокого сна ESP32 и различные способы его пробуждения. Вы можете пробудить ESP32 с помощью таймера, сенсорных выводов или изменения состояния GPIO.
Давайте подведем итоги по каждому источнику пробуждения:
Пробуждение по таймеру
Для включения пробуждения по таймеру используется функция esp_sleep_enable_timer_wakeup(time_in_us);
Используйте функцию esp_deep_sleep_start() для запуска глубокого сна.
Пробуждение по касанию
Для включения сенсорных выводов как источника пробуждения используйте функцию touchSleepWakeUpEnable();
Наконец, используйте функцию esp_deep_sleep_start() для перевода ESP32 в режим глубокого сна.
Внешнее пробуждение
В качестве внешнего источника пробуждения можно использовать только RTC GPIO;
Можно использовать два различных метода: ext0 и ext1;
ext0 позволяет пробуждать ESP32 с помощью одного GPIO;
ext1 позволяет пробуждать ESP32 с помощью нескольких GPIO.
Надеемся, это руководство оказалось для вас полезным. Если вы хотите узнать больше об ESP32, обязательно ознакомьтесь со всеми нашими проектами ESP32 и электронной книгой по ESP32.
Источник: ESP32 Deep Sleep with Arduino IDE and Wake Up Sources