در این راهنما، نحوه استفاده از در این راهنما، نحوه استفاده از وقفه ها و تایمرهای ESP8266 در Arduino IDE را خواهید آموخت. وقفه ها به شما این امکان را می دهند که تغییرات در وضعیت GPIO را بدون نیاز به بررسی مداوم مقدار فعلی آن تشخیص دهید. با وقفه ها، هنگامی که یک تغییر حالت را شناسایی کنند، یک event راه اندازی می شود (در واقع یک تابع فراخوانی می شود). در Arduino IDE را خواهید آموخت. وقفه ها به شما این امکان را می دهند که تغییرات در وضعیت GPIO را بدون نیاز به بررسی مداوم مقدار فعلی آن تشخیص دهید. با وقفه ها، هنگامی که یک تغییر حالت را شناسایی کنند، یک event راه اندازی می شود (در واقع یک تابع فراخوانی می شود).

کد نویسی و نحوه عملکرد کامل وقفه ها و تایمرهای ESP8266 با مثال عملی

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

ما حرکت افراد را با استفاده از یک حسگر حرکتی PIR تشخیص خواهیم داد: هنگامی که حرکت تشخیص داده می‌شود، ESP8266 یک تایمر را راه‌اندازی می‌کند و یک LED را برای تعداد ثانیه‌های از پیش تعریف شده روشن می‌کند. وقتی شمارش معکوس تایمر تمام شد، LED به طور خودکار خاموش می شود.

attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);

قبل از ادامه این آموزش، باید افزونه بردهای  ESP8266 را در Arduino IDE خود نصب کنید.اگر قبلاً این کار را نکرده اید این آموزش را دنبال کنید:

  نصب بردهای ESP8266 در آردوینو IDE (ویندوز – مک – لینوکس)

معرفی وقفه های ESP8266

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

با وقفه ها، نیازی به بررسی مداوم مقدار پین فعلی ندارید. هنگامی که یک تغییر شناسایی می شود، یک event راه اندازی می شود – یک تابع فراخوانی می شود. این تابع روتین سرویس وقفه (ISR) نامیده می شود.

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

کد نویسی و نحوه عملکرد کامل وقفه ها و تایمرهای ESP8266 با مثال عملی

این ویژگی در پروژه های ما برای زمانی که سنسور حرکت را تشخیص میدهد مفید است تا بعد از تشخیص حرکت برنامه مرتبط با آن اجرا شود.

تابع () attachInterrupt

برای تنظیم یک وقفه در آردوینو IDE، از تابع attachInterrupt() استفاده می کنید که به عنوان آرگومان باید مقادیر زیر را معرفی برای این تابع کنید:

پین وقفه GPIO، نام تابعی که باید اجرا شود و حالت ایجاد وقفه

attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);

پین وقفه GPIO

اولین آرگومان یک وقفه GPIO است. شما باید از digitalPinToInterrupt (GPIO) برای تنظیم GPIO واقعی به عنوان پین وقفه استفاده کنید. به عنوان مثال، اگر می خواهید از GPIO 14 به عنوان وقفه استفاده کنید، باید به صورت زیر تعریف شود.

digitalPinToInterrupt(14)

ESP8266 از وقفه در همه GPIO به جز GPIO16 پشتیبانی می کند.

ISR

آرگومان دوم تابع attachInterrupt() نام تابعی است که هر بار که وقفه ایجاد می شود روتین سرویس وقفه (ISR) فراخوانی می شود.

عملکرد ISR باید تا حد امکان ساده باشد، بنابراین پردازنده به سرعت به اجرای برنامه اصلی باز می گردد.

بهترین روش این است که با استفاده از یک متغیر سراسری به کد اصلی سیگنال دهید که وقفه اتفاق افتاده است و در داخل loop() آن پرچم را بررسی و پاک کنید و کد را اجرا کنید.

ISR ها باید قبل از تعریف تابع، ICACHE_RAM_ATTR داشته باشند تا کد وقفه را در RAM اجرا کنند.

حالت های وقفه

آرگومان سوم حالت وقفه است که 3 حالت مختلف وجود دارد:

CHANGE: برای ایجاد وقفه هر زمان که وضعیت پین تغییر می‌کند – برای مثال از HIGH به LOW یا LOW به HIGH.

FALLING: برای زمانی که پین از HIGH به LOW می رود فعال می‌شود.

RISING: هنگامی که پین از LOW به HIGH می‌رود فعال می‌شود.

برای مثال ما از حالت RISING استفاده می کنیم، زیرا وقتی سنسور حرکت PIR، حرکت را تشخیص می دهد، GPIO که به آن متصل است از LOW به HIGH تغییر حالت میدهد.

معرفی تایمر ESP8266

کد نویسی و نحوه عملکرد کامل وقفه ها و تایمرهای ESP8266 با مثال عملی

برای این آموزش، ما از تایمر استفاده خواهیم کرد. ما می خواهیم LED برای تعداد ثانیه های از پیش تعیین شده پس از تشخیص حرکت روشن بماند. به‌جای استفاده از تابع delay() که کد شما را مسدود می‌کند و به شما اجازه انجام هیچ کار دیگری را برای چند ثانیه معین نمی‌کند، از یک تایمر استفاده می‌کنیم.

تفاوت delay() و millis()

تابع delay() یک عدد int را به عنوان آرگومان می پذیرد. این عدد نشان دهنده مدت زمانی است که برنامه باید منتظر بماند تا به خط بعدی کد منتقل شود.

delay(time in milliseconds);

وقتی Delay(1000) را فراخوانی می کنید، برنامه شما به مدت 1 ثانیه در آن خط متوقف می شود. ()delay یک تابع blocking است. توابع blocking برنامه را از انجام هر کار دیگری تا زمانی که آن کار خاص کامل نشده است، باز می دارد. اگر نیاز دارید که چندین کار همزمان انجام شوند، نمی توانید از delay() استفاده کنید. برای اکثر پروژه ها باید از تاخیر استفاده نکنید و به جای آن از تایمر استفاده کنید.

با استفاده از تابعی به نام millis() می‌توانید تعداد میلی‌ثانیه‌هایی را که از اولین شروع برنامه گذشته است را برگردانید.

millis();

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

پروژه چشمک زدن LED با استفاده از millis() (بدون تاخیر)

اگر با تابع ()millis آشنایی ندارید، توصیه می کنیم این بخش را بخوانید. اگر قبلاً با تایمرها و نحوه کار آنها آشنایی دارید، می توانید به پروژه حسگر حرکت PIR بروید.

قطعه کد زیر نشان می دهد که چگونه می توانید از تابع millis () برای ایجاد یک پروژه چشمک زن استفاده کنید. نحوه کار این پروژه به این صورت است که یک LED را به مدت 1000 میلی ثانیه روشن می کند و سپس آن را خاموش می کند.

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

// constants won't change. Used here to set a pin number :
const int ledPin =  26;      // the number of the LED pin

// Variables will change :
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change :
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

نحوه کار کد

بیایید نگاهی دقیق تر به این برنامه چشمک زن بیندازیم که بدون تابع delay() کار می کند (به جای آن از تابع millis() استفاده می کند.)

اساساً این کد زمان ثبت شده قبلی (previousMillis) را از زمان فعلی (currentMillis) کم می کند. اگر باقیمانده بیشتر از فاصله زمانی باشد (در این مورد 1000 میلی ثانیه)، برنامه متغیر قبلیMillis را به زمان فعلی به روز می کند و LED را روشن یا خاموش می کند.

if (currentMillis - previousMillis >= interval) {
  // save the last time you blinked the LED
  previousMillis = currentMillis;
  (...)

از آنجایی که این کد non blocking است، هر کدی که خارج از عبارت if قرار دارد باید به طور معمول کار کند.

اکنون باید بتوانید درک کنید که می توانید وظایف دیگری را به تابع loop() خود اضافه کنید و کد شما همچنان LED را هر یک ثانیه چشمک می زند.

می توانید این کد را در ESP8266 خود آپلود کنید تا آن را آزمایش کنید. LED روی برد باید هر ثانیه چشمک بزند.

کد نویسی و نحوه عملکرد کامل وقفه ها و تایمرهای ESP8266 با مثال عملی

پروژه ESP8266 با سنسور حرکت PIR

در این بخش، نحوه تشخیص حرکت با سنسور حرکت PIR با استفاده از وقفه ها و تایمرها را یاد خواهید گرفت.

لوازم مورد نیاز

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

  • برد ESP8266
  • ماژول سنسور PIR
  • 5mm LED
  • مقاومت 330 اهم
  • بردبورد
  • سیم های اتصال

دیاگرام شماتیک

سنسور حرکت PIR و یک LED را به ESP8266 خود متصل کنید. ما LED را به GPIO 12 (D6) و پین داده سنسور حرکت PIR را به GPIO 14 (D5) وصل می کنیم.

کد نویسی و نحوه عملکرد کامل وقفه ها و تایمرهای ESP8266 با مثال عملی

راهنمای جامع پین های ESP8266 (کدام پین ها را میتوانیم استفاده کنیم؟)

مهم: سنسور حرکت Mini AM312 PIR مورد استفاده در این پروژه در ولتاژ 3.3 ولت کار می کند. با این حال، اگر از سنسور حرکت PIR دیگری مانند HC-SR501 استفاده می کنید، در ولتاژ 5 ولت کار می کند که باید با استفاده از پین Vin آن را تغذیه کنید.

شکل زیر سنسور حرکتی AM312 PIR را نشان می دهد.

کد نویسی و نحوه عملکرد کامل وقفه ها و تایمرهای ESP8266 با مثال عملی

کد

پس از سیم کشی مدار همانطور که در دیاگرام شماتیک نشان داده شده است، کد زیر را در Arduino IDE خود کپی کنید.

می‌توانید کد را همانطور که هست آپلود کنید یا می‌توانید تعداد ثانیه‌هایی که LED بعد از تشخیص حرکت روشن می‌شود را تغییر دهید. به سادگی متغیر timeSeconds را با تعداد ثانیه هایی که می خواهید تغییر دهید.

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

#define timeSeconds 10

// Set GPIOs for LED and PIR Motion Sensor
const int led = 12;
const int motionSensor = 14;

// Timer: Auxiliary variables
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;

// Checks if motion was detected, sets LED HIGH and starts a timer
ICACHE_RAM_ATTR void detectsMovement() {
  Serial.println("MOTION DETECTED!!!");
  digitalWrite(led, HIGH);
  startTimer = true;
  lastTrigger = millis();
}

void setup() {
  // Serial port for debugging purposes
  Serial.begin(115200);
  
  // PIR Motion Sensor mode INPUT_PULLUP
  pinMode(motionSensor, INPUT_PULLUP);
  // Set motionSensor pin as interrupt, assign interrupt function and set RISING mode
  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

  // Set LED to LOW
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
}

void loop() {
  // Current time
  now = millis();
  // Turn off the LED after the number of seconds defined in the timeSeconds variable
  if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
    Serial.println("Motion stopped...");
    digitalWrite(led, LOW);
    startTimer = false;
  }
}

نحوه کار کد

بیایید نگاهی به کد بیندازیم.

با اختصاص دو پین GPIO به متغیرهای led و motionSensor شروع میکنیم.

const int led = 12;
const int motionSensor = 14;

سپس، متغیرهایی را ایجاد میسازیم که به ما امکان می دهد تایمر را تنظیم کنیم تا پس از تشخیص حرکت، LED خاموش شود.

unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;

متغیر now زمان جاری را نگه می دارد. متغیر lastTrigger زمانی را نگه می دارد که حسگر PIR حرکت را تشخیص می دهد. startTimer یک متغیر بولی است که زمانی که حرکت تشخیص داده می شود تایمر را شروع می کند.

setup()

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

Serial.begin(115200);

سنسور حرکت PIR را به عنوان INPUT_PULLUP تنظیم کنید.

pinMode(motionSensor, INPUT_PULLUP);

برای تنظیم پین سنسور PIR به عنوان یک وقفه، از تابع attachInterrupt() همانطور که قبلا توضیح داده شد استفاده کنید.

attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

پینی که حرکت را در حالت RISING تشخیص می دهد GPIO 14 است و تابع detectsMovement() را  فراخوانی می کند.

LED یک OUTPUT است که حالت آن از LOW شروع می شود.

pinMode(led, OUTPUT);
digitalWrite(led, LOW);

loop()

تابع loop() مدام بارها و بارها اجرا می شود. در هر اجرا، متغیر now با زمان جاری به روز می شود.

now = millis();

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

تابع detectsMovement() پیامی را در مانیتور سریال چاپ می کند، LED را روشن می کند، متغیر بولین startTimer را روی true تنظیم می کند و متغیر lastTrigger را با زمان فعلی به روز می کند.

ICACHE_RAM_ATTR void detectsMovement() {
  Serial.println("MOTION DETECTED!!!");
  digitalWrite(led, HIGH);
  startTimer = true;
  lastTrigger = millis();
}

پس از این مرحله، کد به loop() برمی گردد. این بار شرط متغیر startTimer درست است. بنابراین، هنگامی که زمان تعیین شده بر حسب ثانیه گذشته است (از زمانی که حرکت شناسایی شده است)، عبارت if زیر درست خواهد بود.

if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
  Serial.println("Motion stopped…");
  digitalWrite(led, LOW);
  startTimer = false;
}

پیام “Motion stopped…” در مانیتور سریال چاپ می شود، LED خاموش می شود و متغیر startTimer روی false تنظیم می شود.

نتیجه

کد را در ESP8266 خود آپلود کنید. مطمئن شوید که برد و پورت COM مناسب را انتخاب کرده اید.

مانیتور سریال را با نرخ باود 115200 باز کنید.

دست خود را جلوی سنسور PIR حرکت دهید. LED باید روشن شود و پیامی در مانیتور سریال  “MOTION DETECTED!!!” چاپ می شود. بعد از 10 ثانیه LED باید خاموش شود.

کد نویسی و نحوه عملکرد کامل وقفه ها و تایمرهای ESP8266 با مثال عملی

جمع بندی

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

آموزش های مرتبط دیگر با ESP8266:

منبع: https://randomnerdtutorials.com/interrupts-timers-esp8266-arduino-ide-nodemcu/
برای امتیاز به این نوشته کلیک کنید!
[کل: 0 میانگین: 0]

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *