فهرست مطالب
Toggleآموزش ESP32-IDF قسمت دوم
سلام به آموزش ESP32-IDF قسمت دوم خوش آمدید.
در این قسمت با استفاده از ESP-IDF، میخوام شما رو با برخی مفاهیم اولیه که در هنگام برنامه نویسی ESP32 در محیط VsCode باید بدونیم آشنا کنم .همچنین نحوه کار با GPIO ها رو هم در ادامه با ذکر چند مثال عملی براتون توضیح میدم.
اگر هنوز نمیدونید که چطور باید فریمورک ESP-IDF رو نصب و محیط VsCode رو برای کار آماده کنید حتما به قسمت اول آموزش مراجعه نمایید.
لاگ کردن در ESP32 :
ESP-IDF دارای یک کتابخانه بسیار مفید لاگ کردن است که بسیار در حین کدنویسی برای دیباگ برنامه مورد استفاده قرار میگیرد.به همین منظور قبل از هرکاری این کتابخونه رو مورد بررسی قرار میدهیم. برای استفاده از توابع لاگ در ESP32 ابتدا نیاز است فایل هدر “esp_log.h” را به برنامه خود اضافه نماییم.
#include "esp_log.h"
با استفاده از این کتابخانه ما می توانیم به جای چاپ یک دستور با استفاده از دستور printf از انواع مختلف اعلانهای log استفاده کنیم. این کتابخانه لاگ برنامه را در 5 سطح مختلف به ما نشان میدهد.
- ESP_LOGE: Error Logging (lowest level)
- ESP_LOGW : Warning Logging
- ESP_LOGI : Informational Log
- ESP_LOGD : Debug Statement
- ESP_LOGV : Verbose(highest level)
که به ترتیب لاگ خطا،لاگ هشدار،لاگ اطلاعات،لاگ دیباگ و لاگ طولانی.فکر کنم همون انگلیسیشو یاد بگیرید بهتر باشه.
برای اینکه یک خطا در برنامه رو اعلان کنیم میتونیم از دستور زیر استفاده کنیم :
ESP_LOGE(tag, format, __VA_ARGS__)
نکته: در تابه ()ESP_LOGE حرف E نشان دهنده خطا یا Erorr میباشد.به این رویش میتونید راحت تر به یاد بسپارید.
تابع log دو آرگومان به عنوان ورودی می گیرد. آرگومان اول تگ و آرگومان دوم یک رشته قالب بندی شده است که در واقع همان پیغام نمایش داده شده هنگام خطا میباشد. تگ هم در واقع به عنوان یک برچسب استفاده میشود تا بتوانیم تشخیص بدیم خطا مربوط به کدام بخش میباشد.اگر برچسبهای مختلفی را در بخشهای مختلف کد تنظیم کنید، نظارت بر آن بخش از کد آسانتر میشود.
خب بزارید یک مثال بزنیم، ما “LOG” را به عنوان اولین پارامتر که تگ است و “This is an error” را به عنوان رشته فرمت شده به جای آرگومان دوم قرار میدیم. بنابراین تابع ثبت خطای ما به شکل زیر خواهد بود:
ESP_LOGE("LOG", "This is an error");
به همین ترتیب برای سایر انواع log هم توابع مربوطه آنها را با آرگومانهای دلخواه فراخوانی میکنیم.مانند مثال زیر :
ESP_LOGW("LOG", "This is a warning");
ESP_LOGI("LOG", "This is an info");
ESP_LOGD("LOG", "This is a debug");
ESP_LOGV("LOG", "This is a verbose");
خب بیاید این تکه کد رو تست کنیم و ببینیم خروجی به چه شکل هستش.
خب حالا کد رو فلش و مانیتور میکنیم تا ببینیم خروجی به چه صورت هستش.
برای فلش و مانیتور کردن میتونیم از دستور زیر استفاده کنیم.
یا از نوار پایین VsCode بر روی build,flash,monitor کلیک میکنیم.
idf.py flash monitor
خب حالا یکم بررسی کنیم ببینم عباراتی که تو خروجی چاپ شدن به چه معنایی هستند. کلمه E اول Error و W اول Warning و حرف I هم ابتدای Info میباشد.اعداد بین پرانتزها هم زمان را بر حسب میلی ثانیه پس از شروع برنامه و فراخوانی آن تابع خاص نشان می دهد.
اگر دقت کنید متوجه میشید که لاگ های خطا، هشدار و اطلاعات قابل مشاهده هستند اما دستور debug و verbose وجود ندارد. دلیلیش هم این هست که به طور پیش فرض، فعال نیستند. خودمان باید تنظیمات را تغییر دهیم.
روش فعال کردن این سطح لاگ رو تو ویدئو آموزشی این بخش توضیح دادم.
فعال سازی Debug و Verbose در کد:
ما میتوانیم با استفاده از کد هم سطح دیباگ و سطح verbose رو فعال کنیم.کافیه که از تابع زیر استفاده کنیم.این تابه دو آرگومان دارد که ارگومان اول TAG که تعیین میکند تنظیمات جدید روی Log مربوط به کدوم تگ اعمال بشه و آرگومان دوم سطح جدید رو تعیین میکند.روی هر سطحی تنظیم بشود فقط سطوح پایینتر از آن سطح در خروجی نمایش داده میشوند.
esp_log_level_set("LOG", ESP_LOG_INFO);
خب برای اینکه بهتر متوجه بشید یکسری دستور Log دیگه با برچسب متفاوت و با نام TAG هم اضافه کردیم تا دقیق متوجه نحوه عملکرد این تابع بشید. کد نهایی به شکل زیر خواهد بود.
#include
#include "esp_log.h"
void app_main(void)
{
esp_log_level_set("LOG", ESP_LOG_INFO);
ESP_LOGE("LOG", "This is an error");
ESP_LOGW("LOG", "This is a warning");
ESP_LOGI("LOG", "This is an info");
ESP_LOGD("LOG", "This is a debug");
ESP_LOGV("LOG", "This is a verbose");
ESP_LOGE("TAG", "This is an error");
ESP_LOGW("TAG", "This is a warning");
ESP_LOGI("TAG", "This is an info");
ESP_LOGD("TAG", "This is a debug");
ESP_LOGV("TAG", "This is a verbose");
}
اگر به خروجی دقت کنید میبینید که. خطوطی که برچسب “LOG” داشتند تا سطح Info چاپ شدند. که به این دلیل است که ما تگ “LOG” را روی سطح Info تنظیم می کنیم. با این حال، مواردی که دارای برچسب “TAG” بودند تمام چاپ شدند. زیرا این حالت حالت پیشفرضی است که ما تنظیم کردیم.
ایجاد تاخیر ESP32 با ESP-IDF :
مفهوم مهم دیگری که بسیار مورد استفاده قرار می گیرد، گنجاندن تاخیر در کد برنامه است.
داخل ویدیوی آموزشی قسمت قبل توضیح دادم که FreeRTOS به صورت یکپارچه درون ESP-IDF قرار دارد.بنابراین ما برای ایجاد تاخیر از توابع FreeRTOS استفاده خواهیم کرد.
در ابتدای کار لازم هستش که تو هدر فایل زیر را به کد پروژه اضافه کنیم.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
خب حالا چیکار کنیم که بتونیم تاخیر بر حسب میلی ثانیه ایجاد کنیم.
جوابش خود FreeRTOS هستش.!
با استفاده از ثابتی به نام portTICK_PERIOD_MS میتوانیم تیک کلاک رو به میلی ثانیه تبدیل کنیم. فقط کافیه مقدار موردنظر رو تقسیم براین ثابت بکنیم
vTaskDelay(1000 /portTICK_PERIOD_MS);
الان با استفاده از تیکه کد بالا تاخیری برابر با 1000 میلی ثانیه خواهیم داشت.
برای مثال می تونیم کد زیر را اجرا کنیم تا ببینیم چگونه تاخیر ایجاد می شود. در کد زیر ما TAG را به عنوان “DELAY” تعریف کرده ایم. و به عنوان پارامتر اول به تابع ()ESP_LOGI ارسال میکنیم. برای پارامتر دوم، مقدار متغیر int ‘i’ را افزایش میدهیم که در ابتدا مقدار 0 را دارد. ابتدا یک تاخیر ایجاد میشود و پس از آن لاگ اطلاعات چاپ میشود.
#include
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define TAG "DELAY"
void app_main(void)
{
int i = 0;
while(1)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "in loop %d", i++);
}
}
خب بیاید این تکه کد رو تست کنیم و ببینیم خروجی به چه شکل هستش.
خب حالا کد رو اجرا میکنیم تا ببینیم خروجی به چه صورت است.
idf.py flash monitor
خب همونطور که در تصویر بالا مشخص هست هر لوپ با تاخیر 1 ثانیه اجرا میشود. به عدد داخل پرانتز دقت کنید!!.
LED چشمک زن با ESP-IDF :
ابتدایی ترین توانایی در کار با پردازنده های دیجیتال توانایی کار با ورودی خروجی های همه منظوره یا GPIO ها میباشد. در این آموزش با پین های ESP32 GPIO و نحوه برنامه ریزی آنها به عنوان پایه های خروجی دیجیتال آشنا می شویم.
قراره که با هم یک پروژه در VsCode ایجاد کنیم و با استفاده از ESP-IDF یک پایه را به حالت خروجی برده و توسط آن یک LED را خاموش و روشن کنیم.
در این مثال با نحوه پیکربندی GPIO ها در ESP-IDF آشنا خواهید شد.
بردی که ما در این مثال استفاده کردیم YD-ESP32-23 نام دارد.البته همانطور که قبلا گفتم فرقی نداره که از چه برد توسعه ای استفاده میکنید.ترجیحا سری ESP32-S3 باشه که با اموزشها هماهنگ باشه.
دربالا نحوه نامگذاری پینها در برد مورد استفاده مارا مشاهده میکنید.در ویدئو این قسمت در مورد GPIO ها با جزئیات صحبت کردیم.
برای نوشتن این مثال ما GPIO9 رو انتخاب میکنیم.پایه GPIO9 را یک مقاومت و یک LED به صورت سری به زمین متصل میکنیم.
API های واحد GPIO :
قبل از ادامه کار تعدادی از توابع کار با GPIO ها را در ESP-IDF با هم بررسی میکنیم.
esp_err_t gpio_config(const gpio_config_t *pGPIOConfig)
تابع بالا برای اعمال تنظیمات مورد نظر برای یک GPIO فراخوانی میشود.
ویژگیهایی مانند Mode ، فعال بودن پول آپ یا پول داون و نوع اینتراپت پین توسط این تابع ست میشود.
pGPIOConfig یک اشاره گر به ساختار تنظیمات GPIO میباشد.
تابع بعدی تابع ریست تنظیمات GPIO میباشد که تنظیمات را به حالت پیشفرض باز میگرداند.
حالت پیشفرض GPIO میباشد با پول آپ فعال و غیر فعال بودن خروجی و ورودی.همچنین اگر پایه موردنظر را به یک واحد جانبی متصل کرده باشید مانند پورت سریال با فراخوانی این تابع GPIO از واحد جانبی اختصاص داده شده جدا میشود.
esp_err_t gpio_reset_pin(gpio_num_t gpio_num);
gpio_num شماره پین موردنظر
تابع بعدی برای تنظیم کردن سطح ولتاژ GPIO مورد نظر استفاده میشود.
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level)
gpio_num GPIO_NUM_16 به عنوان مثال
level low:0 high:1
مقدار برگشتی این توابع میتواند دو مقدار زیر باشد.
ESP_OK Success
ESP_ERR_INVALID_ARG Parameter error
تابع مهم دیگری که نیاز است برای این مثال بدونیم تابع تنظیم GPIO به عنوان ورودی یا خروجی میباشد.
esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
در تابع بالا gpio_num شماره پین مورد نظر میباشد.به عنوان مثال اگر بخواهیم جهت gpio 18 را تعیین کنیم باید مقدار GPIO_NUM_18 را به تابع ارسال کنیم.
همچنین مقدار mode جهت پین را تعیین میکند.
gpio_pad_select_gpio(uint8_t gpio_num)
با استفاده از این تابع میتوان پایه مورد نظر را به عنوان GPIO انتخاب نمود.
البته کتابخانه gpio میکروکنترلر ESP32 توابع دیگری هم دارد که فعلا به آنها نمیپردازیم و در مثالهای آینده در مورد آنها صحبت خواهیم کرد.
خب طبق آموزش جلسه اول ابتدا یک پروژه میسازیم و کد زیر را در درون آن قرار میدهیم.
#include
#include "driver/gpio.h"
#include "rom/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define LED_PIN 18
void app_main(void)
{
gpio_pad_select_gpio(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
int ON = 0;
while(true)
{
ON = !ON;
gpio_set_level(LED_PIN, ON);
vTaskDelay(1000/ portTICK_PERIOD_MS);
}
}
بررسی کد LED چشمک زن با ESP-IDF :
در کد بالا ابتدا هرفایلهای مورد نیاز رو اضافه کردیم.
“driver/gpio.h” و “rom/gpio.h” هدرهای موردنیاز برای کار با GPIO ها میباشد.
کتابخانه های freeRTOS رو هم به منظور استفاده از تاخیر در برنامه اضافه میکنیم.
#include
#include "driver/gpio.h"
#include "rom/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
ما در برنامه میخواهیم gpio18 رو به LED متصل کنیم بنابراین برای راحتی کار یک ثابت با نام LED_PIN رو دیفاین میکنیم .این کار برای راحتی و خوانایی کد هستش.
#define LED_PIN 18
در ادامه لازم هستش که پین 18 رو به عنوان GPIO تعریف کنیم و این کار با فراخوانی تابع زیر و ارسال LED_PIN به عنوان پارامتر به درون تابع انجام میشود.
gpio_pad_select_gpio(LED_PIN);
سپس با استفاده از تابع ()gpio_set_direction جهت پین را به عنوان خروجی تنظیم می کنیم. این تابع دو آرگومان می گیرد. آرگومان اول پین GPIO و آرگومان دوم حالتی (ورودی یا خروجی) است که میخواهیم پین را در آن حالت تنظیم کنیم. در این مورد ، میخواهیم LED_PIN را به عنوان پایه خروجی تنظیم کنیم.
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
حالا برای اینکه LED ما چشمک بزند منطق زیر را در حلقه while پیاده سازی میکنیم.
while(true)
{
ON = !ON;
gpio_set_level(LED_PIN, ON);
vTaskDelay(1000/ portTICK_PERIOD_MS);
}
ابتدا یک متغیر عدد صحیح به نام “ON” ایجاد می کنیم که مقدار 0 را نگه می دارد. از این متغیر به عنوان یک فلگ یا پرچم که وضععیت روشن یا خاموش بودن LED را نگه میدارد استفاده میکنیم. در داخل حلقه while، متغیر ON را تغییر می دهیم. و با استفاده از gpio_set_level(LED_PIN، ON)، وضعیت LED را روی مقدار ذخیره شده در ON قرار میدهیم می کنیم. سپس قبل از اینکه دوباره حلقه تکرار شود یک تاخیر نیز اضافه می کنیم تا بتونیم چشمک زدن LED را ببینیم.
برای اضافه کردن 1 ثانیه تاخیر بین تغییر وضعیت LED، از تابع ()vTaskDelay استفاده می کنیم.
خب دیگه کار خاصی باقی نمانده و فقط کافیه پروژه کامپایل و فلش شود.
اگر احیانا برنامه با خطا همراه بود محتویات فایل CMakeLists.txt موجود در پوشه main رو با عبارت زیر جایگزین نمایید.داخل ویدئو دلیلشو توضیح دادم.
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES freertos esp_common driver )