Bật ESP32 Deep Sleep và cách đánh thức bằng yếu tố bên ngoài [Phần 2]

Trong phần trước, mình đã hướng dẫn bạn về ESP32 Deep Sleep và cách đánh thức thông qua chân Touch và bộ hẹn giờ Timer. Trong bài này, chúng ta cùng tìm hiểu về cách đánh thức ESP32 thông qua các yếu tố bên ngoài, chẳng hạn như bằng nút nhấn nhé! Bạn có thể xem lại bài viết trước tại đây: Cách bật chế độ Deep Sleep ESP32 trên Arduino IDE

Mình sẽ sử dụng Arduino IDE để lập trình và sử dụng các phương thức ext1, ext0.

Tổng quan

Để viết code bật chế độ ESP32 Deep Sleep và đánh thức mạch, bạn cần đi theo thuật toán sau:

  1. Xác định cách đánh thức ESP32 khỏi chế độ Deep Sleep, trong bài này thì mình sử dụng các yếu tố bên ngoài như nút nhấn để đánh thức.
  2. Quyết định bật hay tắt các ngoại vi nào trong mạch ESP32.
  3. Sử dụng chức năng esp_deep_sleep_start() để bật chế độ ESP32 Deep Sleep.

Thuật toán đánh thức ESP32 Deep Sleep từ yếu tố bên ngoài

Bạn có thể đánh thức ESP32 khỏi chế độ Deep Sleep bằng cách chuyển đổi giá trị của tín hiệu trên chân, chẳng hạn như nhấn nút trên mạch ESP32.

Bạn có thể lựa chọn 2 yếu tố sau: ext0 và ext1.

Đánh thức bằng ext0

Chúng ta sẽ sử dụng pin để đánh thức ESP32. Với lựa chọn ext0, hệ thống sẽ sử dụng RTC GPIO để đánh thức ESP32. Khi đó, các ngoại vi RTC sẽ được duy trì trong chế độ Deep Sleep. Câu lệnh như sau:

esp_sleep_enable_ext0_wakeup(GPIO_NUM_X, level)

Chúng ta cần điền thông tin chân pin muốn dùng vào câu lệnh này, ở định dạng GPIO_NUM_X, trong đó X là số GPIO của pin đó.

Level trong câu lệnh trên có thể là 1 hoặc 0, đây là các trạng thái của GPIO dùng để đánh thức ESP32 Deep Sleep.

Lưu ý: Với các yếu tố đánh thức từ bên ngoài, bạn chỉ có thể sử dụng các châ GPIO RTC.

Đánh thức bằng ext1

Với cách này, bạn có thể sử dụng nhiều GPIO RTC khác nhau. Về hàm logic, bạn có thể chọn 1 trong 2 cách sau:

  • Đánh thức ESP32 Deep Sleep nếu các chân bạn chọn bị chuyển sang trạng thái HIGH
  • Đánh thức ESP32 Deep Sleep nếu các chân bạn chọn bị chuyển sang trạng thái LOW

Nguồn đánh thức ESP32 này được thực hiện dựa trên bộ điều khiển RTC. Chúng ta sử dụng câu lệnh sau:

esp_sleep_enable_ext1_wakeup(bitmask, mode)

Cụ thể:

  • Bitmask: Một bitmask của các chân GPIO sẽ tạo ra hành động đánh thức ESP32
  • Mode là chế độ logic để đánh thức ESP32, mình đã trình bày sẵn 2 logic phía trên:
    • ESP_EXT1_WAKEUP_ALL_LOW: ESP32 thức dạy khi tất cả các GPIO chuyển sang trạng thái LOW
    • ESP_EXT1_WAKEUP_ANY_HIGH: ESP32 thức dạy khi bất kỳ GPIO nào chuyển sang trạng thái HIGH

Bạn có thể xem sơ đồ chân GPIO RTC trên mạch ESP32 của mình đang dùng để biết chân nào là RTC nhé! Như hình dưới là các chân có màu cam nhạt:

Các chân RTC GPIO dùng để đánh thức ESP32 Deep Sleep

Chuẩn bị phần cứng

  • Mạch ESP32
  • 2 x Nút nhấn
  • 2 x Điện trở 10k Ohm
  • Breadboard
  • Dây Jumper

Chương trình mẫu – Đánh thức ESP32 Deep Sleep bằng 1 chân GPIO

Trước khi nạp code vào ESP32, bạn cần phải cài tiện ích ESP32 trong Arduino IDE trước nhé!

Để đánh thức ESP32 Deep Sleep bằng ext0, bạn click vào File >> Examples >> ESP32 >> Deep Sleep >> ExternalWakeUp và chọn chương trình mẫu có sẵn trong thư viện:

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

Trong chương trình ESP32 Deep Sleep trên, khi kích hoạt chân GPIO 33 thành trạng thái HIGH thì ESP32 sẽ được đánh thức trên. Bạn có thể thấy code đang dùng cả ext0 lẫn ext1.

Giải thích chương trình mẫu

Lưu dữ liệu vào bộ nhớ RTC

Trên ESP32 có sẵn SRAM 8kb, cho phép chúng ta lưu dữ liệu vào bộ nhớ RTC. Các dữ liệu này vẫn sẽ tồn tại, không bị xóa dù đang trong chế độ Deep Sleep. Tuy nhiên, khi bạn nhấn nút RTS (Reset) trên mạch thì chúng sẽ bị xóa.

Câu lệnh để lưu dữ liệu vào bộ nhớ RTC:

RTC_DATA_ATTR int bootCount = 0;

In ra lý do đánh thức ESP32

Dòng code print_wakeup_reason() sẽ in ra màn hình Serial Monitor lý do khiến ESP32 bị đánh thức:

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

Khởi tạo Serial Monitor ở tốc độ 115200:

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

Tăng biến bootCount thêm 1, và in giá trị biến ra Serial Monitor:

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

In lý do đánh thức ESP32 khỏi Deep Sleep:

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

Sau đó, chúng ta kích hoạt các nguồn đánh thức. Mình sẽ lần lượt kiểm tra từng nguồn ext0 và ext1:

ext0

ESP32 được đánh thức khi chân GPIO 33 ở chế độ HIGH:

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

Ngoài chân GPIO 33 thì bạn vẫn có thể sử dụng bất kỳ chân RTC GPIO nào khác để đánh thức ESP32 Deep Sleep nhé!

Sơ đồ kết nối

Để kiểm tra đoạn code, bạn kết nối một nút nhấn vào ESP32 theo sơ đồ bên dưới – Nút nhấn kết nối với GPIO 33:

Sơ đồ kết nối để đánh thức ESP32 Deep Sleep bằng chân GPIO 33
Sơ đồ kết nối để đánh thức ESP32 Deep Sleep bằng chân GPIO 33

Nạp code và kiểm tra

Bạn hãy nạp đoạn code vào ESP32 và mở Serial Monitor ở tốc độ 115200.

Sau đó, bạn nhấn nút để đánh thức ESP32 khỏi Deep Sleep:

Nhấn nút để đánh thức ESP32 Deep Sleep
Nhấn nút để đánh thức ESP32 Deep Sleep

Bạn có thể nhấn nút nhiều lần và quan sát kết quả in ra trên màn hình Serial, biến Boot number sẽ tăng thêm 1 sau mỗi lần bạn nhấn nút:

Biến boot numer tăng thêm 1 sau mỗi lần đánh thức ESP32 Deep Sleep
Biến boot numer tăng thêm 1 sau mỗi lần đánh thức ESP32 Deep Sleep

Phương pháp đánh thức ESP32 này khá hữu ích trong các dự án, khi bạn cần nhấn nút để đánh thức ESP32 làm một nhiệm vụ nào đó.

Tuy nhiên, với ext0 thì chúng ta chỉ có thể sử dụng 1 chân RTC GPIO làm nguồn đánh thức. Giả sử như bạn muốn làm một dự án có nhiều nút nhấn khác nhau, và nhấn nút nào cũng có thể đánh thức ESP32 thì làm sao? Đó là lúc bạn nên dùng đến ext1.

ext1

Ext1 cho phép bạn đánh thức ESP32 Deep Sleep thông qua nhiều nút nhấn khác nhau. Mỗi nút nhấn thì bạn có thể cài đặt một tác vụ riêng cho ESP32 mà bạn muốn.

Lúc này, chúng ta sử dụng hàm Esp_sleep_enable_ext1_wakeup():

esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

Trong ví dụ này, mình sử dụng biến BUTTON_PIN_BITMASK đã được khai báo ở đầu chương trình:

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

Mình chỉ cấu hình một chân GPIO 33 làm nguồn đánh thức. Bạn có thể sửa Bitmask lại để cấu hình nhiều GPIO khác nhau làm nguồn đánh thức ESP32 Deep Sleep nhé!

GPIO Bitmask

Để có bitmask GPIO, bạn làm như sau:

  1. Tính theo công thức 2^(GPIO_NUMBER) và lưu kết quả dưới dạng số thập phân
  2. Truy cập vào trang Web sau để đổi số thập phân thành định dạng hex: rapidtables.com/convert/number/decimal-to-hex.html
  3. Điền số hẽ nhận được vào biến BUTTON_PIN_BITMASK

Bitmask cho 1 GPIO duy nhất

Để hiểu hơn, bạn hãy xem qua ví dụ sau nhé. Trong code có sẵn của thư viện, nút nhấn đang được kết nối với GPIO 33. Chúng ta thực hiện như sau để tính bitmask cho GPIO này:

1. Tính theo công thức: 2^33 = 8589934592

2. Click vào link trên và đổi 8589934592 thành mã hex:

Đổi số thập phân thành hex

3. Copy mã hex vào biến BUTTON_PIN_BITMASK:

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

Bitmask cho nhiều chân GPIO’

Nếu bạn muốn sử dụng chân GPIO 2 và GPIO 15, bạn thực hiện như sau:

  1. Tính tổng 2^2 + 2^15 = 32772
  2. Thay đổi sang hex, chúng ta được 8004
  3. Điền vào code:
#define BUTTON_PIN_BITMASK 0x8004

Xác định chân GPIO làm nguồn đánh thức ESP32 Deep Sleep

Khi sử dụng nhiều chân GPIO để đánh thức ESP32, bạn cần khai báo cho chương trình biết chân nào được dùng làm nguồn đánh thức ESP32, thông qua câu lệnh:

esp_sleep_get_ext1_wakeup_status()

Hàm trên sẽ trả về kết quả là một số lũy thừa của 2, trong đó chân GPIO là số mũ, cụ thể: 2^(GPIO). Do đó, để có được con số GPIO ở dạng số thập phân, bạn cần dùng thêm phép tính:

GPIO = log(RETURNED_VALUE)/log(2)

Thực hành: Viết code đánh thức ESP32 Deep Sleep bằng nhiều chân GPIO

Để đánh thức ESP32 bằng nhiều chân GPIO, bạn cần thay đổi chương trình trên:

  • Tạo một Bitmask để dùng cho GPIO 2 và GPIO 15 (theo hướng dẫn ở mục ngay phía trên mục này)
  • Kích hoạt ext1 làm nguồn đánh thức
  • Sử dụng Esp_sleep_get_ext1_wakeup_status() để chân GPIO kích hoạt và đánh thức ESP32

Chương trình hoàn chỉnh sẽ như sau:

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

Đầu tiên, bạn khai báo GPIO:

#define BUTTON_PIN_BITMASK 0x8004 // GPIOs 2 and 15

Tạo hàm để in ra chân GPIO nào đánh thức ESP32 Deep Sleep:

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

Kích hoạt ext1 làm nguồn đánh thức ESP32 Deep Sleep:

esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

Sau đó, bạn kết nối 2 nút nhấn lần lượt với GPIO 2 và GPIO 15, nạp code vào ESP32 rồi nhấn nút thử nhé:

Cách đánh thức ESP32 Deep Sleep bằng 2 nút nhấn
Cách đánh thức ESP32 Deep Sleep bằng 2 nút nhấn

Bạn có thể mở màn hình Serial ở tốc độ 115200 và quan sát kết quả sau mỗi lần nhấn nút nhé!

Lời kết

Bạn có thể lựa chọn đánh thức ESP32 Deep Sleep bằng nhiều cách khác nhau: Sử dụng bộ hẹn giờ Timer, chân cảm ứng Touch hoặc đánh thức bằng các yếu tố bên ngoài như nút nhấn. Mình đã hướng dẫn bạn chi tiết tất cả các cách này (Với 2 cách đầu thì bạn xem lại bài viết trước nhé!).

Một số dự án ESP32 khác mình đã hướng dẫn, bạn có thể xem qua nếu thích:

IoTZone – Chuyên cung cấp thiết bị điện tử & tài liệu cho Makers

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *