آموزش شی گرایی آردوینو (ساخت کلاس و آبجکت – جامع)

یک کلاس مجموعهای از توابع و متغیرهاست که میتوان از آن برای انجام وظایف خاص برنامهنویسی استفاده کرد. در برنامهنویسی آردوینو، یک کلاس در واقع همان چیزی است که به آن «کتابخانه» گفته میشود.
کلاس مانند یک الگو (template) است که به شما اجازه میدهد کد را بدون نیاز به تایپ مجدد، چندین بار استفاده کنید. برای مثال فرض کنید باید 20 سند بسازید که همگی فونت، سربرگ و حاشیههای یکسانی دارند. تغییر دادن قالب هر 20 سند بهصورت جداگانه بسیار وقتگیر خواهد بود. در عوض میتوانید یک سند الگو با فونت، سربرگ و حاشیههای درست بسازید، سپس فقط متن را اضافه کرده و آن را بهعنوان فایل جداگانه ذخیره کنید.
کلاسها مانند همان سند الگو هستند. آنها الگوهایی از کد هستند که برای چیزی به نام «شیء» (object) استفاده میشوند. میتوانید کلاس را مثل سند الگو و شیء را مثل سند نهایی در نظر بگیرید. اشیاء به شما اجازه میدهند به توابع و متغیرهای داخل کلاس دسترسی داشته باشید. به یک شیء گاهی «نمونهای» (instance) از یک کلاس گفته میشود.
ویدیوی آموزشی مربوط به این درس را میتوانید در اینجا مشاهده کنید:
کلاس و آبجکت در کد های آردوینو
برای نشان دادن میزان کاربردی بودن کلاسها و اشیاء، به پروژهای نگاه کنید که در آن دو LED با نرخ های متفاوت چشمک میزنند، بدون آنکه از کلاس یا شیء استفاده شده باشد.
در این پروژه به قطعات زیر نیاز دارید:
- Arduino Uno
- سیم جامپر
- برد بورد
- دو مقاومت 220 اهم
- دو LED
دیاگرام سیمکشی پروژه در تصویر زیر نشان داده شده است:
کد زیر باعث میشود LED سبز هر دو ثانیه یکبار روشن و خاموش شود و LED قرمز هر یک ثانیه یکبار چشمک بزند:
int redLED = 10;
int greenLED = 11;
void setup() {
pinMode(redLED, OUTPUT);
pinMode(greenLED, OUTPUT);
}
void loop() {
digitalWrite(greenLED, HIGH);
delay(2000);
digitalWrite(greenLED, LOW);
delay(2000);
digitalWrite(redLED, HIGH);
delay(1000);
digitalWrite(redLED, LOW);
delay(1000);
}
توضیح کد
در بالای اسکچ، متغیرهایی برای پینهای redLED و greenLED اعلان میکنیم. در setup()، حالت هر پین LED را بهعنوان OUTPUT تنظیم میکنیم. سپس در loop()، ابتدا greenLED را با digitalWrite در حالت HIGH قرار میدهیم، سپس به مدت 2000 میلیثانیه تأخیر میدهیم، آن را LOW میکنیم و دوباره 2000 میلیثانیه تأخیر میدهیم.
سپس redLED را HIGH میکنیم، به مدت 1000 میلیثانیه تأخیر میدهیم، آن را LOW میکنیم و دوباره 1000 میلیثانیه تأخیر میدهیم.
این مقدار کد برای کاری به سادگی چشمکزدن چند LED زیاد است. میتوانیم این برنامه را با استفاده از کلاسها و اشیاء (آبجکت) خیلی کوچکتر کنیم.
چشمک زدن LED ها با کلاسها و آبجکت
بیایید ببینیم چگونه میتوان LEDها را در اسکچ بالا با کلاسها و اشیاء چشمکزن کرد. ابتدا کد کلاس را مینویسیم، و بعداً در اسکچ شیءها را ایجاد میکنیم تا به کد داخل کلاس دسترسی پیدا کنند.
برای ساخت یک کلاس آردوینو، باید دو فایل ایجاد کنیم: یک فایل هدر و یک فایل سورس. بعداً اسکچی که از این کلاس استفاده میکند را مینویسیم، اما حالا از فایل هدر شروع میکنیم.
فایل هدر
فایل هدر جایی است که کلاس در آن اعلان میشود. همچنین جایی است که توابع و متغیرهای مورد استفاده داخل کلاس اعلان میشوند. بیایید مرحلهبهمرحله نحوه نوشتن یک فایل هدر را مرور کنیم.
اولین قطعه کدی که در فایل هدر نیاز است، چیزی است که به آن include guard گفته میشود. این include guard از خطاهای برنامه جلوگیری میکند اگر کتابخانه بهطور تصادفی بیش از یکبار در اسکچ وارد شود:
#ifndef MyClass_h #define MyClass_h // code goes here #endif
شما میتوانید نام فایل هدر را هر چیزی بگذارید اما در این مقاله نام آن MyClass خواهد بود. فایل هدر باید با پسوند .h ذخیره شود. در #ifndef و #define، نام فایل هدر باید با _h دنبال شود.
بعد باید کتابخانه Arduino.h را وارد کنیم. Arduino.h کتابخانه هستهای است که شامل تمام توابع داخلی آردوینو میشود. وارد کردن آن در فایل هدر این توابع را برای استفاده در کلاس در دسترس قرار میدهد:
#ifndef MyClass_h #define MyClass_h #include "Arduino.h" #endif
حالا میتوانیم کلاس را تعریف کنیم. برای تعریف یک کلاس، class را نوشته و سپس نام کلاس را قرار دهید:
#ifndef MyClass_h
#define MyClass_h
#include "Arduino.h"
class MyClass {
// class code goes here
};
#endif
نام کلاس در این مثال MyClass است. کد کلاس داخل آکولادها قرار میگیرد. توابع و متغیرهای داخل یک کلاس میتوانند عمومی (public) یا خصوصی (private) باشند:
#ifndef MyClass_h
#define MyClass_h
#include "Arduino.h"
class MyClass {
public:
private:
};
#endif
کلیدواژههای public و private بهعنوان مشخصکنندههای دسترسی شناخته میشوند. توابع و متغیرهای public میتوانند در داخل اسکچ استفاده شوند، اما توابع و متغیرهای private فقط داخل خود کلاس قابل استفاده هستند.
هر کلاس به تابع خاصی به نام سازنده (Constructor) نیاز دارد. سازنده تعیین میکند زمانی که یک شیء ساخته میشود چه اتفاقی بیفتد. وقتی اسکچ این پروژه را بنویسیم، قرار است یک شیء برای LED سبز و شیء دیگری برای LED قرمز ایجاد کنیم. سازنده به آردوینو میگوید هنگام ایجاد این اشیاء چه کاری انجام دهد.
کد واقعی سازنده را هنگام تعریف آن در فایل سورس خواهیم نوشت، اما باید آن را در فایل هدر نیز اعلان کنیم. از آنجا که اشیاء در اسکچ ساخته میشوند، اعلان سازنده باید در بخش public باشد و همان نام کلاس را داشته باشد. در این مثال نام کلاس MyClass است، پس اعلان سازنده به صورت زیر خواهد بود:
#ifndef MyClass_h
#define MyClass_h
#include "Arduino.h"
class MyClass {
public:
MyClass();
private:
};
#endif
تعیین جزئی تر موارد پروژه
در این پروژه دو LED به آردوینو متصل داریم، بنابراین باید حالت هر پین LED را بهعنوان OUTPUT تنظیم کنیم. زمانی که کد سازنده را در فایل سورس مینویسیم، تابع pinMode() را داخل آن قرار میدهیم. تابع pinMode() یک شماره پین را بهعنوان پارامتر اول میگیرد. باید این شماره پین را به سازنده ارسال کنیم تا pinMode() بتواند از آن استفاده کند. برای این کار، متغیری از نوع int به نام pin داخل پرانتزهای اعلان سازنده تعریف میکنیم:
#ifndef MyClass_h
#define MyClass_h
#include "Arduino.h"
class MyClass {
public:
MyClass(int pin);
private:
};
#endif
اکنون باید تابعی را اعلان کنیم که LEDها را چشمکزن کند. توابع داخل کلاسها به همان روشی اعلان میشوند که در اسکچها نوشته میشوند. ابتدا نوع بازگشت (return type) را مشخص میکنیم. از آنجا که این تابع هیچ مقداری برنمیگرداند، نوع بازگشت آن void است. سپس نام تابع را مینویسیم. فرض کنیم نام آن را myFunction() بگذاریم:
#ifndef MyClass_h
#define MyClass_h
#include "Arduino.h"
class MyClass {
public:
MyClass(int pin);
void myFunction();
private:
};
#endif
از آنجا که این تابع باعث چشمکزدن LEDها میشود، بهتر است بتوانیم سرعت چشمکزدن را کنترل کنیم. میتوانیم با ارسال یک متغیر به تابع myFunction()، طول زمان تأخیر را مشخص کنیم. زمانی که تابع در اسکچ استفاده شود، یک پارامتر خواهد داشت که مدت زمان تأخیر را تعیین میکند.
بیایید متغیری در تابع myFunction() تعریف کنیم تا این پارامتر را ذخیره کند و نام آن را blinkRate بگذاریم:
#ifndef MyClass_h
#define MyClass_h
#include "Arduino.h"
class MyClass {
public:
MyClass(int pin);
void myFunction(int blinkRate);
private:
};
#endif
اکنون هر آرگومانی که در اسکچ در تابع myFunction() استفاده شود، به کد این تابع در فایل سورس ارسال خواهد شد.
تابع myFunction() باید بداند شماره پینهای متصل به LEDها چیست، بنابراین باید متغیری برای ذخیره شماره پین تعریف کنیم. از آنجا که این متغیر فقط در داخل کلاس استفاده میشود، آن را بهصورت private تعریف میکنیم. طبق قرارداد نامگذاری در C++، متغیرهای private معمولاً با یک زیرخط (_) در ابتدای نام مشخص میشوند. پس متغیری به نام _pin تعریف میکنیم:
#ifndef MyClass_h
#define MyClass_h
#include "Arduino.h"
class MyClass {
public:
MyClass(int pin);
void myFunction(int blinkRate);
private:
int _pin;
};
#endif
این تمام کدی است که برای فایل هدر نیاز داریم.
برای ذخیره فایل هدر، به مسیر Documents > Arduino > Libraries بروید. یک پوشه جدید با همان نام کلاس (در این مثال MyClass) ایجاد کنید. فایل هدر را در این پوشه ذخیره کنید. فایل هدر باید با پسوند .h ذخیره شود. در این مثال، نام فایل هدر باید MyClass.h باشد.
فایل سورس (Source File)
فایل سورس شامل کد واقعی توابع است و مشخص میکند وقتی یک شیء ایجاد میشود، چه اتفاقی رخ میدهد. در ادامه، مرحلهبهمرحله نحوه نوشتن فایل سورس را بررسی میکنیم.
فایل سورس باید همان نام فایل هدر را داشته باشد، اما با پسوند .cpp.
اولین کاری که باید انجام دهیم، وارد کردن کتابخانه Arduino.h است تا بتوانیم از توابع داخلی آردوینو مانند pinMode()، digitalWrite() و delay() استفاده کنیم:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass() {
}
سپس باید فایل هدر را که قبلاً نوشتیم وارد کنیم. این کار را مانند سایر کلاسها انجام میدهیم — با دستور #include و نام فایل هدر درون کوتیشن:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass() {
}
اکنون میتوانیم تعریف سازنده را بنویسیم. این کد هر بار که در اسکچ یک شیء ایجاد میشود، اجرا خواهد شد. تعریف سازنده با نام کلاس شروع میشود، سپس دو علامت دونقطه (::). در این مثال، نام کلاس MyClass است. این دونقطهها عملگر تعیین محدوده (scope resolution operator) نامیده میشوند. بعد از آن نام سازندهای که در فایل هدر اعلان کردیم میآید. نام سازنده باید دقیقاً با نام کلاس یکی باشد. بنابراین نام سازنده MyClass() است:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass() {
}
اشیائی که با این کلاس ایجاد میکنیم، نمایانگر پینهای آردوینو متصل به LEDهای قرمز و سبز هستند. هر بار که یک شیء LED ساخته میشود، باید pinMode() مربوط به آن پین را روی OUTPUT تنظیم کنیم. برای این کار، شماره پین متصل به LED را از طریق سازنده به کلاس ارسال میکنیم، با تعریف متغیری به نام pin داخل سازنده:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass(int pin) {
}
در داخل سازنده، تنها کاری که باید انجام دهیم تنظیم حالت پین LEDها است. شماره پینی که در متغیر pin ذخیره میشود، هنگام ساخت شیء به سازنده منتقل خواهد شد، بنابراین آن را با دستور زیر به خروجی تنظیم میکنیم:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass(int pin) {
pinMode(pin, OUTPUT);
}
به یاد دارید که در فایل هدر متغیر خصوصی _pin را تعریف کرده بودیم؟ حالا باید آن را برابر با متغیر pin قرار دهیم که در سازنده تعریف کردهایم. به این ترتیب، مقدار شماره پین در هر دو متغیر ذخیره میشود:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass(int pin) {
pinMode(pin, OUTPUT);
_pin = pin;
}
حالا بیایید تابعی بنویسیم که باعث چشمکزدن LEDها میشود. برای تعریف یک تابع، ابتدا نوع بازگشتی (return type) آن را مینویسیم. چون در این مثال تابع هیچ مقداری برنمیگرداند، نوع بازگشت آن void است.
نام کلاس ها
سپس نام کلاس را مینویسیم، بعد از آن دونقطهها (::) و سپس نام تابع. ما قبلاً تابع myFunction() را در فایل هدر اعلان کرده بودیم، پس حالا میتوانیم آن را تعریف کنیم:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass(int pin) {
pinMode(pin, OUTPUT);
_pin = pin;
}
void MyClass::myFunction() {
}
تابع myFunction() یک پارامتر برای تعیین مدت زمان چشمک LED دارد. ما در فایل هدر متغیری به نام blinkRate تعریف کردیم تا این مقدار را نگه دارد، اما باید آن را در فایل سورس نیز داخل پرانتزهای تابع تعریف کنیم:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass(int pin) {
pinMode(pin, OUTPUT);
_pin = pin;
}
void MyClass::myFunction(int blinkRate) {
}
این کار یک پارامتر برای تابع myFunction() ایجاد میکند. حالا وقتی تابع را در اسکچ فراخوانی کنیم، میتوانیم یک آرگومان (مثل blinkRate) به آن ارسال کنیم.
اکنون میتوانیم کد مربوط به چشمکزدن LED را بنویسیم. این همان کد معمولی است که برای چشمک زدن LEDها استفاده میشود:
#include "Arduino.h"
#include "MyClass.h"
MyClass::MyClass(int pin) {
pinMode(pin, OUTPUT);
_pin = pin;
}
void MyClass::myFunction(int blinkRate) {
digitalWrite(_pin, HIGH);
delay(blinkRate);
digitalWrite(_pin, LOW);
delay(blinkRate);
}
ابتدا متغیر خصوصی _pin را در حالت HIGH قرار میدهیم. در سازنده، متغیر خصوصی _pin برابر با متغیر pin قرار داده شده است. بنابراین وقتی شیء LED ساخته میشود، شماره پین در متغیر pin ذخیره میشود و به سازنده ارسال میگردد. سپس همان شماره در متغیر خصوصی _pin ذخیره میشود و به تابع digitalWrite() منتقل میگردد.
مدت زمان تأخیر در اسکچ تعیین شده و در متغیر blinkRate ذخیره میشود، بنابراین این متغیر بهعنوان آرگومان تابع delay() استفاده میشود. سپس دوباره _pin را در حالت LOW قرار میدهیم و با همان blinkRate تأخیر میدهیم.
خب، این تمام چیزی است که برای فایل سورس نیاز داریم.
برای ذخیره فایل سورس، مسیر Documents > Arduino > Libraries را باز کنید. فایل سورس را در پوشهای که برای MyClass ایجاد کردهاید ذخیره کنید. نام فایل سورس باید با فایل هدر یکسان باشد، اما با پسوند .cpp. در این مثال، فایل سورس باید MyClass.cpp نام داشته باشد.
اکنون بیایید اسکچ را بنویسیم.
اسکچ (The Sketch)
در برنامهنویسی آردوینو، کلاسها دقیقاً مانند کتابخانهها (libraries) در نظر گرفته میشوند. بنابراین میتوان کلاس MyClass را همانطور که کتابخانهها را وارد میکنیم، در اسکچ وارد کرد:
#include <MyClass.h>
برای استفاده از توابعی که در کلاس تعریف شدهاند، باید چند شیء (object) ایجاد کنیم. در این پروژه ما دو LED چشمکزن داریم، پس یک شیء برای LED قرمز و یک شیء دیگر برای LED سبز ایجاد میکنیم.
برای ایجاد یک شیء، ابتدا نام کلاس را بنویسید، سپس نام شیء را. نام اشیاء میتواند دلخواه باشد. در این مثال، ما شیء redLED را برای LED قرمز و شیء greenLED را برای LED سبز میسازیم. هر شیء از طریق کلاس MyClass فراخوانی میشود:
#include <MyClass.h> MyClass redLED(10); MyClass greenLED(11);
وقتی سازنده (constructor) کلاس MyClass را در فایل سورس تعریف کردیم، متغیر عمومی pin را به آن پاس دادیم. اکنون میتوانیم شماره پین آردوینو را بهعنوان آرگومان شیء وارد کنیم و آن شماره پین به سازنده ارسال خواهد شد. در این مثال، سازنده پینهای 10 و 11 را بهعنوان خروجی تنظیم میکند.
بخش Loop
اکنون میتوانیم بخش loop() را بنویسیم.
در این بخش، LED قرمز و LED سبز را با استفاده از تابع myFunction() که در فایل سورس تعریف شده، چشمکزن میکنیم. برای استفاده از توابع داخل یک کلاس یا کتابخانه، نام شیء را بنویسید، سپس یک نقطه (.)، و در ادامه نام تابع مورد نظر را:
#include <MyClass.h>
MyClass redLED(10);
MyClass greenLED(11);
void setup() {
}
void loop() {
greenLED.myFunction(2000);
}
این کد تابع myFunction() از کلاس MyClass را فراخوانی کرده و آن را روی شیء greenLED اعمال میکند.
هنگامی که تابع myFunction() را تعریف کردیم، متغیر blinkRate را به آن پاس دادیم تا سرعت چشمکزدن LEDها را کنترل کند. وقتی مقدار 2000 را بهعنوان آرگومان میفرستیم، تابع delay() در داخل myFunction() به مدت 2000 میلیثانیه (دو ثانیه) تأخیر ایجاد میکند.
اکنون فقط کافی است تابع myFunction() را برای شیء redLED نیز فراخوانی کنیم. در این حالت، LED قرمز هر 1000 میلیثانیه (یک ثانیه) روشن و خاموش میشود:
#include <MyClass.h>
MyClass redLED(10);
MyClass greenLED(11);
void setup() {
}
void loop() {
greenLED.myFunction(2000);
redLED.myFunction(1000);
}
مزیت بزرگ استفاده از کلاسها و اشیاء این است که میتوان همان تابع را برای چندین شیء مختلف استفاده کرد. این کار کد شما را سادهتر، بهینهتر و خواناتر میکند، و همچنین دیگران نیز راحتتر میتوانند از آن استفاده کنند.
پس از آپلود کردن اسکچ روی آردوینو، باید LED قرمز را ببینید که هر ثانیه یک بار چشمک میزند و LED سبز هر دو ثانیه یک بار روشن و خاموش میشود.
امیدواریم این مقاله باعث شود استفاده از کلاسها و اشیاء در آردوینو برای شما آسانتر شود. اگر پرسشی دارید، حتماً در بخش نظرات مطرح کنید.









