مقدمه
همانطور که میدانید، امروزه برد ESP32 به دلیل قابلیتهای شگفتانگیزی که ارائه میدهد، بسیار مورد توجه و استقبال قرار گرفته است. توانایی اضافه کردن ویژگیهایی همچون بلوتوث و وایفای به پروژهها، این برد را به گزینهای عالی برای انجام انواع پروژههای مربوط به اینترنت اشیا (Internet of Things یا به اختصار IoT) تبدیل کرده است. البته واضح است که قابلیتهای جذاب ESP32 در نهایت هزینهای همراه خود دارد، و آن هزینه در واقع مصرف انرژی بالاست. سازندگان ESP32 برای حل این مشکل، راهحلی در نظر گرفتهاند و آن توانایی قرار دادن ESP32 در حالتهای گوناگون Sleep است. در این آموزش ما به کاملترین حالت Sleep در برد ESP32 یعنی حالت Deep Sleep میپردازیم.
آنچه در این آموزش یاد می گیرید
- آشنایی با قسمتهای مختلف مصرف کننده انرژی در برد ESP32
- آشنایی با حالتهای گوناگون Sleep در برد ESP32
- آشنایی کامل با حالت Deep Sleep در برد ESP32 و انواع حالات آن
حالتهای مختلف Sleep در برد ESP32
اگر در پروژهتان، قصد دارید از یک آداپتور دیواری یا چیزی شبیه به آن برای تامین انرژی استفاده کنید، دیگر نگران مصرف انرژی نخواهید بود. اما اگر قصد استفاده از یک باتری را دارید، حتما میزان مصرف انرژی پروژهتان بسیار مهم خواهد بود. ESP32 به دلیل دارا بودن وایفای و بلوتوث، میتواند حداکثر تا 260mA جریان بکشد و به همین علت باتری شما را به سرعت تمام خواهد کرد! همانطور که در مقدمه گفته شد طراحان ESP32 برای حل این مساله راهحلی در نظر گرفتهاند و آن استفاده از ESP32 در حالتهای گوناگون Sleep است.
در حالت کلی میتوان ESP32 را به 7 قسمت مختلف از نظر مصرف انرژی تقسیم کرد. این 7 قسمت به شرح زیر است.
- وایفای
- بلوتوث
- رادیو
- هسته اصلی ESP32
- پردازنده جانبی کم مصرف (ULP Coprocessor)
- قطعات جانبی (Peripherals)
- زمانسنج (RTC)
در حالت عادی و فعال بودن کامل برد ESP32، تمامی قسمتهای بالا مشغول به کار هستند. اما برای صرفهجویی در انرژی میتوان با قرار دادن برد ESP32 در حالتهای مختلف Sleep، قسمتهای گوناگون را غیرفعال کرده و مصرف انرژی را بهینه کرد. در زیر میتوانید حالتهای گوناگونی را که برد ESP32 میتواند در آنها قرار بگیرد، مشاهده کنید.
- حالت فعال
- حالت Modem Sleep
- حالت Light Sleep
- حالت Deep Sleep
- حالت Hibernation
در حالت فعال همانطور که گفته شد تمامی قسمتهای ESP32 فعال بوده و مصرف انرژی در حالت حداکثر است. در حالت Hibernation، بر عکس حالت فعال، تمامی قسمتهای ESP32 از کار افتاده و تنها در صورت ریست کردن، برد به حالت عادی برمیگردد. برای صرفهجویی در مصرف انرژی میتوان از قرار دادن برد ESP32 در حالتهای مختلف Sleep استفاده کرد که بهینهترین آنها حالت Deep Sleep یا همان خواب عمیق است که در این آموزش به آن پرداخته میشود.
حالت Deep Sleep در ESP32 و انواع آن
همانطور که پیشتر اشاره شد، برای صرفهجویی در مصرف انرژی، میتوان با قرار دادن ESP32 در حالتهای Sleep، قسمتهای مختلف آن را غیرفعال کرد. بهینهترین حالت Sleep در ESP32 حالت Deep Sleep است. در این حالت تنها 2 قسمت از 7 قسمت اصلی ESP32 فعال مانده و مابقی قسمتها غیرفعال میشوند.
این 2 قسمت عبارتند از:
- پردازنده جانبی کممصرف (ULP Coprocessor)
- و زمانسنج (RTC)
مسالهای که ممکن است تا کنون نیز به آن فکر کرده باشید این است که بعد از قرار گرفتن ESP32 در حالت خواب عمیق یا همان Deep Sleep چه اتفاقی میافتد؟ چگونه در صورت نیاز آن را بیدار کنیم؟ برای بیدار کردن ESP32 از Deep Sleep روشهای گوناگونی توسط طراحان آن در نظر گرفته شده است. این روشها در یک نگاه اجمالی به شرح زیر است:
- استفاده از Timer و بیدار کردن ESP32 از خواب در دورههای زمانی خاص (مثلا هر 2 ساعت، هر یک روز، …)
- استفاده از پایههای حسگر لمسی (Touch Pins)
- استفاده از روش External Wake-up
- استفاده از پردازنده جانبی کممصرف (ULP Coprocessor)
روش آخر در این آموزش بحث نمیشود. در ادامه، 3 روش ابتدایی به صورت کامل مورد بررسی قرار میگیرند.
تجهیزات مورد نیاز
قطعات مورد نیاز
نرم افزارهای مورد نیاز
بردی که در اینجا استفاده کردهایم، برد NodeMCU – ESP32S Edition است. اما در صورتی که از بردهای مبتنی بر ESP32 دیگر نیز استفاده میکنید، همچنان میتوانید بر اساس همین آموزش و کدهای موجود در آن عمل کنید.
بردن ESP32 به Deep Sleep و بیدار کردن آن با روش Timer
سادهترین روش برای بیدار کردن ESP32 از حالت خواب عمیق، استفاده از Timer است. در این روش، ابتدا تنظیم میکنیم که چه مدت میخواهیم ESP32 در خواب بماند و سپس آن را به خواب میبریم. در ادامه، قسمتهای پردازنده جانبی و زمانسنج که همچنان فعالاند، در موقع مقرر، ESP32 را از خواب بیدار میکنند.
کد
برای بردن ESP32 به حالت خواب عمیق و بیدار کردن آن با Timer، کد زیر را بر روی ESP32 خود آپلود کنید.
/*
Modified on Jul 12, 2021
Modified by MehranMaleki from Arduino Examples
Home
*/
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */
RTC_DATA_ATTR int bootCount = 0;
//Function for printing the reason by which ESP32 has been awakened 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(500);
//Increment boot number and print it every reboot
bootCount++;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/*
1st Step: we should configure the wake up source:
We set our ESP32 to use "Timer" to wake up every 5 seconds
*/
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");
/*
2nd Step: we can decide what peripherals to shut down or keep on.
By default, ESP32 will automatically power down all peripherals
not needed by the wakeup source. But if you want to configure peripherals on your own, read the details in the API docs:
http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
The comment line below is an example of how to configure peripherals. It turns off all RTC peripherals in deep sleep mode. You can leave that comment.
*/
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
/*
3rd Step: Now that we have setup a wake cause, and if needed, setup the peripherals state in deep sleep mode, we can now start going to deep sleep.
If you have provided no wake up sources but the deep
sleep has been started, it will sleep forever unless hardware
reset occurs.
*/
Serial.println("Going to sleep now");
delay(500);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This will not be used in Deep Sleep mode.
}
با پروگرم کردن برد ESP32 با کد بالا، این برد به حالت Deep Sleep رفته و هر 5 ثانیه یک بار توسط Timer بیدار شده و دوباره به خواب میرود. خروجی برنامه در Serial Monitor به شکل زیر است.
در ادامه، قسمتهای مختلف کد را توضیح میدهیم.
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */
در ابتدای برنامه، دو عدد تعریف شدهاند. مدت زمان در خواب ماندن ESP32 باید بر حسب میکروثانیه برای آن تنظیم شود. از آنجایی که در این آموزش میخواهیم بر اساس ثانیه کار کنیم، برای راحتتر شدن برنامهنویسی، این دو عدد را تعریف کردهایم. برای تغییر مدت زمان در خواب عمیق بودن ESP32، تنها کافی است تغییر مورد نظر را در این قسمت اعمال کنید.
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR
استفاده کنیم.
//Function for printing the reason by which ESP32 has been awakened 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;
}
}
یک تابع تعریف میکنیم که با استفاده از آن، بتوانیم بعد از هر بار بیدار شدن ESP32، علت بیدار شدن را در Serial Monitor نمایش دهیم. همانطور که در کد مشاهده کنید، این دلایل میتواند هر کدام از حالتهای EXT0 ،EXT1 ،TIMER ،TOUCHPAD و یا ULP باشد. این تابع را در مراحل بعد نیز استفاده خواهیم نمود.
void setup(){
Serial.begin(115200);
delay(500);
//Increment boot number and print it every reboot
bootCount++;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
پس از تعریف تابع در مرحله قبل، وارد قسمت setup برنامه خواهیم شد. در حالت Deep Sleep تنها قسمت setup را خواهیم داشت و تمامی کد ما در این قسمت قرار خواهد گرفت. در واقع در پایان setup، برنامه به حالت Deep Sleep میرود و در نتیجه هیچوقت وارد قسمت loop نخواهیم شد. در قسمت setup ابتدا تنظیمات لازم برای نمایش در Serial Monitor را انجام میدهیم. هر بار رسیدن برنامه به ابتدای setup به معنای بیدار شدن ESP32 از حالت Deep Sleep است، در نتیجه متغیر تعریف شده در حافظه زمانسنج را یکی افزایش داده و نمایش میدهیم. در ادامه علت بیدار شدن ESP32 از خواب را در Serial Monitor نمایش میدهیم.
در ادامه، قسمتهای اصلی کد که برای انجام تنظیمات برای بردن ESP32 به Deep Sleep است را مشاهده میکنیم. برای توضیح کامل هر کدام از این قسمتها، یک کامنت نیز قبل از هر مرحله نوشته شده است که آن را به طور کامل توضیح میدهد.
/*
1st Step: we should configure the wake up source:
We set our ESP32 to use "Timer" to wake up every 5 seconds
*/
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");
برای بردن ESP32 به خواب عمیق، در ابتدا لازم است که روش بیدار شدن ESP32 را تنظیم نماییم. در این قسمت از آموزش، قصد داریم که با روش Timer این کار را انجام دهیم. برای تنظیم Timer به عنوان روش بیدار شدن ESP32 از Deep Sleep میتوانیم از تابع esp_sleep_enable_timer_wakeup(time in us) استفاده کنیم. این تابع زمان را بر حسب میکروثانیه میگیرد و روش Timer را به عنوان منبع بیدار کردن ESP32 از Deep Sleep قرار میدهد.
/*
2nd Step: we can decide what peripherals to shut down or keep on.
By default, ESP32 will automatically power down all peripherals
not needed by the wakeup source. But if you want to configure peripherals on your own, read the details in the API docs:
http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
The comment line below is an example of how to configure peripherals. It
turns off all RTC peripherals in deep sleep mode. You can leave that comment.
*/
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
در این مرحله میتوانیم، قسمتهایی از ESP32 را که در حالت Deep Sleep غیرفعال میشوند تنظیم کنیم. ESP32 به صورت پیشفرض، تمامی قسمتهایی را که برای بیدار شدن به روشی که در مرحله قبل استفاده کردهایم مشکلی به وجود نمیآورد غیرفعال میکند. در نتیجه میتوان از این مرحله گذر کرد.
/*
3rd Step: Now that we have set up a wake cause, and if needed, setup the
peripherals state in deep sleep mode, we can now start going to
deep sleep.
If you have provided no wake up sources but the deep
sleep has been started, it will sleep forever unless hardware
reset occurs.
*/
Serial.println("Going to sleep now");
delay(500);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
در این مرحله که مرحله نهاییست، پس از تنظیمات انجام شده در مراحل قبل، یعنی تنظیم روش بیدار شدن و تنظیم قسمتهایی که در خواب عمیق غیرفعال میشوند، میتوانیم برد ESP32 را در حالت Deep Sleep قرار دهیم. برای این کار میتوانیم از دستور esp_deep_sleep_start() استفاده کنیم. با این خط از دستور، برد ESP32 به خواب عمیق میرود و تا مدت زمان تنظیم شده در خواب میماند. پس از بیدار شدن ESP32 نیز قسمت setup از ابتدا اجرا میشود. در نتیجه همانطور که پیشتر نیز گفته شد، کد هیچگاه به قسمتهای بعد از خط دستور esp_deep_sleep_start() نمیرسد.
در این قسمت، به طور کامل به نحوه بردن ESP32 به حالت Deep Sleep و بیدار کردن آن به کمک Timer پرداختیم. در ادامه، روشهای دیگر را بررسی میکنیم.
بردن ESP32 به Deep Sleep و بیدار کردن آن با روش پایه های حسگر لمسی (Touch)
همانطور که میدانید، برد ESP32 دارای پایههایی است که میتوانند به عنوان یک حسگر لمسی (Touch) عمل کنند. این پایهها در میکروکنترلر ESP-WROOM-32 که میکروکنترلر اصلی در اکثر بردهای ESP32 است، عبارتند از:
- T0 (GPIO 4)
- T1 (GPIO 0)
- T2 (GPIO 2)
- T3 (GPIO 15)
- T4 (GPIO 13)
- T5 (GPIO 12)
- T6 (GPIO 14)
- T7 (GPIO 27)
- T8 (GPIO 33)
- T9 (GPIO 32)
برای اطلاع کامل از انواع پایههای موجود در ESP32 میتوانید به لینک زیر مراجعه کنید.
آموزش پایههای میکروکنترلر ESP32
علاوه بر کاربرد این پایهها به عنوان سنسور لمسی، میتوان از این پایهها برای بیدار کردن برد ESP32 از خواب عمیق هم استفاده کرد. در این حالت تنظیم میکنیم که با لمس کدام یک از پایههای لمسی، میکروکنترلر ESP32 از حالت Deep Sleep بیدار شود. در این روش، برد به خواب عمیق رفته و قسمتهای پردازنده جانبی کممصرف و زمانسنج که هنوز فعالاند به بررسی پایههای لمسی پرداخته و با لمس پایه مورد نظر، برد ESP32 از حالت Deep Sleep بیدار میشود.
کد
برای بردن ESP32 به حالت خواب عمیق و بیدار کردن آن با پایههای لمسی، کد زیر را بر روی ESP32 خود آپلود کنید.
/*
Modified on Jul 12, 2021
Modified by MehranMaleki from Arduino Examples
Home
*/
#define Threshold 40 /* Greater the value, more the sensitivity */
RTC_DATA_ATTR int bootCount = 0;
//Function for printing the reason by which ESP32 has been awakened 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;
}
}
//Function for printing the touchpad by which ESP32 has been awakened from sleep
void print_wakeup_touchpad(){
touch_pad_t touchPin;
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(){
//write instructions you want to be performed when the ESP32 wakes up
}
void setup(){
Serial.begin(115200);
delay(500);
//Increment boot number and print it every reboot
bootCount++;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32 and aslo the touchpad
print_wakeup_reason();
print_wakeup_touchpad();
/*
1st Step: we should configure the wake up source:
We set our ESP32 to use "TouchPad 3 (GPIO15)" to wake up
To do that, we first need to setup interrupt on one of
the touch pins -in our case "Touch Pad 3"- and then,
configure "Touchpad" as the wakeup source.
*/
//Setup interrupt on Touch Pad 3 (GPIO15)
touchAttachInterrupt(T3, callback, Threshold);
//Configure Touchpad as wakeup source
esp_sleep_enable_touchpad_wakeup();
/*
2nd Step: we can decide what peripherals to shut down or keep on.
By default, ESP32 will automatically power down all peripherals
not needed by the wakeup source. But if you want to configure peripherals
on your own, read the details in the API docs:
http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
The comment line below is an example of how to configure peripherals. It
turns off all RTC peripherals in deep sleep mode. You can leave that comment.
*/
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
/*
3rd Step: Now that we have set up a wake cause, and if needed, setup the
peripherals state in deep sleep mode, we can now start going to
deep sleep.
If you have provided no wake up sources but the deep
sleep has been started, it will sleep forever unless hardware
reset occurs.
*/
Serial.println("Going to sleep now");
delay(500);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This will not be used in Deep Sleep mode.
}
با پروگرم کردن برد ESP32 خود با کد بالا، این برد به حالت Deep Sleep رفته و با لمس پایه 3 لمسی T3 که معادل GPIO15 است، از حالت خواب عمیق بیدار شده و پس از انجام وظایف تعریف شده، دوباره به خواب میرود. خروجی برنامه در Serial Monitor به شکل زیر است.
در ادامه، قسمتهای مختلف کد را توضیح میدهیم.
#define Threshold 40 /* Greater the value, more the sensitivity */
در ابتدای برنامه عددی به عنوان Threshold تعریف شده است. در واقع هر یک از پایههای لمسی ESP32، یک پایه آنالوگ هستند که عددی بین 0 تا 4095 را به پردازنده داخلی برمیگردانند. نحوه کار کردن این پایههای لمسی به این صورت است که در صورت لمس شدن، عددی که برمیگردانند عدد کوچکتری خواهد بود. معمولا در صورت لمس، عددی که پایه لمسی برمیگرداند حدودا پایین تر از 80 است. در این قسمت از برنامه، مقداری را که میخواهیم نشاندهنده لمس شدن پایه لمسی باشد، تعریف میکنیم. واضح است که هر چه مقدار عدد تعریف شده بیشتر باشد، حساسیت افزایش مییابد.
در ادامه نیز همانند روش قبل، یک متغیر با فرمت int در حافظه زمانسنج (RTC) تعریف میکنیم که این متغیر تعداد بوت شدن ESP32 را ذخیره میکند.
پس از آن، مشابه روش قبل، یک تابع تعریف میکنیم که با استفاده از آن بتوانیم بعد از هر بار بیدار شدن ESP32، علت بیدار شدن را در Serial Monitor نمایش دهیم.
//Function for printing the touchpad by which ESP32 has been awakened from sleep
void print_wakeup_touchpad(){
touch_pad_t touchPin;
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;
}
}
یک تابع تعریف میکنیم. در صورتی که علت بیدار شدن ESP32 از خواب عمیق، لمس شدن پایه لمسی باشد، همان پایهای را که لمس شده است، در Serial Monitor نمایش دهد.
void callback(){
//write instructions you want to be performed when the ESP32 wakes up
}
تابعی با نام callback() را تعریف میکنیم. این تابع همانطور که در کامنت موجود در کد هم توضیح داده شده است، مربوط به وظایفی است که از ESP32 میخواهیم پس از هر بار بیدار شدن توسط لمس پایه لمسی، انجام دهد. این تابع در کد ما خالی است و میتوانید وظایفی را مطابق دلخواه خود در آن قرار دهید. (مثلا یک وظیفه ساده مثل روشن کردن یک LED یا هر چیز دیگر)
در ادامه وارد قسمت setup برنامه میشویم. تنظیمات اولیه برای Serial را مشابه روش قبل انجام میدهیم و متغیر تعریف شده در حافظه زمانسنج (RTC) را هم آپدیت میکنیم.
پس از طی مراحل بالا، مشابه روش قبل، قسمتهای اصلی کد را که برای انجام تنظیمات برای بردن ESP32 به Deep Sleep است مشاهده میکنیم. برای توضیح کامل هر کدام از این قسمتها، یک کامنت نیز قبل از هر مرحله نوشته شده است که آن را به طور کامل توضیح میدهد.
/*
1st Step: we should configure the wake up source:
We set our ESP32 to use "TouchPad 3 (GPIO15)" to wake up
To do that, we first need to set up interrupt on one of
the touch pins -in our case "Touch Pad 3"- and then,
configure "Touchpad" as the wakeup source.
*/
//Setup interrupt on Touch Pad 3 (GPIO15)
touchAttachInterrupt(T3, callback, Threshold);
//Configure Touchpad as wakeup source
esp_sleep_enable_touchpad_wakeup();
همانطور که قبلا اشاره شد، برای بردن ESP32 به حال Deep Sleep، ابتدا لازم که روش بیدار کردن آن را تنظیم کنیم. در این قسمت قصد داریم که این کار را با روش استفاده از پایههای لمسی انجام دهیم. برای این کار، لازم است که به طور دقیق پایهای را که میخواهیم در صورت لمس، ESP32 از خواب عمیق بیدار شود مشخص کنیم. برای این کار باید آن پایه را به عنوان یک وقفه خارجی (Interrup) تعریف کنیم. برای این کار از تابع touchAttachInterrupt(Pin, Function, Threshold) استفاده میکنیم. این تابع سه ورودی دارد:
- Pin: پایه لمسی که میخواهیم وقفه روی آن تنظیم شود. (در کد ما T3)
- Function: تابعی که میخواهیم در صورت رخ دادن وقفه انجام شود. (در کد ما تابع callback)
- Threshold: مقدار عددی که نشان دهنده میزان حساسیت پایه لمسی است. (در کد ما در همان ابتدا آن را با نام Threshold تعریف کردهایم.)
این کار را میتوانیم برای بیش از یک پایه لمسی هم انجام دهیم. در آن صورت با لمس هم کدام از آن پایههای تعریف شده، وظیفه مربوط به همان اجرا میشود.
بعد از آن، با دستور esp_sleep_enable_touchpad() روش بیدار شدن ESP32 از حالت Deep Sleep را تنظیم میکنیم.
دو مرحله بعد عینا مشابه روش Timer است. ابتدا قسمتهایی را که میخواهیم در حالت خواب عمیق غیرفعال باشند تنظیم میکنیم. و سپس ESP32 را با استفاده از دستور esp_deep_sleep_start() در حالت Deep Sleep قرار می دهیم. پس از اجرای این دستور، ESP32 به خواب عمیق رفته و با لمس پایه (و یا پایههای) مورد نظر از حالت Deep Sleep خارج شده و با اجرای وظایف مربوطه دوباره به حالت Deep Sleep برمیگردد.
در این مرحله، نحوه بردن ESP32 به حالت Deep Sleep و بیدار کردن آن به کمک پایههای لمسی ESP32 را بررسی کردیم. در قسمت بعد، آخرین روش برای بیدار کردن ESP32 از خواب عمیق را نیز بررسی خواهیم کرد.
بردن ESP32 به Deep Sleep و بیدار کردن آن با روش External Wake-up
اگر آموزش مربوط به پایههای میکروکنترلر ESP32 را خوانده باشید، حتما در بررسی پایههای RTC با این موضوع برخورد کردهاید که یکی از کاربردهای پایههای RTC، بیدار کردن ESP32 از حالت Deep Sleep است.
در واقع روش بیدار کردن برد ESP32 از حالت خواب عمیق، به کمک پایههای RTC است. این پایهها در میکروکنترلر ESP-WROOM-32 که میکروکنترلر اصلی در اکثر بردهای ESP32 است، عبارتند از:
- RTC_GPIO0 (GPIO 36)
- RTC_GPIO3 (GPIO 39)
- RTC_GPIO4 (GPIO 34)
- RTC_GPIO5 (GPIO 35)
- RTC_GPIO6 (GPIO 25)
- RTC_GPIO7 (GPIO 26)
- RTC_GPIO8 (GPIO 33)
- RTC_GPIO9 (GPIO 32)
- RTC_GPIO10 (GPIO 4)
- RTC_GPIO11 (GPIO 0)
- RTC_GPIO12 (GPIO 2)
- RTC_GPIO13 (GPIO 15)
- RTC_GPIO14 (GPIO 13)
- RTC_GPIO15 (GPIO 12)
- RTC_GPIO16 (GPIO 14)
- RTC_GPIO17 (GPIO 27)
برای بردن ESP32 به خواب عمیق و بیدار کردن آن به روش External Wake-up دو حالت وجود دارد:
- ext0: یک پایه مخصوص ESP32 را از حالت Deep Sleep بیدار میکنیم.
- ext1: چندین پایه ESP32 را از حالت Deep Sleep بیدار میکنیم.
این دو مورد بسیار شبیه به هم هستند، اما برای فهم بهتر، هر کدام را جداگانه بررسی میکنیم.
روش ext0:
در این روش، همانطور که اشاره شد با استفاده از یک پایه، ESP32 را از خواب عمیق بیدار میکنیم. در این حالت یکی از پایههای RTC را انتخاب کرده و تنظیم میکنیم که با LOW و یا HIGH شدن آن پایه، ESP32 از حالت Deep Sleep خارج شود.
روش ext1:
در این روش تعدادی از پایههای RTC را انتخاب کرده، و تنظیم میکنیم که در چه صورتی برد ESP32 از حالت Deep Sleep خارج شود. برای این کار دو حالت ممکن است:
- تمامی پایههای انتخاب شده LOW شوند. (ESP_EXT1_WAKEUP_ALL_LOW)
- حداقل یکی از پایههای انتخاب شده HIGH شود. (ESP_EXT1_WAKEUP_ANY_HIGH)
در صورتی که قرار است LOW شدن پایهای را تشخیص دهیم، آن پایه را PULL_UP میکنیم.
در صورتی که قرار است HIGH شدن پایهای را تشخیص دهیم، آن پایه را PULL_DOWN میکنیم.
اندازه مقاومت میتواند بین 1 کیلواهم تا 100 کیلواهم باشد.
کد
به دلیل شباهت روشهای ext0 و ext1، کد مربوط به آنها در یک کد یکسان تهیه شده است. تنها تفاوت کد مربوط به ext0 و ext1 دو خط بوده که در ادامه توضیح میدهیم که برای هر روش از کدام 2 خط استفاده شود.
برای بردن ESP32 به حالت خواب عمیق و بیدار کردن آن به روش External Wake-up و با پایههای RTC، کد زیر را بر روی ESP32 خود آپلود کنید.
/*
Modified on Jul 12, 2021
Modified by MehranMaleki from Arduino Examples
Home
*/
#include "driver/rtc_io.h"
RTC_DATA_ATTR int bootCount = 0;
//Function for printing the reason by which ESP32 has been awakened 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(500);
//Increment boot number and print it every reboot
bootCount++;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/*
1st Step: we should configure the wake up source:
We set our ESP32 to use "ext0 OR ext1" to wake up
*/
//lines of code used for ext0
//rtc_gpio_pulldown_en(GPIO_NUM_33);
//esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
//lines of code used for ext1
#define BUTTON_PIN_BITMASK 0x300000000
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
/*
2nd step: After setting up a wake cause,
we can now start going to deep sleep.
*/
Serial.println("Going to sleep now");
delay(500);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This will not be used in Deep Sleep mode.
}
با پروگرم کردن برد ESP32 خود با کد بالا، این برد به حالت Deep Sleep رفته و
- با HIGH شدن پایه RTC_GPIO8 که معادل GPIO33 است (در صورتی که به روش ext0 عمل کرده باشیم)، و یا
- با HIGH شدن هر یک از پایههای RTC_GPIO8 و RTC_GPIO9 که معادل GPIO33 و GPIO32 هستند (در صورتی که به روش ext1 عمل کرده باشیم)،
از حالت خواب عمیق بیدار میشود.
در ادامه به توضیح کامل کد میپردازیم. (در این کد، برای سادگی بیشتر، مرحله 2 را که مربوط به انتخاب قسمتهایی که غیرفعال میشوند، حذف کردهایم.)
#include "driver/rtc_io.h"
در ابتدای کد، “driver/rtc_io.h” را به برنامه اضافه کردهایم. همانطور که پیشتر تاکید شد، در روش External Wake-up لازم است که پایههای مورد استفاده حتما PULLUP و یا PULLDOWN شوند. در ESP32، مقاومتهای داخلی برای این منظور وجود دارد. یعنی با تنظیم پایه مورد استفاده در کد، میتوان به سادگی آن را PULLDOWN و یا PULLUP کرد. برای استفاده از این قابلیت لازم است که خط “include “driver/rtc_io.h” به برنامه اضافه شود. نحوه PULLUP و یا PULLDOWN کردن یک پایه، در ادامه گفته خواهد شد.
در ادامه کد، همانند روشهای قبل، یک متغیر با فرمت int در حافظه زمانسنج (RTC) تعریف میکنیم. سپس، یک تابع تعریف میکنیم که با استفاده از آن بتوانیم بعد از هر بار بیدار شدن ESP32، علت بیدار شدن را در Serial Monitor نمایش دهیم. در ادامه نیز در قسمت setup ابتدا تنظیمات لازم برای نمایش در Serial Monitor را انجام میدهیم.
پس از طی مراحل بالا، آماده نوشتن کد مربوط به بردن برد ESP32 به حالت Deep Sleep و بیدار کردن آن توسط روش External Wake-up هستیم.
در مرحله اول باید روش بیدار شدن ESP32 از خواب عمیق را تنظیم کنیم. که این کار را برای حالتهای ext0 و ext1 به ترتیب زیر انجام میدهیم.
/*
1st Step: we should configure the wake up source:
We set our ESP32 to use "ext0 OR ext1" to wake up
*/
lines of code used for ext0
rtc_gpio_pulldown_en(GPIO_NUM_33);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
برای حالت ext0 باید از خط دستور esp_sleep_enable_ext0_wakeup(GPIO_NUM_X, mode) استفاده کنیم. این دستور در ورودی اول شماره پایه مورد نظر و در ورودی دوم حالتی را که باعث بیدار شدن ESP32 از Deep Sleep است میگیرد. برای ورودی اول، شماره پایه مورد نظرتان را جای X قرار دهید. ورودی mode نیز میتواند HIGH و یا LOW باشد.
برای PULLUP و یا PULLDOWN با مقاومت داخلی نیز میتوانیم از خط دستور rtc_gpio_pullup_en(GPIO_NUM_X) و rtc_gpio_pulldown_en(GPIO_NUM_X) استفاده کرد.
//lines of code used for ext1
#define BUTTON_PIN_BITMASK 0x300000000
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
برای حالت ext1، ابتدا لازم به ذکر است که باید PULLUP و PULLDOWN کردن پایههای مورد نظرتان را خودتان توسط مقاومت انجام دهید و این کار به کمک مقاومتهای داخلی ESP32 ممکن نیست. در این حالت برای تنظیم روش بیدار شدن از Deep Sleep باید از خط دستور esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, mode) استفاده کنیم. این دستور در ورودی اول یک عدد به صورت hex دریافت میکند که نشاندهنده پایههای مورد نظر است. سادهترین راه برای فهم نحوه ساخت این عدد نوشتن آن به صورت باینری است. به صورت زیر:
0000 0011 0000 0000 0000 0000 0000 0000 0000 0000
هر رقم این عدد باینری 40 رقمی، نماینده پایههای GIPO0 تا GPIO39 است. با یک کردن هر کدام از این عددها، در واقع نشان میدهیم که آن پایه را به عنوان پایهای برای روش ext1 در نظر گرفته ایم. سپس عدد باینری بدست آمده را به صورت hex تبدیل کرده و در کد استفاده کنید. در مثال ما، این عدد 0x30000000 بوده است. (GPIO32 و GPIO33)
ورودی دوم دستور نوشته شده نیز mode است که میتواند ESP_EXT1_WAKEUP_ALL_LOW و یا ESP_EXT1_WAKEUP_ANY_HIGH باشد.
/*
2nd step: After setting up a wake cause,
we can now start going to deep sleep.
*/
Serial.println("Going to sleep now");
delay(500);
esp_deep_sleep_start();
Serial.println("This will never be printed");
پس از تنظیم کامل روش بیدار شدن ESP32 از خواب عمیق، میتوانیم آن را در حالت Deep Sleep قرار دهیم. برای این کار نیز عینا مشابه روشهای قبل عمل میکنیم.
در این آموزش، حالتهای گوناگون Deep Sleep در میکروکنترلر ESP32 را به طور کامل بررسی کردیم و ازین پس میتوانیم با استفاده از این حالت، به بهینهترین شکل ممکن، مصرف انرژی در این میکروکنترلر را کنترل کنیم.
یک گام جلوتر
با اتصال یک سنسور دما و ماژول SD card به یک برد ESP32، برنامهای طراحی کنید که هر 6 ساعت یک بار ESP32 از حالت خواب عمیق بیدار شود. دمای هوا را خوانده، آن را در SD card ذخیره کند و سپس به حالت خواب عمیق برگردد.