آموزش آردوینو

آموزش کامنت گذاری و ساختار کلی اسکچ در آردوینو (setup و loop)

در این مستند با ساختار اصلی برنامه های آردوینو که به آن ها Sketch گفته می شود آشنا می شوید. کاربرد تابع های setup() و loop()، تفاوت اجرای یک باره و تکرارشونده آن ها، روش نوشتن کامنت های تک خطی و چند خطی و اصول مرتب سازی کد را با مثال های عملی یاد می گیرید. همچنین با اشتباهات رایج در ساختار برنامه نویسی آردوینو آشنا خواهید شد.

سطح مقدماتی

آموزش کامنت گذاری و ساختار کلی اسکچ در آردوینو (setup و loop)

مقدمه

برای برنامه نویسی برد آردوینو، معمولا از محیط Arduino IDE استفاده می کنیم. برنامه ای که در این محیط می نویسیم، Sketch نام دارد.

یک اسکچ فقط مجموعه ای از دستورها نیست؛ بلکه ساختاری مشخص دارد که تعیین می کند برنامه از کجا شروع شود، کدام تنظیمات در ابتدای اجرا انجام شوند و چه کارهایی به صورت مداوم تکرار شوند.

دو تابع اصلی در بیشتر برنامه های معمول آردوینو عبارت اند از:

  • setup()
  • loop()

در کنار این ساختار، کامنت گذاری یا توضیح نویسی به ما کمک می کند هدف بخش های مختلف کد را مشخص کنیم و در آینده راحت تر آن را توسعه دهیم.

۱. ساختار اصلی یک اسکچ آردوینو

یک برنامه ساده آردوینو معمولا به شکل زیر نوشته می شود:

void setup() {
  // دستورهای مربوط به راه اندازی اولیه
}

void loop() {
  // دستورهایی که باید مرتب تکرار شوند
}

در این ساختار، دو تابع اصلی وجود دارد:

  • تابع setup() برای انجام تنظیمات اولیه استفاده می شود.
  • تابع loop() پس از پایان setup() به صورت پیوسته اجرا می شود.

هنگامی که برد روشن یا ریست می شود، برنامه از ابتدا راه اندازی می شود. پس از اجرای setup()، آردوینو وارد loop() می شود و تا زمانی که برنامه در حال اجراست، آن را تکرار می کند.

۲. تابع setup چیست؟

تابع setup() برای دستورهایی استفاده می شود که معمولا باید یک بار در هر بار راه اندازی برنامه اجرا شوند.

برای مثال، تنظیم ورودی یا خروجی بودن یک پایه، شروع ارتباط سریال و آماده سازی اولیه بعضی از ماژول ها در این تابع انجام می شود.

مثال

const int ledPin = 8;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
}

در این برنامه:

  • مقدار ledPin شماره پایه متصل به LED را مشخص می کند.
  • دستور pinMode() پایه را به عنوان خروجی تنظیم می کند.
  • دستور Serial.begin(9600) ارتباط سریال را با نرخ ۹۶۰۰ بیت در ثانیه راه اندازی می کند.
  • تابع loop() خالی است؛ بنابراین هیچ دستور تکرارشونده ای اجرا نمی شود.

نکته: منظور از «یک بار» این است که تابع setup() در هر بار راه اندازی برنامه یک بار اجرا می شود. اگر برد ریست شود یا دوباره روشن شود، این تابع مجددا اجرا خواهد شد.

۳. تابع loop چیست؟

تابع loop() محل قرار گرفتن دستورهایی است که باید به صورت مداوم اجرا شوند.

پس از پایان setup()، آردوینو دستورهای داخل loop() را به ترتیب اجرا می کند. وقتی به انتهای تابع برسد، اجرای آن را از ابتدا شروع می کند.

مثال روشن و خاموش کردن LED

const int ledPin = 8;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  digitalWrite(ledPin, HIGH);
  delay(1000);

  digitalWrite(ledPin, LOW);
  delay(1000);
}

در این برنامه:

  1. پایه ۸ به عنوان خروجی تنظیم می شود.
  2. LED روشن می شود.
  3. برنامه یک ثانیه منتظر می ماند.
  4. LED خاموش می شود.
  5. برنامه یک ثانیه دیگر منتظر می ماند.
  6. این مراحل دوباره تکرار می شوند.

در این مثال، دستورهای مربوط به تغییر وضعیت LED داخل loop() قرار گرفته اند؛ زیرا باید بارها اجرا شوند.

۴. تفاوت setup و loop

ویژگیsetup()loop()
زمان اجرایک بار در هر راه اندازیبه صورت پیوسته
کاربرد اصلیتنظیمات اولیهاجرای منطق اصلی برنامه
تنظیم پایه هامعمولا اینجا انجام می شوددر صورت نیاز هم ممکن است تغییر کند
راه اندازی ارتباط سریالمعمولا اینجا انجام می شودمعمولا لازم نیست در هر دور تکرار شود
خواندن مداوم سنسورمعمولا محل اصلی آن نیستمعمولا اینجا انجام می شود
کنترل پیوسته خروجی هابرای کارهای یک باره مناسب نیستمحل معمول اجرای این کارهاست

۵. کامنت گذاری در آردوینو چیست؟

کامنت یا Comment متنی است که برای توضیح کد نوشته می شود و کامپایلر آن را به عنوان دستور اجرایی برنامه در نظر نمی گیرد.

کامنت ها برای موارد زیر مفید هستند:

  • توضیح هدف یک بخش از برنامه
  • مشخص کردن کاربرد پایه ها
  • ثبت علت انتخاب یک روش برنامه نویسی
  • کمک به عیب یابی و نگهداری کد
  • توضیح محدودیت ها یا نکات مهم مدار

در زبان برنامه نویسی آردوینو که بر پایه C++ است، دو روش رایج برای کامنت گذاری وجود دارد.

۶. کامنت تک خطی

برای نوشتن کامنت تک خطی از دو علامت اسلش استفاده می کنیم:

//

هر چیزی که در همان خط بعد از این علامت قرار بگیرد، به عنوان کامنت شناخته می شود.

مثال

// شماره پایه متصل به LED
const int ledPin = 8;

void setup() {
  pinMode(ledPin, OUTPUT); // تنظیم پایه به عنوان خروجی
}

void loop() {
  digitalWrite(ledPin, HIGH); // روشن کردن LED
  delay(1000);                // انتظار یک ثانیه
  digitalWrite(ledPin, LOW);  // خاموش کردن LED
  delay(1000);
}

در این مثال، کامنت ها توضیح می دهند که هر بخش از برنامه چه کاری انجام می دهد.

کامنت می تواند در خط جداگانه یا بعد از یک دستور نوشته شود. با این حال، بهتر است توضیحات مهم را در خط جداگانه قرار دهید تا کد خواناتر باشد.

۷. کامنت چند خطی

اگر بخواهیم توضیحی طولانی تر بنویسیم، می توانیم از /* برای شروع و */ برای پایان کامنت استفاده کنیم.

مثال

/*
  این برنامه یک LED را به صورت متناوب
  روشن و خاموش می کند.

  LED به پایه دیجیتال شماره ۸ متصل است.
  زمان روشن و خاموش بودن در هر مرحله یک ثانیه است.
*/

const int ledPin = 8;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  digitalWrite(ledPin, HIGH);
  delay(1000);

  digitalWrite(ledPin, LOW);
  delay(1000);
}

تمام متنی که بین /* و */ قرار دارد، کامنت محسوب می شود؛ حتی اگر چند خط داشته باشد.

نکته: کامنت های چند خطی معمولی را به صورت تو در تو در یکدیگر قرار ندهید؛ زیرا این نوع استفاده می تواند باعث خطای کامپایل شود.

۸. کامنت گذاری برای غیرفعال کردن موقت کد

گاهی هنگام آزمایش برنامه می خواهیم اجرای یک دستور را به طور موقت متوقف کنیم، بدون آنکه آن را کاملا حذف کنیم.

برای این کار می توانیم ابتدای دستور از // استفاده کنیم.

مثال

const int ledPin = 8;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  digitalWrite(ledPin, HIGH);

  // Serial.println("LED is ON");

  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
}

در این برنامه، دستور Serial.println() به دلیل کامنت شدن اجرا نمی شود.

این روش برای آزمایش بخش های مختلف برنامه مفید است؛ اما بهتر است کامنت های موقت و غیرضروری را پس از پایان عیب یابی پاک کنید تا کد شلوغ نشود.

۹. استفاده از کامنت برای مستندسازی پایه ها

در پروژه های الکترونیکی، مشخص کردن اتصال پایه ها اهمیت زیادی دارد. اگر شماره پایه ها فقط در بخش های مختلف کد نوشته شده باشند، ممکن است بعدا فراموش کنیم هر پایه به کدام قطعه متصل است.

مثال

// اتصالات سخت افزاری:
// LED قرمز: پایه ۸
// کلید: پایه ۲ و GND
// سنسور آنالوگ: پایه A0

const int ledPin = 8;
const int buttonPin = 2;
const int sensorPin = A0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  int buttonState = digitalRead(buttonPin);
  int sensorValue = analogRead(sensorPin);

  Serial.print("Button: ");
  Serial.print(buttonState);

  Serial.print(" | Sensor: ");
  Serial.println(sensorValue);

  delay(200);
}

در این مثال، کامنت های ابتدای برنامه اتصالات سخت افزاری را توضیح می دهند. این کار هنگام تعمیر، توسعه یا انتقال پروژه به برد دیگر بسیار مفید است.

۱۰. اصول نوشتن کامنت های مفید

هر کامنتی لزوما باعث بهتر شدن برنامه نمی شود. کامنت خوب باید اطلاعاتی بدهد که از روی خود کد به سادگی مشخص نیست.

کامنت مناسب

// به دلیل استفاده از مقاومت Pull-up داخلی، فشردن کلید مقدار LOW ایجاد می کند.
pinMode(buttonPin, INPUT_PULLUP);

این کامنت علت رفتار کلید را توضیح می دهد.

کامنت کم فایده

// افزایش مقدار i به اندازه یک واحد
i++;

این کامنت فقط همان چیزی را تکرار می کند که از روی دستور مشخص است.

بهتر است کامنت ها بیشتر به این پرسش ها پاسخ دهند:

  • چرا این روش انتخاب شده است؟
  • این بخش چه هدفی دارد؟
  • کدام اتصال سخت افزاری برای اجرای صحیح برنامه ضروری است؟
  • چه محدودیت یا نکته ای باید هنگام تغییر کد رعایت شود؟

۱۱. ساختار پیشنهادی برای یک اسکچ مرتب

با بزرگ تر شدن پروژه، بهتر است بخش های برنامه را با نظم مشخصی بنویسیم.

یک ساختار ساده و قابل توسعه می تواند شامل بخش های زیر باشد:

  1. توضیح کوتاه درباره هدف برنامه
  2. تعریف کتابخانه های مورد نیاز
  3. تعریف ثابت ها و متغیرهای سراسری
  4. تابع setup()
  5. تابع loop()
  6. توابع کمکی برای انجام کارهای جداگانه

مثال کامل

/*
  نام پروژه: کنترل LED
  هدف: روشن و خاموش کردن متناوب LED
  اتصال: LED به پایه دیجیتال ۸ از طریق مقاومت مناسب
*/

const int ledPin = 8;
const unsigned long interval = 1000;

void setup() {
  // تنظیم اولیه پایه
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // اجرای چرخه روشن و خاموش شدن
  turnLedOn();
  delay(interval);

  turnLedOff();
  delay(interval);
}

void turnLedOn() {
  digitalWrite(ledPin, HIGH);
}

void turnLedOff() {
  digitalWrite(ledPin, LOW);
}

در این برنامه، دو تابع کمکی به نام های turnLedOn() و turnLedOff() تعریف شده اند. هر تابع یک وظیفه مشخص دارد و باعث می شود کد اصلی خواناتر شود.

این توابع را می توان پس از loop() تعریف کرد. در اسکچ های معمول آردوینو، محیط Arduino IDE به طور خودکار اعلان اولیه توابع را برای بسیاری از برنامه ها ایجاد می کند.

۱۲. آیا loop همیشه باید دستور داشته باشد؟

خیر. از نظر ساختاری، می توان تابع loop() را خالی نوشت:

void setup() {
  Serial.begin(9600);
}

void loop() {
}

در این مثال، پس از راه اندازی ارتباط سریال، دستور دیگری اجرا نمی شود.

با این حال، در بیشتر پروژه ها لازم است loop() کارهایی مانند خواندن سنسور، بررسی کلیدها یا به روزرسانی خروجی ها را انجام دهد.

همچنین، اگر درون loop() از حلقه بی پایان یا دستورهایی استفاده کنید که اجرای برنامه را برای مدت طولانی متوقف می کنند، دیگر دستورهای آن تابع در زمان مورد انتظار اجرا نخواهند شد.

۱۳. اشتباهات رایج در ساختار اسکچ

اشتباه اول: فراموش کردن آکولادها

void setup()
  pinMode(8, OUTPUT);

این کد از نظر ساختار صحیح نیست؛ زیرا بدنه تابع باید داخل آکولاد باز و بسته قرار بگیرد.

شکل صحیح:

void setup() {
  pinMode(8, OUTPUT);
}

اشتباه دوم: نوشتن نام تابع به شکل نادرست

نام تابع های اصلی باید دقیقا به شکل زیر نوشته شوند:

void setup() {
}

void loop() {
}

نوشتن نام هایی مانند Setup() یا Loop() به جای این نام ها صحیح نیست؛ زیرا حروف بزرگ و کوچک در زبان C++ تفاوت دارند.

اشتباه سوم: فراموش کردن نقطه ویرگول

در بسیاری از دستورهای زبان C++ باید در پایان دستور از ; استفاده شود.

int ledPin = 8;

اما بعد از تعریف سرآیند تابع، مانند void setup(), نقطه ویرگول قرار نمی دهیم.

اشتباه چهارم: تصور اینکه کامنت اجرا می شود

// digitalWrite(8, HIGH);

دستور بالا اجرا نمی شود؛ زیرا به صورت کامنت نوشته شده است.

اشتباه پنجم: قرار دادن تنظیمات تکراری در loop

اگر تنظیمات اولیه ای مانند Serial.begin() یا pinMode() در هر دور loop() تکرار شوند، ممکن است باعث رفتار نامناسب یا پیچیدگی غیرضروری شوند. این تنظیمات معمولا باید در setup() انجام شوند؛ مگر آنکه دلیل مشخصی برای تکرارشان وجود داشته باشد.

۱۴. تمرین عملی

برنامه ای بنویسید که کارهای زیر را انجام دهد:

  • یک LED به پایه ۸ متصل باشد.
  • در setup() پایه ۸ به عنوان خروجی تنظیم شود.
  • در loop() LED به مدت ۵۰۰ میلی ثانیه روشن و سپس به مدت ۵۰۰ میلی ثانیه خاموش شود.
  • در ابتدای برنامه یک کامنت چند خطی برای توضیح هدف برنامه بنویسید.
  • کنار هر دستور مهم، در صورت نیاز، یک کامنت کوتاه اضافه کنید.

پس از نوشتن برنامه، تلاش کنید با خواندن کامنت ها و بدون اجرای کد، هدف و روش کار آن را توضیح دهید.

جمع بندی

هر اسکچ آردوینو معمولا با دو تابع اصلی setup() و loop() سازمان دهی می شود. تابع setup() برای راه اندازی اولیه و تابع loop() برای اجرای پیوسته منطق برنامه به کار می رود.

کامنت گذاری نیز روشی ساده اما مهم برای افزایش خوانایی، مستندسازی اتصالات سخت افزاری و آسان تر کردن عیب یابی است.

با رعایت ساختار منظم، نام گذاری روشن و کامنت های مفید، می توانید برنامه هایی بنویسید که نه تنها امروز قابل فهم باشند، بلکه در آینده نیز به سادگی قابل اصلاح و توسعه باقی بمانند.

نمونه کد :

/*
  پروژه: چشمک زن LED
  هدف: روشن و خاموش کردن LED با فاصله زمانی یک ثانیه
*/

const int ledPin = 8;

void setup() {
  pinMode(ledPin, OUTPUT); // تنظیم پایه به عنوان خروجی
}

void loop() {
  digitalWrite(ledPin, HIGH); // روشن کردن LED
  delay(1000);

  digitalWrite(ledPin, LOW);  // خاموش کردن LED
  delay(1000);
}