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。

IMG20220613202405

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

螢幕擷取畫面 2022-06-13 175123

使用此方法可用於使用按鈕喚醒 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 現在處於深度睡眠模式。您可以通過按下按鈕將其喚醒。

IMG20220613203527-1

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

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

螢幕擷取畫面 2022-06-13 204908

總結

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

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

發佈留言

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