راه اندازی سنسور تشخیص حرکت PIR با ESP32.

به نام خدا

به آموزش راه اندازی سنسور تشخیص حرکت PIR با ESP32 خوش اومدید.

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

همچنین تو این مثال با دو مفهوم تایمر و وقفه هم آشنا میشیم.

قطعاتی که برای این مثال نیاز داریم

  • ESP32 DOIT DEVKIT V1 Board
  • سنسور تشخیص حرکت PIR  کوچیک مدل AM312 و یا سنسور تشخیص حرکت PIR مدل HC-SR501
  • LED  mm5
  • مقاومت ۳۳۰ اهم
  • بِردبُرد
  • سیم جامپر

تعریف وقفه

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

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

حالا برای اینکه در ARDUINO IDE یک وقفه ایجاد کنیم از دستور ()attachInterrupt استفاده میشه. این دستور ۳ ورودی داره. پین GPIO ، اسم دستورالعملی که وضعیتش رو بررسی می کنیم و حالت.

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

وقفه GPIO

برای ایجاد وقفه GPIO با دستور digitalPinToInterrupt(GPIO) با ورودی شماره GPIO وقفه ایجاد می کنیم . مثلا برای ایجاد وقفه برای GPIO27 به این صورت نوشته میشه :

digitalPinToInterrupt(27)

در برد ESP32 شکل زیر همه پین هایی که با کادر قرمز مشخص شدن می تونن در ایجاد وقفه استفاده بشن . ما تو این مثال با پین GPIO27 وقفه درست کردیم.

دستورالعمل

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

حالت

ورودی سوم دستور ()attachInterrupt حالته و ۵ وضعیت میتونه داشته باشه:

  • LOW : وقتی پین در حالت LOW قرار داشته باشه وقفه ایجاد میشه.
  • HIGH : وقتی پین در حالت HIGH قرار داشته باشه وقفه ایجاد میشه.
  • CHANGE : وقتی  وضعیت پین تغییر کنه وقفه ایجاد میشه. مثلا از HIGH  به LOW و برعکس.
  • FALLING : وقتی وضعیت پین  از HIGH  به LOW تغییر کنه وقفه ایجاد میشه.
  • RISING : وقتی وضعیت پین  از LOW به HIGH  تغییر کنه وقفه ایجاد میشه.

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

معرفی تایمرها

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

دستور ()delay

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

delay(time in miliseconds)

مثلا با اجرای delay(1000) اون خط کد یک ثانیه متوقف میشه.

()delay یک دستور متوقف کننده است. یک دستور متوقف کننده ماهیتش به این صورته که تا زمانی که تسک مورد نظر به پایان نرسه برنامه هیچ کاری نمیتونه انجام بده. پس یعنی اگر همزمان چند تا تسک رو بخواهیم داشته باشیم با ()delay امکان پذیر نیست.

راه حل چیه ؟!

دستور ()millis

با دستور()millis مدت زمان اجرای برنامه از اول تا زمان اجرا رو با واحد میلی ثانیه میشه به دست آورد. این دستور خیلی مفیده چون بدون اینکه کد برنامه متوقف بشه این مدت زمان محاسبه میشه .

LED چشمک زن با ()millis

با کد زیر به مدت ۱۰۰۰ میلی ثانیه یک LED رو به حالت چشمک زن برد و بعد خاموش میشه.

/*********
  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);
  }
}

این کد چطور کار می کنه ؟

اساس این کد اینطوریه که مدت زمان ثبت شده تا حال (previousMillis) رو از زمان حال  (currentMillis)کم می کنه. اگر باقیمانده بزرگتر از وقفه (تو این مثال ۱۰۰۰ میلی ثانیه) باشه برنامه متغیر PreviousMillis رو به زمان حال تغییر میده و LED خاموش و یا روشن میشه.

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

خب چون این عملگر ماهیت متوقف کنندگی نداره پس هر کدی خارج از If اول نوشته بشه باید به صورت نرمال اجرا بشه. پس حالا دیگه میشه تسک های دیگه ای رو به ()loop اضافه کرد و LED در هر ثانیه چشمک زن بشه.

برای اینکه مثال رو عملی انجام بدین و آزمایش کنید میتونید مدار رو به شکل زیر ببندید.

ESP32 با سنسور تشخیص حرکت PIR

مدار شکل زیر رو میشه به راحتی پیاده سازی کرد. LED  به GPIO26 متصله و از سنسور تشخیص حرکت PIR  کوچیک مدل AM312 استفاده کردیم . این سنسور با ولتاژ ۳.۳ ولت کار می کنه و به GPIO27 متصل شده.

پین های خروجی سنسور AM312 که در شکل زیر نمایش داده شده ببینید.

آپلود کردن کد

خب بعد از اینکه طبق شکل مدار و اتصالاتش رو بستید کد زیر رو در ARDUINO IDE کپی کنید. اگر زمان چشمک زدن LED رو بخواین تغییر بدین کافیه در دستور timeSecons زمان مورد نظر رو وارد کنید.

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

#define timeSeconds 10

// Set GPIOs for LED and PIR Motion Sensor
const int led = 26;
const int motionSensor = 27;

// 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
void IRAM_ATTR 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 و سنسور تشخیص حرکت متصل شده.

// Set GPIOs for LED and PIR Motion Sensor
const int led = 26;
const int motionSensor = 27;

بعد باید متغیری تعریف بشه که تایمر رو برای خاموش کردن LED بعد از تشخیص حرکت تنظیم کنه .

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

 متغیر now زمان حال و lastTrigger زمان شناسایی حرکت توسط سنسور رو بیان می کنه.

()Setup

این دستور با مقدار دهی اولیه به پورت سریال به مقدار  باد ریت ۱۱۵۲۰۰ شروع میشه.

Serial.begin(115200);

سنسور تشخیص حرکت PIR به عنوان ورودی پول آپ تنظیم میشه.

pinMode(motionSensor, INPUT_PULLUP);

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

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

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

LED هم که با حالت LOW شروع به کار می کنه به عنوان خروجیه.

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

()loop

این دستور به صورت مداوم داره اجرا میشه و تو هر بار اجرا متغیر now برابر زمان حال میشه.

now = millis();

این روال تا زمانی که یک حرکت جدید تشخیص داده بشه ادامه داره. به محض تشخیص حرکت توسط سنسور، دستور()detectsMovement فراخوانی میشه. (چون یک وقفه در ()setup تنظیم کرده بودیم!)

دستور ()detectsMovement هم پیامی رو در مانیتور نمایش میده، LED روشن میشه، متغیر بولین startTimer به حالت TRUE تنظیم میشه و متغیر lastTrigger به زمان حال آپدیت میشه.

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

نکته : IRAM_ATTR برای زمانیه که وقفه در رم ذخیره بشه در غیر اینصورت وقفه در حافظه فلش ذخیره میشه و کندتره!

خب بعد از این مرحله کد به ()loop برمیگرده. حالا دیگه متغیر startTimer در حالت true قرار داره. پس اگر مدت زمانی که تعریف کردیم سپری بشه (بعد از تشخیص حرکت) شرط if برقرار میشه .

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

حالا دیگه پیام  “Motion stopped…” (حرکت متوقف شد) در مانیتورنمایش داده میشه، LED خاموش میشه و متغیر startTimer درحالت false قرار می گیره.

اثبات عملکرد سنسور

برای اینکه عملکرد سنسور رو بهتر ببینید کد رو در ESP32 آپلود کنید و مانیتور سریال رو با بادریت ۱۱۵۲۰۰ روشن کنید.

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

 پایان آموزش راه اندازی سنسور تشخیص حرکت PIR با ESP32.

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

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

قبلا حساب کاربری ایجاد کرده اید؟
گذرواژه خود را فراموش کرده اید؟
Loading...