深度睡眠的實驗

 深度睡眠的實驗

為什麼要讓ESP32進入深度睡眠模式?

其實我也是最近剛學到這一塊,所以就順手紀錄下來。
記得以前做過一些要電池的成品,發現放一天半左右就
沒電了,後來網路上搜尋相關知識才知道Arduino和esp32
它們都有使其進入深度睡眠的方法。
在深度睡眠中,可以使機板降低功耗,CPU和WIFI都會
停止運作,只有ULP還會以最低功耗運作,雖然 ESP32 
處於深度睡眠模式,但 RTC 記憶體也仍然處於打開狀態,
因此我們可以為 ULP 共同處理器編寫一個程式,並將其
存儲在 RTC 記憶體中以訪問週邊設備、內部定時器和內
部感測器。

RTC引腳

下圖黃色區塊的都可以使用RTC

喚醒模式

主要有種喚醒模式

  1. 定時器喚醒
  2. 觸摸喚醒
  3. 外部喚醒/多種不同的外部喚醒
  4. ULP喚醒

1.定時器喚醒模式實驗

ESP32可進入深睡模式,並預設時間喚醒

#define us_to_s 1000000
#define time__to_sleep 5

RTC_DATA_ATTR int count = 0;

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);
  ++count;
  Serial.println("boot num: " + String(count));
  print_wakeup_reason();
  esp_sleep_enable_timer_wakeup(us_to_s * time__to_sleep);
  delay(1000);
  esp_deep_sleep_start();
}

void loop()
{
}

一開始設定微秒轉成秒,也可以以直接寫成TIME_TO_SLEEP  5000000
#define us_TO_s 1000000
#define TIME_TO_SLEEP  5   
這一段是說要在RTC上保存喚醒的次數,可以在最前面加上RTC_DATA_ATTR
*但是在按下EN鍵重啓這個數據會流失掉
RTC_DATA_ATTR int count = 0;
這個函式主要是設定"被換醒的原因"

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

每次被喚醒,count就會加1,並被印出來
++count; Serial.println("Boot number: " + String(bootCount));

調用剛最前方那個被喚醒的原因函式
print_wakeup_reason()

啓用幾秒後,調用睡眠模式的函式
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start()

實驗結果

紅色字體是指進入深睡模式
土黃色字體是指被喚醒的次數
深藍色字體是被喚醒的原因
rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5828
entry 0x400806a8
boot num: 2
Wakeup caused by timer
ets Jun  8 2016 00:22:57

rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5828
entry 0x400806a8
boot num: 3
Wakeup caused by timer
ets Jun  8 2016 00:22:57

rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5828
entry 0x400806a8
boot num: 4
Wakeup caused by timer
ets Jun  8 2016 00:22:57

2.觸摸喚醒實驗



後來我發現來在Arduino IDE裡切換到ESP32的模式下,範例裡就有DEEP SLEEP的範例可用
將範例載入後,上傳。


#define Threshold 40 

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;

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 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(){
  
}

void setup(){
  Serial.begin(115200);
  delay(1000); 

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

  
  print_wakeup_reason();
  print_wakeup_touchpad();

  
  touchAttachInterrupt(T4callbackThreshold);

  
  esp_sleep_enable_touchpad_wakeup();

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

void loop(){
  
}

程式碼裡面這個是給予觸摸觸發的值
#define Threshold 40

當在觸摸引腳上讀取的值低於 Threshold 變量上設置的值時,ESP32 喚醒並執行回調函數。
Callback() 函數只有在 ESP32 處於喚醒狀態時才會執行。
所以如果是在喚醒的狀態下壓著觸摸腳不放開,並不會執行callback()回調。
如果是在睡眠狀態下碰了一下引腳馬上放開,這樣也不會執行callback()回調。
touchAttachInterrupt(T3, callback, Threshold);

callback() 函數是空的
void callback(){ //placeholder callback function }

接下來,使用 esp_sleep_enable_touchpad_wakeup() 函數將觸摸引腳設置為喚醒源。
esp_sleep_enable_touchpad_wakeup()

3.外部喚醒 (ext0)


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

Esp_sleep_enable_ext0_wakeup(GPIO_NUM_X,level)
此函數接受您要使用的引腳作為第一個參數,格式為 GPIO_NUM_X,其中 X 表示該引腳的 GPIO 編號。

第二個參數 level 可以是 1 或 0。這表示將觸發喚醒的 GPIO 狀態。

注意:使用此喚醒,只能使用 RTC 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 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
}

 

在這個例子中,我們使用了在代碼開頭定義的變量 BUTTON_PIN_BITMASK
 #define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
按鈕連接到 GPIO 33。要獲取 GPIO 33十六進位碼

計算 2的33次方,應該得到 8589934592,將該數字 (8589934592) 轉換為十六進制。您可以轉到此轉換器來執行此操作。

esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
#可以使用任何其他 RTC GPIO 引腳來代替 GPIO 33


4.外部喚醒 (ext1)

在這裡不使用esp_sleep_enable_ext0_wakeup(),而是使用
esp_sleep_enable_ext1_wakeup()


/*
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
}


0x8004怎麼來的?

計算 2^2 + 2^15。你應該得到 32772
將該數字轉換為十六進制。你應該得到:8004
在這個例子中,我們使用了在代碼開頭定義的變量 BUTTON_PIN_BITMASK

#define BUTTON_PIN_BITMASK 0x8004

當您使用多個引腳來喚醒 ESP32 時,知道是哪個引腳引起了喚醒是很有用的。為此,您可以使用以下功能:

esp_sleep_get_ext1_wakeup_status()

該函數返回一個基數2,與GPIO號碼作為指數:2 ^(GPIO_NUMBER)。因此,在小數得到GPIO,你需要做以下計算:

GPIO = log(GPIO_NUMBER)/log(2)

其它程式碼如同前面試驗



將程式碼上傳後可以看到按壓哪個GPIO會顯示在監測窗口












留言

這個網誌中的熱門文章

ESP32-CAM燒錄出現fd_forward.h: No such file or directory錯誤信息<解決方案>