Năng lượng luôn là một vấn đề quan trọng trong các thiết bị IOT, vì vậy chúng ta cần học cách sử dụng ESP32 Sleep mode giúp đưa ESP32 vào chế độ ngủ và đánh thức phù hợp.
Trong bài này, hãy cùng tìm hiểu các vấn đề về năng lượng của ESP32 và cách lập trình nó nhé
Bài 9 trong Serie Học ESP32 từ A tới Z
Sleep Mode là gì?
Trên thực tế, tất cả các dòng vi điều khiển đều có chế độ ngủ (sleep mode) thế nhưng mỗi dòng sẽ có hiệu suất khác nhau. Với những ứng dụng sử dụng điện trực tiếp từ điện lưới quốc gia, chúng ta không cần quan tâm tới vấn đề năng lượng.
Thế nhưng những ứng dụng đó thì luôn phải đặt một chỗ, không thể di chuyển. Mà các thiết bị IOT hiện nay số lượng sản phẩm gắn trên người hay động vật… rất nhiều, thế nên phải sử dụng pin cho các ứng dụng đó.
Vậy nên Sleep Mode ra đời để đưa MCU về chế độ tiết kiệm năng lượng, bằng cách tắt bớt các phần chưa cần hoạt động đi. Và đặt những sự kiện có thể đánh thức (Wake Up) MCU dậy
ESP32 Sleep Mode
Trong ESP32 khi vào chế độ Sleep mode, các phần không cần thiết sẽ ngừng hoạt động. Với từng chế độ ngủ khác nhau, chúng ta sẽ tắt lần lượt các khối khác nhau. Ngoài ra ESP32 còn có một bộ xử lý tiết kiệm năng lượng ULP Processor (Ultra-Low Power) độc lập với Core chính, và có thể truy cập được một số ngoại vi nhất định. Giúp khả năng xử lý của ESP32 trong khi ngủ tốt hơn.
Sơ đồ khối của ESP32 và cách ESP32 tiêu thụ năng lượng
Hãy cùng xem cấu trúc của ESP32
Đây là các nguồn tiêu thụ năng lượng của ESP32, thực chất khi vào Sleep mode, ESP32 sẽ turn off một số phần, đẫn tới năng lượng tiêu thụ giảm. Các phần đó bao gồm:
- Bluetooth + Wifi + Radio: Đây là phần tiêu thụ điện năng lớn nhất trên ESP32
- ESP32 Core+Memory: Lõi chính và bộ nhớ
- ULP Coprocessor: Bộ xử lý tiết kiệm năng lượng
- RTC: Khối thời gian thực
- Peripherals: Các ngoại vi
Các chế độ tiết kiệm năng lượng của ESP32 Sleep Mode
Trong ESP32 có 5 chế độ tiết kiệm năng lượng đó là:
- Active mode
- Modem Sleep mode
- Light Sleep mode
- Deep Sleep mode
- Hibernation mode
Với 5 chế độ này các ngoại vi sẽ được bật/tắt theo bảng sau:
Để tra cứu năng lượng tiêu thụ của các khối, chúng ta có thể tham khảo bảng sau:
Chi tiết các bạn xem thêm tại: ESP32 Espressif datasheet
Để hiểu chi tiết hơn về các chế độ tiết kiệm năng lượng của ESP32 chúng ta sẽ đi vào từng chế độ
ESP32 Active Mode
Trong chế độ này, tất cả các khối chức năng của chip đều được bật, và đây là chế độ bình thường khi chạy các chương trình
Vì Active Mode bật mọi chức năng (đặc biệt là module WiFi, Bluetooth và nhân xử lý ), nên chip yêu cầu dòng điện hơn 240mA để hoạt động. Ngoài ra, nếu bạn sử dụng cả hai chức năng WiFi và Bluetooth cùng lúc thì năng lượng tiêu thụ sẽ lớn hơn rất nhiều (lớn nhất là 790mA).
Lưu ý: Vì thế nên khi sử dụng các KIT esp32 chúng ta cần cấp nguồn ngoài cho KIT nếu không wifi hoặc BT/BLE sẽ không hoạt động nếu phải điều khiển thêm nhiều các phần tử khác trong mạch.
ESP32 Modem Sleep
Trong Modem Sleep, mọi thứ đều hoạt động, chỉ có WiFi, Bluetooth và Radio bị tắt. Để duy trì sự hoạt động của Wifi và BT/BLE, chúng phải được đánh thức định kì theo một thời gian mà lập trình viên mong muốn.
Chế độ này chỉ hoạt động trong chế độ máy trạm (Wifi Station), không được sử dụng trong chế độ điểm truy cập (Access Point). Khi ở chế độ này ESP32 vẫn kết nối với bộ định tuyến thông qua cơ chế DTIM Beacon.
Để tiết kiệm năng lượng, ESP32 vô hiệu hóa module Wi-Fi giữa hai khoảng thời gian DTIM Beacon và tự động thức dậy trước khi đến Beacon tiếp theo. Thời gian wake up có thể từ 100 – 1000ms.
Với chế độ này ESP32 tiêu thụ từ: 3mA – 20mA
ESP32 Light Sleep
Với chế độ này ESP32 tắt các thành phần phát sóng như wifi, BT/BLE như chế độ modem sleep, cùng với tăt nguồn xung đồng hồ (Clock) của CPU và RAM và ngoại vi. Nhưng RTC và ULP – coprocessor vẫn hoạt động bình thường.
Kĩ thuật này được gọi là clock-gating.
Clock gating là một kỹ thuật để giảm tiêu thụ năng lượng. Nó vô hiệu hóa các phần của mạch điện bằng cách tắt các xung clock, để các flip-flop trong chúng không phải chuyển trạng thái. Vì khi có chuyển đổi trạng thái thì năng lượng bị tiêu thụ, ngược lại, mức tiêu thụ năng lượng bằng 0.
Trong chế độ này ESP32 tiêu thụ khoảng 0.8mA
ESP32 Deep Sleep
Ở chế độ Deep Sleep, CPU, RAM và tất cả các ngoại vi đều bị tắt. Các bộ phận duy nhất của chip vẫn được cấp nguồn là: bộ RTC, ngoại vi RTC (bao gồm bộ ULP) và bộ nhớ RTC.
CPU chính bị tắt nguồn còn bộ ULP thực hiện các phép đo cảm biến và đánh thức hệ thống chính dựa trên dữ liệu đo được.
Cùng với CPU, bộ nhớ chính của chip cũng bị tắt. Vì vậy, mọi thứ được lưu trữ trong bộ nhớ đó bị xóa sạch và không thể truy cập được.
Tuy nhiên, bộ nhớ RTC vẫn được bật. Vì vậy, nội dung của nó được bảo quản trong Deep Sleep và có thể được lấy ra sau khi chip được đánh thức. Đó là lý do mà chip lưu trữ dữ liệu kết nối Wi-Fi và Bluetooth trong bộ nhớ RTC trước đó.
Khi Wake up khỏi Deep Sleep, ESP32 sẽ hoạt động lại từ đầu, tương tự như việc reset vậy.
Trong chế độ này ESP32 tiêu thụ từ 10µA đến 0.15mA
ESP32 Hibernation mode
Trong chế độ Hibernation Mode, chip vô hiệu hóa bộ tạo dao động 8MHz bên trong và bộ ULP. Bộ nhớ phục hồi RTC cũng bị tắt nguồn, có nghĩa là không có cách nào chúng ta có thể bảo quản dữ liệu trong chế độ ngủ đông.
Mọi thứ khác đều bị tắt ngoại trừ bộ đếm thời gian RTC slow clock và một số GPIO RTC đang hoạt động. Chúng có trách nhiệm đánh thức chip ra khỏi Hibernation Mode.
Vậy nên hãy lưu ý khi sử dụng chế độ này nhé.
Trong chế độ Hibernation Mode, chip chỉ tiêu thụ khoảng 2.5µA.
Các cơ chế đánh thức (Wake Up) ESP32 khỏi chế độ Deep Sleep
Trong bài này, chúng ta sẽ dề cập tới chế độ Deep Sleep và cách đánh thức nó.
Để có thể đánh thức được ESP32 từ trạng thái ngủ về hoạt động bình thường, chúng ta có các nguồn sau
- Timer: Định thời gian cho ESP32 thức dậy theo chu kì nhất định
- Touch pad: Sử dụng các chân cảm ứng-
- External wake up: Sử dụng 2 nguồn bên ngoài để đánh thức ESP32 đó là (ext0 & ext1)
- ULP Processor: Sử dụng bộ xử lý tiết kiệm năng lượng để đánh thức ( Sẽ không được đề cập trong bài này)
Tùy vào nhu cầu cụ thể, chúng ta sẽ lựa chọn các phương án đánh thức cho phù hợp với dự án của mình.
Lập trình ESP32 Timer Wake Up
Với chế độ đánh thức bằng timer chúng ta sẽ sử dụng hàm esp_sleep_enable_timer_wakeup(time_in_us)
. Khi sử dụng hàm này, mỗi khi esp32 rơi vào trạng thai ngủ, chúng sẽ được tự động đánh thức sau khoảng thời gian time_in_us
Để vào chế độ ngủ chúng ta sử dụng câu lệnh esp_deep_sleep_start();
Code và giải thích code
Copy code vào platform IO
#include <Arduino.h> #define uS_TO_S_FACTOR 1000000 // biến chuyển từ micro giây sang giây #define TIME_TO_SLEEP 5 //Thời gian thức dậy RTC_DATA_ATTR int bootCount = 0; // biến này lưu tại bộ nhớ RTC int bootCountInRam = 0; // biến này lưu tại RAM //in ra nguồn đá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; } } void setup(){ Serial.begin(115200); delay(1000); //chờ 1 khoảng nhỏ cho serial hoạt động //mỗi lần thức dậy sẽ tăng biến này 1 lần và in ra ++bootCount; ++bootCountInRam; Serial.println("Boot number: " + String(bootCount)); Serial.println("Boot in ram number: " + String(bootCountInRam)); //in ra nguồn đánh thức esp32 print_wakeup_reason(); //gọi hàm thức dậy mỗi 5s 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"); Serial.println("Going to sleep now"); delay(1000); Serial.flush(); // băt đầu vào chế độ ngủ esp_deep_sleep_start(); } void loop(){ //This is not going to be called }
Nạp và kết quả
Cứ sau 5s chúng ta sẽ thấy ESP32 thức dậy 1 lần, biến bootCount
được lưu trong bộ nhớ RTC thế nên không bị reset lại giá trị
Biến bootCountInRam sẽ bị xóa giá trị liên tục khi ESP32 thức dậy.
Lập trình ESP32 Touch Wake Up
Sơ đồ nguyên lý
Code và giải thích code
Full code
#include <Arduino.h> #define Threshold 40 //biến giới hạn (độ nhạy) cho touch pin // khai báo 1 biến kiểu touch_pad_t touch_pad_t touchPin; RTC_DATA_ATTR int bootCount = 0; // biến này lưu tại bộ nhớ RTC //in ra nguồn đá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; } } //in ra chân wake up 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(){ //Hàm được gọi mỗi khi có touch pin dc kích hoạt } void setup(){ Serial.begin(115200); delay(1000); //chờ 1 khoảng nhỏ cho serial hoạt động //mỗi lần thức dậy sẽ tăng biến này 1 lần và in ra ++bootCount; Serial.println("Boot number: " + String(bootCount)); //in ra nguồn đánh thức esp32 print_wakeup_reason(); //in ra chân touch đánh thức ESP32 print_wakeup_touchpad(); //Cài đặt ngắt cho chân Touch Pad 3 (GPIO15) touchAttachInterrupt(T3, callback, Threshold); //Cấu hình chê độ thức dây là touchpad esp_sleep_enable_touchpad_wakeup(); Serial.println("Going to sleep now"); delay(1000); Serial.flush(); // băt đầu vào chế độ ngủ esp_deep_sleep_start(); } void loop(){ //This is not going to be called }
Chúng ta sẽ sử dụng hàm esp_sleep_enable_touchpad_wakeup();
để cấu hình thức dậy cho ESP32 bằng Touch.
Sau khi cấu hình xong esp32 sẽ được đưa về trạng thái ngủ bằng hàm esp_deep_sleep_start();
Mỗi khi chạm vào chân Touch, ESP32 sẽ wake up và in ra kiểu wake up và chân wake up.
Lập trình ESP32 External Wake Up
Các chân External Wake up phải là các chân của bộ RTC (Bởi vì chỉ bộ này hoạt động trong chế độ Deep Sleep). Chúng ta cần treo trở bên ngoài cho nút nhấn, bởi vì khi vào Sleep tất cả các trở treo bằng lệnh PinMode hay các lệnh liên quan tới IO đều không hoạt động.
Nếu không treo trở cho chân IO, chân đó sẽ có mức điện áp là Floating (trôi nổi) dẫn tới kết quả sẽ bị sai.
Sơ đồ nguyên lý
Code và giải thích code
#include <Arduino.h> #define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex RTC_DATA_ATTR int bootCount = 0; // biến này lưu tại bộ nhớ RTC //in ra nguồn đá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; } } void setup(){ Serial.begin(115200); delay(1000); //chờ 1 khoảng nhỏ cho serial hoạt động //mỗi lần thức dậy sẽ tăng biến này 1 lần và in ra ++bootCount; Serial.println("Boot number: " + String(bootCount)); //in ra nguồn đánh thức esp32 print_wakeup_reason(); //cấu hình chế độ thức dậy là ext0 //chế độ này chỉ bật được khi bộ RTC còn hoạt động esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low //cấu hình chế độ thức dậy là ext1 //chế độ này có thể bật ngay cả khi RTC không còn hoạt động //esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH); Serial.println("Going to sleep now"); delay(1000); Serial.flush(); // băt đầu vào chế độ ngủ esp_deep_sleep_start(); } void loop(){ //This is not going to be called }
Với thức dậy bằng Ext0 chúng ta sử dụng hàm esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1);
với 2 tham số GPIO_NUM_33
là chân GPIO sử dụng trong ngắt và 1 là mức sảy ra ngắt (khi IO đó có điện áp là Vcc).
Với thức dậy bằng Ext1 chúng ta sử dụng hàm esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
trong đó:
BUTTON_PIN_BITMASK
được định nghĩa bằng lệnh#define BUTTON_PIN_BITMASK 0x200000000
(bit thứ 32 là 1 khi chuyển đổi qua BIN)ESP_EXT1_WAKEUP_ANY_HIGH
là kiểu thức dậy khi bất kì chân nào được có giá trị là 1.
Kết
Nắm vững được các chế độ Sleep Mode của ESP32 thì mới có thể làm được các ứng dụng sử dụng Pin, accquy… Đó là tiêu chuẩn bắt buộc cho các thiết bị di động. Thực ra ESP32 Sleep Mode không khó, bạn hoàn toàn có thể làm chủ được.
Nếu thấy bài này có ích hãy chia sẻ cho cộng đồng nhé. Đừng quên ra nhập Hội Anh Em Nghiện Lập trình để kết nối với những người bạn cùng đam mê.