مقدمه
در این قسمت از مجموعه آموزش های مخترع شوید، قصد داریم با معرفی و توضیح چگونگی عملکرد interrupt یا وقفه و تایمر در برد ESP8266، یک LED را کنترل می کنیم. برای این پروژه از یک سنسور حرکتی PIR نیز استفاده می شود. به طور کلی می خواهیم مداری را ببندیم که در آن با تشخیص حرکت توسط PIR, برد ESP8266 یک LED را بر اساس زمانی که از قبل تعیین کرده اید روشن می کند(با شروع تایمر) و پس از پایان timer ، ال ای دی به طور اتوماتیک خاموش می شود.
قبل از شروع این آموزش, باید برد ESP8266 خود را به نرم افزار Arduino IDE اضافه کنید. بدین هدف می توانید پست WiKi را در مطالعه کنید:
راهنمای نصب برد ESP8266 در Arduino IDE
تعریف ESP8266 Interrupt
وقفه ها برای ساخت برنامه های اتوماتیک در روند کد نویسی یک میکرکنترلر بسیار مفید و کاربردی می باشد. همچنین از interrupt یا وقفه برای هندل و حل کردن مشکلات تایمینیگ (زمان) ، استفاده می شود.
وقتی که وقفه اتفاق می افتد؛ پردازنده برای اجرای یک کار اجرای برنامه اصلی را متوقف می کند و پس از انجام آن کار, به برنامه اصلی برمی گردد.
تابع ()attachInterrupt
برای وارد کردن وقفه در Arduino IDE باید از تابع ()attachInterrupt استفاده کنید. این تابع؛ پین GPIO interrupt ، نام تابعی که باید اجرا شود ISR و mode را به عنوان آرگومان می پذیرد:
1 |
attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode); |
GPIO interrupt pin
- اولین آرگومان پین وقفه GPIO , می باشد. با توجه به دستور کد زیر، از digitalPinToInterrupt(GPIO) استفاده کردیم. به عنوان مثال در این آموزش ما پین 14 را ست کرده ایم:
1 |
digitalPinToInterrupt(14) |
ISR
- دومین آرگومان از تابع ()attachInterrupt ، اسم همان تابعی است که هر زمان وقفه یا interrupt شروع می شود، آن را صدا می زند service routine (ISR).
- قبل از تعریف تابع برای اجرای کد وقفه در RAM، دقت داشته باشید که آرگومان ISR باید ICACHE_RAM_ATTR را داشته باشد.
mode
سومین آرگومان, mode دارای 3 حالت مختلف است:
1.CHANGE: برای اجرای وقفه در هر تغییر و هرزمان_ به عنوان مثال: HIGH به LOW یا LOW به HIGH.
1.1 FALLING: برای زمانی که پین از HIGH به LOW می رود.
1.2 RISING: برای زمانی که پین از LOW به HIGH می رود.
به عنوان مثال از RISING mode استفاده خواهیم کرد. زیرا زمانی که سنسور PIR، حرکت را تشخیص می دهد, پین از LOW به HIGH می رود.
تعریف ESP8266 Timers
از آنجای که می خواهیم یک LED به مدت زمانی معلوم روشن باشد. به جای استفاده از تابع ()delay، که باعث بلاک شدن کد و عدم اجرای هر کاری می شود, از تایمرها استفاده می کنیم.
مقایسه ()delay با ()millis
- تابع ()delay، یک سیگنال از نوع عددی int را به عنوان یک آرگومان می پذیرد. این عدد نشان دهنده زمان بر حسب میلی ثانیه است که برنامه باید منتظر بماند تا به خط بعدی کد برود.
1 |
delay(time in milliseconds); |
- وقتی که delay(1000) را فراخوان می دهید، برنامه شما به مدت 1 ثانیه در آن خط متوقف می شود. ()delay یک تابع مسدود کننده است.
- با استفاده از تابع ()millis, می توانید تعداد میلی ثانیه ای که از زمان شروع برنامه گذشته است را برگردانید:
1 |
millis(); |
LED چشمک زن با استفاده از ()millis
در این بخش از آموزش قصد داریم تابع ()millis را معرفی کنیم. اگر آشنایی کافی با این توضیحات دارید به قسمت بعدی مقاله مراجعه کنید. دستوراتی که در زیر مشاهده می کنید با استفاده از تابع ()millis، یک LED را به مدت 1000 میلی ثانیه روشن و سپس خاموش می کند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
/********* Rui Santos Complete project details at https://ewink.ir/wiki *********/ // 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); } } |
شرح جزئیات کد
بیایید با دقت بیشتر به طرح LED چشمک زن که از millis به جای delay، استفاده شده, نگاهی بیاندازیم🤓
اساساً, این کد زمان ضبط شده قبلی (previousMillis) را از زمان فعلی (currentMillis) کم می کند. اگر مقدار باقی مانده بزرگتر از مقدار 1000 میلی ثانیه(در این مورد) باشد؛ برنامه متغیر previousMillis را به زمان فعلی به روزرسانی می کند و پس از آن LED خاموش یا روشن می شود.
1 2 3 4 |
if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; (...) |
- با توجه به اینکه این کد طرح مسدود نیست؛ پس هر دستور که خارج از آن قرار بگیرد می تواند اجرا شود.
- کد را در برد ESP8266 بارگذاری کرده و امتحان کنید. مشاهده خواهید کرد که LED تعبیه شد روی برد, هر یک ثانیه چشمک می زند.
ESP8266 NodeMCU با سسنور حرکتی PIR
چگونگی تشخیص حرکت توسط PIR با استفاده از تایمر و وقفه ها را در این قسمت فراخواهید گرفت.
قطعات مورد نیاز
برای خریدی به صرفه و امن به فروشگاه الکترونیکی ایوینک مراجعه کنید.
مدار شماتیک
طبق شکل زیر قطعات را به هم متصل کنید. ما LED را به GPIO 12 (D6) و پین دیتا سنسور PIR را به GPIO 14 (D5) وصل کرده ایم.
مطالعه مقاله پین های ESP8266 توصیه می شود.
کد
پس از بستن مدار بالا, کد را در نرم افزار Arduino ide کپی کنید. کد را بدون تغییر می توانید کپی کنید و یا اینکه مقدار ثانیه ای که LED پس از تشخیص حرکت روشن می باشد را تغییر دهید. این تغییر به سادگی با وارد زمان دلخواهتان در متغیر timeSeconds صورت می پذیرد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
/********* Rui Santos Complete project details at https://ewink.ir/wiki *********/ #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 و سنسور حرکتی شروع می شود:
1 2 |
const int led = 12; const int motionSensor = 14; |
- سپس, متغیرهایی ایجاد می کنیم که اجازه ست کردن تایمر برای خاموش شدن LED را می دهند:
1 2 3 |
unsigned long now = millis(); unsigned long lastTrigger = 0; boolean startTimer = false |
()setup
- پورت سریال با سرعت تبادل داده 115200 مقدار دهی را شروع می کند:
1 |
Serial.begin(115200); |
- سنسور حرکتی PIR به عنوان یک ورودی معرفی می شود:
1 |
pinMode(motionSensor, INPUT_PULLUP); |
پین GPIO 14 حرکت را تشخیص خواهد داد و تابع ()detectsMovement را بر مود RISING فرامی خواند.
- LED یک خروجی OUTPUT است که در حالت LOW شروع می کند:
1 2 |
pinMode(led, OUTPUT); digitalWrite(led, LOW); |
()loop
- این تابع باعث می شود که بارها و بارها اجرای برنامه تکرار شود ولی در هر loop، متغیر now، به روزرسانی زمان فعلی را انجام می دهد:
1 |
now = millis(); |
- تابع ()detectsMovement, یک پیغام در مانیتور سریال نمایش می دهد، LED را روشن می کند، متغیر startTimer از نوع boolean را روی true ست می کند و متغیر lastTrigger را با زمان فعلی به روز می کند:
1 2 3 4 5 6 |
ICACHE_RAM_ATTR void detectsMovement() { Serial.println("MOTION DETECTED!!!"); digitalWrite(led, HIGH); startTimer = true; lastTrigger = millis(); } |
- در این قسمت, کد به loop برمی گردد و اینبار متغیر startTimer در حالت true قرار دارد. بنابراین وقتی که زمان از ثانیه های تعریف شده گذشت(از تشخیص حرکت)؛ دستورات زیر true خواهند بود:
1 2 3 4 5 |
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) { Serial.println("Motion stopped…"); digitalWrite(led, LOW); startTimer = false; } |
نمایش
کد را در ESP8266 آپلود کرده و از انتخاب صحیح پورت و نوع برد ، اطمینان حاصل کنید. سپس مانیتور سریال را در بادریت 115200 باز کنید:
در آخر برای تست مدار خود, دست خود را مقابل سنسور PIR حرکت دهید. LED باید روشن شود و پیغام “!!!MOTION DETECTED” نمایش داده می شود و پس از 10 ثانیه LED خاموش خواهد شد.