خواب عمیق Deep Sleep در ESP32 (آموزش جامع)

محتویات
- ESP32 Deep Sleep
- ورود به حالت Deep Sleep
- منابع بیدارباش ESP32 در حالت Deep Sleep
- بیدار کردن ESP32 با تایمر
- کد نمونه
- نحوه اجرا
- توضیح کد
- بیدار کردن ESP32 از Deep Sleep با لمس
- کد نمونه
- نحوه اجرا
- توضیح کد
- بیداری ESP32 از طریق منابع خارجی
- ext0 : منبع بیدارباش خارجی
- کد نمونه
- توضیح کد
- ext1 : منبع بیدارباش خارجی
- فعال کردن منبع بیدارباش ext1
- Bitmask
- سیمکشی
- کد نمونه
- توضیح کد
وقتی پروژه اینترنت اشیا (IoT) شما با آداپتور دیواری تغذیه میشود، نگرانی زیادی درباره مصرف انرژی وجود ندارد. اما اگر قصد دارید پروژه را با باتری راهاندازی کنید، هر میلیآمپر (mA) اهمیت پیدا میکند.
ESP32 بسته به حالتی که در آن قرار دارد میتواند مصرف انرژی نسبتاً بالایی داشته باشد. بهطور معمول در شرایط عملیاتی عادی حدود 75mA مصرف میکند و هنگام ارسال داده از طریق WiFi حدود 240mA جریان میکشد.
راهحل در اینجا استفاده از حالت Deep Sleep برای کاهش مصرف انرژی ESP32 است.
برای آشنایی بیشتر با سایر حالتهای خواب ESP32 و میزان مصرف توان آنها، میتوانید به آموزش زیر مراجعه کنید:
بررسی مُد های ذخیره انرژی در ESP32 و مصرف آنها
ESP32 بدون شک رقیب قدرتمندی برای بسیاری از تراشههای WiFi/MCU SoCs محسوب میشود و هم از نظر کارایی و هم قیمت عملکرد بالاتری دارد. با این حال، بسته به حالتی که در آن قرار دارد، میزان مصرف انرژی متفاوت است.
ESP32 Deep Sleep
در حالت Deep Sleep، پردازندههای اصلی (CPUها)، بیشتر حافظه RAM و تمام واحدهای جانبی دیجیتال خاموش میشوند. تنها بخشهایی از تراشه که فعال باقی میمانند عبارتاند از:
- ULP Coprocessor
- RTC Controller
- RTC Peripherals
- حافظه سریع و کند RTC
مصرف تراشه در این حالت حدود 0.15mA (در صورتی که ULP coprocessor فعال باشد) تا 10µA است.
در حالت Deep Sleep، پردازنده اصلی خاموش میشود، در حالی که ULP Coprocessor میتواند دادههای حسگر را دریافت کرده و در صورت لزوم پردازنده را بیدار کند. این الگوی خواب به نام ULP sensor-monitored pattern شناخته میشود. این روش برای طراحی برنامههایی کاربرد دارد که در آنها نیاز است پردازنده توسط یک رویداد خارجی، تایمر یا ترکیبی از هر دو بیدار شود، در حالی که مصرف انرژی در حداقل باقی بماند.
به همراه پردازنده اصلی، حافظه اصلی تراشه نیز غیرفعال میشود. در نتیجه هر چیزی که در آن حافظه ذخیره شده باشد پاک شده و دیگر قابل دسترسی نیست.
با این حال، حافظه RTC روشن باقی میماند، بنابراین محتوای آن حتی در طول خواب عمیق حفظ شده و پس از بیدار شدن تراشه قابل بازیابی است. به همین دلیل، تراشه قبل از ورود به حالت Deep Sleep دادههای مربوط به اتصال Wi-Fi و Bluetooth را در حافظه RTC ذخیره میکند.
اگر میخواهید دادهای پس از راهاندازی مجدد (reboot) در دسترس باشد، باید آن را در حافظه RTC ذخیره کنید. این کار با تعریف یک متغیر سراسری همراه با ویژگی RTC_DATA_ATTR انجام میشود. به عنوان مثال:
RTC_DATA_ATTR int myVar = 0;
پس از خروج از حالت Deep Sleep، تراشه ریست میشود و اجرای برنامه از ابتدا آغاز خواهد شد.
ESP32 قابلیت اجرای یک Deep Sleep Wake Stub هنگام خروج از Deep Sleep را نیز پشتیبانی میکند. این تابع بلافاصله پس از بیدار شدن تراشه اجرا میشود، پیش از آنکه هرگونه کد معمولی، bootloader یا کد ESP-IDF اجرا شود. بعد از اجرای wake stub، تراشه میتواند دوباره به حالت خواب برود یا بهطور عادی ESP-IDF را آغاز کند.
ورود به حالت Deep Sleep
برخلاف سایر حالتهای خواب، سیستم نمیتواند بهصورت خودکار وارد حالت Deep Sleep شود. برای این کار باید از تابع esp_deep_sleep_start() استفاده کرد تا تراشه بلافاصله پس از پیکربندی منابع بیدارباش (wake-up sources) وارد خواب عمیق شود.
منابع بیدارباش ESP32 در حالت Deep Sleep
تراشه ESP32 میتواند توسط منابع مختلفی از حالت Deep Sleep بیدار شود. این منابع عبارتاند از:
- تایمر (Timer)
- صفحه لمسی (Touch pad)
- ورودی خارجی (External wakeup – ext0 & ext1)
امکان ترکیب چندین منبع بیدارباش وجود دارد. در این حالت تراشه زمانی بیدار میشود که یکی از منابع فعال شود.
⚠️ هشدار: امکان قرار دادن ESP32 در حالت Deep Sleep بدون پیکربندی هیچ منبع بیدارباشی وجود دارد. در این صورت، تراشه بهطور نامحدود در خواب عمیق باقی میماند تا زمانی که یک ریست خارجی اعمال شود.
بیدار کردن ESP32 با تایمر
کنترلر RTC در برد ESP32 دارای یک تایمر داخلی است که میتوانید از آن برای بیدار کردن ESP32 پس از گذشت یک بازه زمانی از پیش تعریف شده استفاده کنید.
این قابلیت بهویژه در پروژههایی کاربرد دارد که نیاز به ثبت زمان (time stamping) یا انجام وظایف روزانه دارند، در حالی که مصرف انرژی در سطح پایینی حفظ شود.
برای پیکربندی تایمر بهعنوان منبع بیدارباش، از تابع زیر استفاده میشود:
esp_sleep_enable_timer_wakeup(time_in_us)
این تابع یک مقدار زمان را به واحد میکروثانیه (μs) دریافت میکند.
کد نمونه
برای مشاهده نحوه عملکرد، میتوانید از یک نمونه در کتابخانه استفاده کنید. در Arduino IDE مسیر زیر را دنبال کنید:
File > Examples > ESP32 > Deep Sleep > TimerWakeUp
این اسکچ، سادهترین مثال حالت خواب عمیق با تایمر بهعنوان منبع بیدارباش را نشان میدهد و همچنین نحوه ذخیره داده در حافظه RTC برای استفاده پس از راهاندازی مجدد (reboot) را توضیح میدهد.
نحوه اجرا
پس از بارگذاری اسکچ، Serial Monitor را باز کنید و نرخ بود (baud rate) را روی 115200 bps قرار دهید.
ESP32 هر 5 ثانیه یک بار بیدار میشود، دلیل بیدار شدن و مقدار bootCount را روی سریال مانیتور چاپ میکند و دوباره وارد خواب عمیق میشود.
اگر دکمه EN روی ESP32 را فشار دهید، شمارنده bootCount دوباره روی 1 تنظیم میشود که نشاندهنده پاک شدن کامل حافظه RTC است.
توضیح کد
دو خط اول کد مدت زمانی را که ESP32 در حالت خواب خواهد بود مشخص میکنند:
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */ >
در این مثال، از ضریب تبدیل میکروثانیه به ثانیه استفاده شده است تا بتوانید زمان خواب را در متغیر TIME_TO_SLEEP به ثانیه تنظیم کنید. در اینجا ESP32 برای 5 ثانیه به حالت خواب عمیق میرود.
همانطور که پیشتر توضیح داده شد، میتوانید دادهها را در حافظه RTC (به اندازه 8kB SRAM) ذخیره کنید. این دادهها در طول خواب عمیق پاک نمیشوند، اما هنگام ریست شدن ESP32 از بین میروند.
برای ذخیره داده در حافظه RTC کافی است ویژگی RTC_DATA_ATTR را قبل از تعریف متغیر اضافه کنید. در این مثال، متغیر bootCount در حافظه RTC ذخیره شده است تا تعداد دفعات بیدار شدن ESP32 از خواب عمیق را بشمارد:
RTC_DATA_ATTR int bootCount = 0;
سپس تابع print_wakeup_reason() تعریف شده است که دلیل بیدار شدن ESP32 از حالت خواب عمیق را چاپ میکند.
در تابع setup، ابتدا ارتباط سریال با کامپیوتر مقداردهی اولیه میشود:
Serial.begin(115200);
سپس متغیر bootCount یک واحد افزایش یافته و روی سریال مانیتور چاپ میشود:
++bootCount; Serial.println("Boot number: " + String(bootCount));
در ادامه تابع print_wakeup_reason() فراخوانی میشود تا دلیل بیداری ESP32 نمایش داده شود. البته میتوانید در این بخش هر تابع دیگری که برای کار مدنظر شما لازم است (مثل خواندن مقدار یک حسگر) اجرا کنید.
بعد، تایمر بیدارباش پیکربندی میشود تا ESP32 هر 5 ثانیه بیدار شود:
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
در نهایت، ESP32 با فراخوانی تابع زیر به حالت خواب عمیق میرود:
esp_deep_sleep_start();
از آنجا که ESP32 در همان تابع setup به خواب میرود، هیچگاه وارد تابع loop() نمیشود. به همین دلیل تابع loop() خالی باقی گذاشته شده است:
void loop(){ //This is not going to be called }
بیدار کردن ESP32 از Deep Sleep با لمس
میتوان ESP32 را از حالت Deep Sleep با استفاده از پینهای لمسی زیر بیدار کرد.
فعال کردن بیدارباش ESP32 با پین لمسی ساده است. در Arduino IDE کافی است از تابع زیر استفاده کنید:
esp_sleep_enable_touchpad_wakeup()
یک کابل به GPIO#15 (Touch#3) متصل میکنیم که بهعنوان منبع بیدارباش لمسی عمل میکند. میتوانید هر جسم رسانا مانند سیم، فویل آلومینیومی، پارچه رسانا، رنگ رسانا و غیره را به پین حساس به لمس متصل کنید تا آن را به یک Touch Pad تبدیل کنید.
کد نمونه
برای مشاهده عملکرد، میتوانید از یک نمونه در کتابخانه استفاده کنید. در Arduino IDE مسیر زیر را دنبال کنید:
File > Examples > ESP32 > Deep Sleep > TouchWakeUp
این اسکچ، سادهترین مثال حالت Deep Sleep با صفحه لمسی بهعنوان منبع بیدارباش را نشان میدهد و همچنین نحوه ذخیره دادهها در حافظه RTC برای استفاده پس از راهاندازی مجدد را توضیح میدهد.
نحوه اجرا
پس از بارگذاری اسکچ، Serial Monitor را باز کنید و نرخ بود (baud rate) را روی 115200 bps قرار دهید.
اکنون هنگامی که پین لمسی فعال شود، ESP32 مقدار bootCount، دلیل بیداری و پین GPIO که لمس شده را روی سریال مانیتور نمایش میدهد.
توضیح کد
- Threshold: خط اول مقدار آستانه حساسیت پین لمسی را مشخص میکند. هر چه مقدار بالاتر باشد، حساسیت بیشتر خواهد بود. این مقدار میتواند متناسب با پروژه تغییر کند.
#define Threshold 40 /* Greater the value, more the sensitivity */
- حافظه RTC (8kB SRAM) همانطور که پیشتر گفته شد، در طول Deep Sleep پاک نمیشود، اما هنگام ریست شدن ESP32 از بین میرود.
- برای ذخیره داده در حافظه RTC کافی است RTC_DATA_ATTR قبل از تعریف متغیر اضافه شود. در این مثال، متغیر bootCount برای شمارش دفعات بیدار شدن از Deep Sleep استفاده شده است:
RTC_DATA_ATTR int bootCount = 0;
- سپس یک متغیر از نوع touch_pad_t به نام touchPin تعریف میشود تا بتوان پین GPIO که باعث بیدار شدن ESP32 شده را چاپ کرد:
touch_pad_t touchPin;
- تابع print_wakeup_reason() دلیل بیداری ESP32 را چاپ میکند.
- تابع print_wakeup_touchpad() شماره پین لمسی که باعث بیدار شدن ESP32 شده است را چاپ میکند.
- تابع callback() یک Interrupt Service Routine (ISR) است که هر بار که یک وقفه لمسی رخ میدهد فراخوانی میشود. توجه داشته باشید که این تابع در حالت Deep Sleep اجرا نمیشود، بنابراین خالی باقی گذاشته شده است.
- در تابع setup ابتدا ارتباط سریال با کامپیوتر مقداردهی میشود و سپس متغیر bootCount یک واحد افزایش مییابد و روی سریال مانیتور چاپ میشود.
- پس از آن توابع print_wakeup_reason() و print_wakeup_touchpad() فراخوانی میشوند، ولی شما میتوانید هر تابع دیگری برای انجام کار موردنظر خود جایگزین کنید، مثل خواندن مقدار یک حسگر.
- سپس وقفه به یکی از پینهای لمسی با آستانه حساسیت مشخص متصل میشود. در اینجا به Touch Pad 3 (GPIO15) وصل شده است:
touchAttachInterrupt(T3, callback, Threshold);
- سپس منبع بیدارباش لمسی با تابع زیر پیکربندی میشود:
esp_sleep_enable_touchpad_wakeup();
- در نهایت، ESP32 با تابع زیر به حالت Deep Sleep میرود:
esp_deep_sleep_start();
- همانند مثال قبل، ESP32 در تابع setup() وارد حالت Deep Sleep میشود و هیچگاه به تابع loop() نمیرسد. بنابراین تابع loop() خالی باقی مانده است.
void loop(){ //This will never be called }
بیداری ESP32 از طریق منابع خارجی
دو نوع تریگر خارجی برای بیدار کردن ESP32 از حالت Deep Sleep وجود دارد:
- ext0 – زمانی استفاده میشود که میخواهید تراشه تنها توسط یک پین GPIO خاص بیدار شود.
- ext1 – زمانی استفاده میشود که میخواهید تراشه با استفاده از چند پین GPIO بیدار شود.
اگر میخواهید از یک پین وقفهای برای بیدار کردن ESP32 از حالت Deep Sleep استفاده کنید، باید از پینهای RTC_GPIO استفاده کنید. این GPIOها به زیرسیستم کممصرف RTC متصل هستند و میتوانند هنگام قرارگیری ESP32 در Deep Sleep نیز فعال باشند.
ESP32 دارای چند پین RTC_GPIO است که برای بیدار شدن از حالت Deep Sleep استفاده میشوند.
ext0 : منبع بیدارباش خارجی
با استفاده از ext0 میتوان ESP32 را طوری پیکربندی کرد که هنگام تغییر سطح منطقی یکی از پینهای RTC_GPIO بیدار شود.
برای فعال کردن این منبع بیدارباش از تابع زیر استفاده میکنیم:
esp_sleep_enable_ext0_wakeup(GPIO_PIN, LOGIC_LEVEL)
این تابع دو پارامتر میگیرد:
- شماره پین GPIO
- سطح منطقی (LOW یا HIGH) که با رسیدن به آن ESP32 بیدار شود.
از آنجا که ext0 از RTC IO برای بیدار کردن ESP32 استفاده میکند، پرipherals مربوط به RTC در طول Deep Sleep روشن باقی میمانند.
همچنین با فعال بودن ماژول RTC IO میتوان از مقاومتهای pull-up یا pull-down داخلی استفاده کرد. این مقاومتها باید قبل از فراخوانی esp_deep_sleep_start() با توابع زیر پیکربندی شوند:
rtc_gpio_pullup_en() rtc_gpio_pulldown_en()
یک Push Button به پین GPIO#33 متصل کنید و از یک مقاومت 10K Pull-down استفاده کنید.
کد نمونه
برای مشاهده عملکرد، میتوانید از یک نمونه در کتابخانه استفاده کنید. در Arduino IDE مسیر زیر را دنبال کنید:
File > Examples > ESP32 > Deep Sleep > ExternalWakeUp
این اسکچ، سادهترین مثال حالت Deep Sleep با ext0 بهعنوان منبع بیدارباش را نشان میدهد.
پس از بارگذاری اسکچ، Serial Monitor را باز کنید و نرخ بود (baud rate) را روی 115200 bps قرار دهید.
اکنون وقتی دکمه فشار داده شود، ESP32 مقدار bootCount و دلیل بیدار شدن را روی سریال مانیتور نمایش میدهد. چند بار دکمه را فشار دهید و مشاهده کنید که bootCount با هر بار فشار افزایش مییابد. توجه داشته باشید که ext0 از RTC IO برای بیدار کردن ESP32 استفاده میکند.
توضیح کد
- خط اول کد Bitmask را تعریف میکند. برای ext0 ضروری نیست و میتوان آن را نادیده گرفت. برای ext1 کاربرد دارد.
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
- همانطور که پیشتر گفته شد، میتوان دادهها را در حافظه RTC (8kB SRAM) ذخیره کرد که در طول Deep Sleep پاک نمیشود، اما هنگام ریست ESP32 از بین میرود.
- برای ذخیره داده در حافظه RTC کافی است RTC_DATA_ATTR قبل از تعریف متغیر اضافه شود. در این مثال، bootCount برای شمارش دفعات بیدار شدن از Deep Sleep استفاده شده است:
RTC_DATA_ATTR int bootCount = 0;
- تابع print_wakeup_reason() دلیل بیداری ESP32 از Deep Sleep را چاپ میکند.
- در تابع setup() ابتدا ارتباط سریال با کامپیوتر مقداردهی میشود و سپس bootCount یک واحد افزایش یافته و چاپ میشود.
- سپس تابع print_wakeup_reason() فراخوانی میشود، ولی میتوانید هر تابع دیگری برای انجام کار موردنظر خود جایگزین کنید، مثل خواندن مقدار یک حسگر.
- منبع بیدارباش ext0 با تابع زیر پیکربندی میشود:
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1);
در این مثال ESP32 زمانی بیدار میشود که سطح منطقی GPIO#33 برابر HIGH شود.
- در نهایت، ESP32 با تابع زیر به حالت Deep Sleep میرود:
esp_deep_sleep_start();
- همانند مثالهای قبلی، ESP32 در تابع setup() وارد Deep Sleep میشود و هیچگاه به تابع loop() نمیرسد. بنابراین تابع loop() خالی باقی مانده است:
void loop(){ //This is not going to be called }
ext1 : منبع بیدارباش خارجی
با استفاده از ext1 میتوان ESP32 را طوری پیکربندی کرد که با چندین پین بیدار شود. توجه داشته باشید که این پینها باید از میان پینهای RTC_GPIO باشند.
از آنجا که منبع بیدارباش ext1 از RTC Controller استفاده میکند، نیازی به روشن بودن پرipherals RTC یا حافظه RTC نیست. در این حالت مقاومتهای داخلی pull-up و pull-down در دسترس نخواهند بود.
اگر بخواهیم از مقاومتهای داخلی pull-up یا pull-down استفاده کنیم، باید درخواست کنیم که پرipherals RTC در طول خواب روشن باقی بمانند و قبل از ورود به حالت خواب، مقاومتها با توابع زیر پیکربندی شوند:
rtc_gpio_pullup_en() rtc_gpio_pulldown_en()
فعال کردن منبع بیدارباش ext1
برای فعال کردن منبع بیدارباش ext1 از تابع زیر استفاده میکنیم:
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_MASK, LOGIC_LEVEL)
این تابع دو پارامتر میگیرد:
Bit mask که مشخص میکند کدام پینها به عنوان منابع بیدارباش استفاده شوند
سطح منطقی که با رسیدن به آن، بیدارباش اتفاق میافتد. گزینهها:
- ESP_EXT1_WAKEUP_ANY_HIGH : بیدار شدن اگر یکی از پینهای انتخاب شده HIGH شود
- ESP_EXT1_WAKEUP_ALL_LOW : بیدار شدن اگر تمام پینهای انتخاب شده LOW شوند
Bitmask
سادهترین روش برای درک Bitmask این است که آن را به صورت باینری بنویسید. شمارهبندی بیتها بر اساس شماره GPIO است:
- LSB = GPIO#0
- MSB = GPIO#39
- 0 نشاندهنده پینهای غیرفعال
- 1 نشاندهنده پینهایی که به عنوان منبع بیدارباش فعال هستند
برای فعال کردن یک GPIO، در موقعیت مربوطه 1 و در بقیه پینها 0 قرار دهید و سپس آن را به HEX تبدیل کنید.
مثال: اگر بخواهیم GPIO#32 و GPIO#33 را به عنوان منابع بیدارباش فعال کنیم، Bitmask به صورت زیر خواهد بود:
00000011 00000000 00000000 00000000 00000000 BIN = 0x300000000 HEX
سیمکشی
دو Push Button به پینهای GPIO#32 و GPIO#33 متصل کنید و از مقاومتهای 10K Pull-down استفاده کنید.
کد نمونه
برای نمونه، از همان اسکچ ExternalWakeUp استفاده میکنیم:
تغییرات لازم:
- تغییر BUTTON_PIN_BITMASK
- کامنت کردن کد مربوط به ext0
- از کامنت خارج کردن کد مربوط به ext1
توضیح کد
- این کد مشابه ext0 است با دو تفاوت:
- در ابتدای کد، Bitmask تعریف شده است. برای GPIO#32 و GPIO#33، بیتهای مربوطه برابر 1 و سایر بیتها 0 هستند.
- ext1 به عنوان منبع بیدارباش فعال شده است:
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
- با فشار دادن هر یک از دکمهها، ESP32 با استفاده از RTC Controller بیدار میشود و میتوان bootCount و دلیل بیدارباش را روی Serial Monitor مشاهده کرد.