ESP32 深度睡眠中從外部喚醒

本文介紹如何將 ESP32 置於深度睡眠模式從通過外部喚醒(例如按下按鈕)將其喚醒。我們將使用 Arduino IDE 對 ESP32 進行編程,並介紹如何使用 ext0 和 ext1 方法。

在本課程中,我們將涵蓋以下主題:

  • 如何將 ESP32 置於深度睡眠模式;
  • 通過使用 ext0 方法更改一個 GPIO(帶有按鈕)的值來喚醒 ESP32;
  • 使用 ext1 方法使用多個 GPIO 喚醒 ESP32;
  • 識別從哪個 GPIO 喚醒。

要了解有關深度睡眠和其他喚醒來源的更多信息,您可以按照以下教程進行操作:

要編寫一個草稿圖以使您的 ESP32 進入深度睡眠模式,然後將其喚醒,您需要記住:

  1. 首先,您需要配置喚醒源。意思是要配置將喚醒 ESP32 的內容。您可以使用一個或組合多個喚醒源。在本課程中,我們將向您展示如何使用外部喚醒源。
  2. 您可以決定在深度睡眠期間關閉或繼續使用哪些外圍設備。但是,預設情況下,ESP32 會自動關閉您定義的喚醒源不需要的外圍設備。
  3. 最後,您使用esp_deep_sleep_start()使您的 ESP32 進入深度睡眠模式的功能。

外部喚醒

外部喚醒是將 ESP32 從深度睡眠中喚醒的方法之一。這意味著您可以通過切換引腳上的信號值來喚醒 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。您可以使用兩種不同的邏輯功能:

  • 如果您選擇的任何引腳為高電平,來喚醒 ESP32;
  • 如果您選擇的所有引腳均為低電平,來喚醒 ESP32。

此喚醒源由 RTC 控制器實現。因此,可以在此模式下關閉 RTC 外設和 RTC 存儲器。

要使用此喚醒源,請使用以下函數:

esp_sleep_enable_ext1_wakeup(bitmask, mode)

該函數接受兩個參數:

  • 將導致喚醒的 GPIO 編號的位置碼;
  • 模式:喚醒 ESP32 的邏輯。有可能:
    • ESP_EXT1_WAKEUP_ALL_LOW:當所有 GPIO 變低時喚醒;
    • ESP_EXT1_WAKEUP_ANY_HIGH:如果任何 GPIO 變高,則喚醒。

  注意:使用此喚醒源,您只能使用 RTC GPIO 引腳。

下圖顯示了 ESP32 引腳排列及其以淺橙色突出顯示的 RTC GPIO(適用於 ESP32 V1 DOIT 板)。

所需零件

要學習本教程,您需要以下部分:

  • ESP32 DOIT DEVKIT V1 Board
  • 2 個按鈕
  • 2x 10k 歐姆電阻
  • 麵包板
  • 跳線

程式碼-外部喚醒

要對 ESP32 進行編程,我們將使用 Arduino IDE。因此,您需要確保已安裝 ESP32 插件。如果您還沒有安裝 ESP32 插件,請按照以下教程安裝:

為了向您展示如何使用外部 (ext0) 喚醒源,我們將探索 ESP32 程式庫附帶的範例。轉到檔案>範ESP32 >DeepSleepExternalWakeUp

/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots

This code is under Public Domain License.

Hardware Connections
======================
Push Button to GPIO 33 pulled down with a 10K Ohm
resistor

NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.

Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#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");
  delay(1000);
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

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

此範例在您觸發時喚醒 ESP32通用輸入輸出接口 33到高電平。程式碼範例顯示如何使用這兩種方法:ext0 和 ext1。如果您按原範例上傳代碼,您將使用 ext0。註釋了使用 ext1 的函數。我們將向您展示這兩種方法的工作原理以及如何使用它們。

讓我們快速看一下代碼。

將數據保存在 RTC 存儲器上

使用 ESP32,您可以將數據保存在 RTC 存儲器中。ESP32 在 RTC 部分有 8kB SRAM,稱為 RTC 快速存儲器。此處保存的數據在深度睡眠期間不會被刪除。但是,當您按下重置按鈕(ESP32 板上標記為 EN 的按鈕)時,它會被刪除。

要將數據保存在 RTC 記憶體中,您只需新增RTC_DATA_ATTR在變量定義之前。該範示例保存引導計數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加1,並在序列埠監控視窗中列印該變量。

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

接下來,您使用print_wakeup_reason()前面定義的函數。

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

在此之後,您需要啟用喚醒源。我們將分別測試每個喚醒源 ext0 和 ext1。

ext0

在本例中,ESP32 在通用輸入輸出接口 33被觸發為高電平:

esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low

代替通用輸入輸出接口 33,您可以使用任何其他 RTC GPIO 引腳。

示意圖

要測試此範例,請按照下面示意圖將按鈕連接到 ESP32。按鈕連接到通用輸入輸出接口 33使用下拉 10K 歐姆電阻。

(此原理圖使用帶有 30 個 GPIO 的 ESP32 DEVKIT V1 模塊版本 – 如果您使用其他型號,請檢查您正在使用的板的引腳排列。)

注意:只有 RTC GPIO 可以用作喚醒源。除了 GPIO 33,您還可以使用任何 RTC GPIO 引腳來連接您的按鈕。

測試範例

讓我們測試這個例子。將範例程式碼上傳到您的 ESP32。確保您選擇了正確的開發和 COM 端口。以 115200 的鮑率打開序列埠監控視窗。

按下按鈕喚醒 ESP32。

多試幾次,看到每次按下按鈕時啟動計數都會增加。

使用此方法可用於使用按鈕喚醒 ESP32,例如執行特定任務。但是,使用這種方法,您只能使用一個 GPIO 作為喚醒源。

如果你想擁有不同的按鈕,它們都喚醒 ESP,但執行不同的任務怎麼辦?為此,您需要使用 ext1 方法。

ext1

ext1 允許您使用不同的按鈕喚醒 ESP,並根據您按下的按鈕執行不同的任務。

而不是使用esp_sleep_enable_ext0_wakeup()功能,您使用esp_sleep_enable_ext1_wakeup()功能。在代碼中,對該函數進行了註釋:

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

取消註釋該函數,以便您擁有:

esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

該函數的第一個參數是您將用作喚醒源的 GPIO 的位置碼,第二個參數定義喚醒 ESP32 的邏輯。

在這個例子中,我們使用變量BUTTON_PIN_BITMASK,這是在程式碼開頭定義的:

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

這只是將一個引腳定義為喚醒源,通用輸入輸出接口 33. 您需要修改位元遮罩以將更多 GPIO 配置為喚醒源。

GPIO 位元遮罩

要獲取 GPIO 位元遮罩,請執行以下步驟:

  1. 計算 2^(GPIO_NUMBER)。以十進位儲存結果;
  2. 將十進位數轉換為十六進位;
  3. 替換您在BUTTON_PIN_BITMASK變數值。

單個 GPIO 的位元遮罩

為了讓您了解如何獲取 GPIO 位元遮罩,我們來看一個範例。在程式庫中的程式碼中,按鈕連接到通用輸入輸出接口 33. 為了得到遮罩通用輸入輸出接口 33

1. 計算 2^33。你應該得到 8589934592

2. 將該數字 (8589934592) 轉換為十六進制。你可以去這個轉換器來做到這一點:

3.將十六進位數複製到BUTTON_PIN_BITMASK變量,你應該得到:

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

多個 GPIO 的位元遮罩

如果你想使用通用輸入輸出接口 2通用輸入輸出接口 15作為喚醒源,您應該執行以下操作:

  1. 計算 2^2 + 2^15。你應該得到 32772
  2. 將該數字轉換為十六進位。你應該得到:8004
  3. BUTTON_PIN_BITMASK如下:

#define BUTTON_PIN_BITMASK 0x8004

識別用作喚醒源的 GPIO

當您使用多個引腳喚醒 ESP32 時,了解哪個引腳導致喚醒很有用。為此,您可以使用以下功能:

esp_sleep_get_ext1_wakeup_status()

此函數返回一個以 2 為底的數字,GPIO 編號為指數:2^(GPIO)。因此,要獲得十進位的 GPIO,您需要進行以下計算:

GPIO = log(RETURNED_VALUE)/log(2)

外部喚醒 – 多個 GPIO

現在,您應該能夠使用不同的按鈕喚醒 ESP32,並確定是哪個按鈕導致了喚醒。在這個例子中,我們將使用通用輸入輸出接口 2通用輸入輸出接口 15作為喚醒源。

示意圖

將兩個按鈕連接到您的 ESP32。在這個例子中,我們使用通用輸入輸出接口 2通用輸入輸出接口 15,但您可以將按鈕連接到任何 RTC GPIO。

編譯多個 GPIO—外部喚醒

您需要對我們之前使用的範例程式碼進行一些修改:

  • 建立要使用的位元遮罩通用輸入輸出接口 15通用輸入輸出接口 2. 我們之前已經向您展示如何執行此操作;
  • 啟用 ext1 作為喚醒源;
  • 使用esp_sleep_get_ext1_wakeup_status()函數來獲取觸發喚醒的 GPIO。

下一個草稿碼實現了所有這些更改。

/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots

This code is under Public Domain License.

Hardware Connections
======================
Push Button to GPIO 33 pulled down with a 10K Ohm
resistor

NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.

Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#define BUTTON_PIN_BITMASK 0x8004 // GPIOs 2 and 15

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;
  }
}

/*
Method to print the GPIO that triggered the wakeup
*/
void print_GPIO_wake_up(){
  uint64_t 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);
}
  
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();

  //Print the GPIO used to wake up
  print_GPIO_wake_up();

  /*
  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_deep_sleep_enable_ext0_wakeup(GPIO_NUM_15,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");
  delay(1000);
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

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

您在程式碼開頭定義了 GPIO 位元遮罩:

#define BUTTON_PIN_BITMASK 0x8004 // GPIOs 2 and 15

您建立一個函數來列印導致喚醒的 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);
}

最後,啟用 ext1 作為喚醒源:

esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

測試草稿碼

有兩個按鈕連接到通用輸入輸出接口 2通用輸入輸出接口 15,您可以上傳提供給您的 ESP32 的程式碼。確保您選擇了正確的開毃板和 COM 端口。

ESP32 現在處於深度睡眠模式。您可以通過按下按鈕將其喚醒。

以 115200 的鮑率打開序列埠監控視窗。按下按鈕喚醒 ESP32。

你應該在序列埠監控視窗上得到類似的東西。

總結

ESP32 可以使用定時器、觸摸引腳或外部喚醒從睡眠中喚醒。在本課程中,我們向您展示如何使用外部喚醒來喚醒 ESP32。意思為您可以在 GPIO 的值更改(從 HIGH 到 LOW,或從 LOW 到 HIGH)時喚醒 ESP32。

要了解有關使用 ESP32 進行深度睡眠的更多信息,請查看我們的完整指南:使用 Arduino IDE 和喚醒源的 ESP32 深度睡眠

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *