🅿️ پارکینگ هوشمند با آردوینو: دو سنسور اولتراسونیک، راهبند سروو و نمایش جای خالی روی LCD
در این پروژه یک پارکینگ هوشمند با آردوینو میسازیم: ماشین که جلوی ورودی آمد، اگر جای خالی باشد راهبند بالا میرود و بعد از عبور ماشین پایین میآید و یک جا از ظرفیت کم میشود. هنگام خروج برعکس. تعداد جای خالی همیشه روی LCD دیده میشود، چراغ سبز و قرمز وضعیت را نشان میدهند و اگر پارکینگ پر باشد، بوق میزند و در باز نمیشود. این پروژه برای نمایشگاه مدرسه و پروژهی دانشجویی محبوب است و همهاش را میشود اول در شبیهساز آردوینو ساخت.
ایدهی اصلی: تشخیص جهت با دو سنسور
یک سنسور بیرون راهبند است و یکی داخل آن. ماشینی که وارد میشود اول سنسور بیرون و بعد سنسور داخل را فعال میکند؛ ماشینی که خارج میشود برعکس. برنامه سه حالت دارد:
- IDLE (بیکار): راهبند بسته است و منتظر ماشین.
- ENTERING (در حال ورود): سنسور بیرون ماشین دید و جای خالی بود؛ راهبند باز است تا ماشین از جلوی سنسور داخل هم رد شود.
- EXITING (در حال خروج): سنسور داخل ماشین دید؛ راهبند باز است تا ماشین از سنسور بیرون رد شود.
شمارش فقط وقتی انجام میشود که ماشین واقعاً از سنسور آن طرف رد شده باشد و هر دو سنسور ۱٫۵ ثانیه خالی بمانند. اگر راننده جلوی در آمد و دور زد، بعد از ده ثانیه راهبند بدون شمارش بسته میشود.
قطعهها
- آردوینو اونو
- دو سنسور اولتراسونیک HC-SR04
- یک سرووموتور SG90 برای راهبند
- یک LCD ۱۶×۲ با ماژول I2C
- LED سبز و LED قرمز با دو مقاومت ۲۲۰ اهم
- یک بازر
سیمکشی
| قطعه | پایه | به آردوینو |
|---|---|---|
| اولتراسونیک بیرون (ورودی) | + / TRIG / ECHO / − | 5V / D4 / D5 / GND |
| اولتراسونیک داخل (خروجی) | + / TRIG / ECHO / − | 5V / D6 / D7 / GND |
| بازر | + / − | D8 / GND |
| سروو | + (قرمز) / − (قهوهای) / SIG (نارنجی) | 5V / GND / D9 |
| LED سبز | پایهی بلند از راه ۲۲۰ اهم | D11؛ پایهی کوتاه به GND |
| LED قرمز | پایهی بلند از راه ۲۲۰ اهم | D12؛ پایهی کوتاه به GND |
| LCD I2C | GND / VCC / SDA / SCL | GND / 5V / A4 / A5 |
کد کامل
// Smart parking: entry and exit HC-SR04 sensors, a servo barrier,
// an I2C LCD showing free spaces, two LEDs and a buzzer.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
const int IN_TRIG = 4, IN_ECHO = 5; // sensor outside the gate
const int OUT_TRIG = 6, OUT_ECHO = 7; // sensor inside the gate
const int BUZZER = 8;
const int SERVO_PIN = 9;
const int GREEN_LED = 11, RED_LED = 12;
const int CAPACITY = 4; // parking spaces
const int CAR_CM = 15; // closer than this = a car is there
const int GATE_CLOSED = 0, GATE_OPEN = 90;
const unsigned long CLEAR_MS = 1500; // both sensors clear this long = car has passed
const unsigned long GIVE_UP_MS = 10000; // close the gate if the car turns back
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo gate;
enum Mode { IDLE, ENTERING, EXITING };
Mode mode = IDLE;
int freeSpaces = CAPACITY;
bool passed = false;
unsigned long openedAt = 0, lastSeen = 0, lastFull = 0;
long readCm(int trig, int echo) {
digitalWrite(trig, LOW);
delayMicroseconds(2);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
long us = pulseIn(echo, HIGH, 30000); // wait at most 30 ms
if (us == 0) return 999; // no echo = nothing in front
return us / 58; // microseconds -> cm
}
void showLine(int row, String text) {
while (text.length() < 16) text += " "; // pad to wipe old characters
lcd.setCursor(0, row);
lcd.print(text);
}
void showSpaces() {
showLine(0, "Free: " + String(freeSpaces) + "/" + String(CAPACITY));
showLine(1, freeSpaces > 0 ? "Welcome" : "FULL");
}
void openGate(Mode m) {
mode = m;
passed = false;
openedAt = millis();
lastSeen = millis();
gate.write(GATE_OPEN);
tone(BUZZER, 1500, 120);
showLine(1, m == ENTERING ? "Gate open: IN" : "Gate open: OUT");
Serial.println(m == ENTERING ? "Car entering" : "Car leaving");
}
void closeGate() {
gate.write(GATE_CLOSED);
mode = IDLE;
showSpaces();
Serial.print("Free spaces: ");
Serial.println(freeSpaces);
}
void setup() {
Serial.begin(9600);
pinMode(IN_TRIG, OUTPUT);
pinMode(IN_ECHO, INPUT);
pinMode(OUT_TRIG, OUTPUT);
pinMode(OUT_ECHO, INPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
gate.attach(SERVO_PIN);
gate.write(GATE_CLOSED);
lcd.init();
lcd.backlight();
showSpaces();
}
void loop() {
bool atIn = readCm(IN_TRIG, IN_ECHO) < CAR_CM;
delay(20); // let the first echo fade away
bool atOut = readCm(OUT_TRIG, OUT_ECHO) < CAR_CM;
unsigned long now = millis();
if (mode == IDLE) {
if (atIn) {
if (freeSpaces > 0) {
openGate(ENTERING);
} else if (now - lastFull > 3000) { // full: beep, but not on every loop
lastFull = now;
showLine(1, "FULL - sorry");
tone(BUZZER, 400, 400);
}
} else if (atOut) {
openGate(EXITING);
}
} else {
bool farSide = (mode == ENTERING) ? atOut : atIn; // the sensor past the gate
if (farSide) passed = true;
if (atIn || atOut) lastSeen = now;
bool allClear = now - lastSeen >= CLEAR_MS;
if (passed && allClear) {
if (mode == ENTERING) freeSpaces--;
else if (freeSpaces < CAPACITY) freeSpaces++;
closeGate();
} else if (!passed && allClear && now - openedAt > GIVE_UP_MS) {
closeGate(); // the driver turned back: no count
}
}
digitalWrite(GREEN_LED, freeSpaces > 0 ? HIGH : LOW);
digitalWrite(RED_LED, freeSpaces > 0 ? LOW : HIGH);
delay(50);
}
توضیح بخشهای مهم
readCmیک پالس ۱۰ میکروثانیهای به TRIG میفرستد و باpulseInمدت بالا ماندن ECHO را میخواند. صدا رفت و برگشت میکند، پس هر ۵۸ میکروثانیه یعنی یک سانتیمتر فاصله. سومین عددpulseInمهلت است تا وقتی جلوی سنسور خالی است برنامه گیر نکند. جزئیات این سنسور در فاصلهسنج اولتراسونیک با آردوینو است.- بین خواندن دو سنسور ۲۰ میلیثانیه صبر میکنیم تا پژواک سنسور اول به سنسور دوم نرسد و آن را گول نزند.
farSideسنسورِ آن طرف راهبند است: هنگام ورود سنسور داخل و هنگام خروج سنسور بیرون. تا این سنسور ماشین را ندیده،passedنادرست میماند و شمارشی انجام نمیشود.lastSeenآخرین لحظهای است که یکی از سنسورها ماشین دیده. راهبند فقط وقتی پایین میآید که ۱٫۵ ثانیه هیچ سنسوری چیزی نبیند؛ پس روی سقف ماشین فرود نمیآید.showLineمتن را با فاصله تا ۱۶ حرف پر میکند. بدون این کار، وقتی «Welcome» جای «Gate open: IN» نوشته شود، تکهی آخر متن قبلی روی صفحه میماند.lastFullباعث میشود وقتی پارکینگ پر است و ماشین جلوی در ایستاده، بوق فقط هر سه ثانیه یک بار بزند، نه بیست بار در ثانیه.
قدمبهقدم در شبیهساز
- اول فقط دو سنسور را بچین و با سریال مانیتور مطمئن شو هر کدام فاصلهی درست میدهد. پروژهی آمادهی فاصلهسنج برای این مرحله کافی است.
- سروو را اضافه کن و با یک برنامهی کوچک ۰ و ۹۰ درجه را امتحان کن تا جهت باز و بسته شدن را بشناسی.
- LCD را وصل کن و کد کامل را اجرا کن. باید «Free: 4/4» و «Welcome» ببینی.
- فاصلهی سنسور بیرون را زیر ۱۵ سانتیمتر ببر: راهبند باز میشود. حالا فاصلهی سنسور بیرون را زیاد و سنسور داخل را کم کن (ماشین رد شد) و بعد آن را هم زیاد کن. بعد از ۱٫۵ ثانیه راهبند بسته میشود و «Free: 3/4» میآید.
- چهار بار این کار را تکرار کن تا پارکینگ پر شود؛ LED قرمز روشن میشود و در دیگر باز نمیشود.
- خروج را با سنسور داخل امتحان کن و ببین شمارنده بالا میرود.
آردوینو اونو، سنسور اولتراسونیک و سروو در تینکرکد فارسی رایگاناند. LCD I2C جزو ماژولهای حرفهای است و برای اجرایش اشتراک Plus لازم است؛ اگر نداری، سه خط lcd را حذف کن و پیامها را در سریال مانیتور ببین. برای تمرین با یک سنسور، درس سنسور پارک و پروژهی سنسور پارک را ببین.
ساخت ماکت واقعی
- تغذیهی سروو: یک SG90 کوچک با ۵ ولت آردوینو کار میکند، ولی اگر برد هنگام حرکت راهبند ریست شد، سروو را از یک منبع ۵ ولتی جدا تغذیه کن و GND ها را به هم وصل کن.
- جای سنسورها: دو سنسور را حدود ۱۰ تا ۱۵ سانتیمتر دورتر از دو طرف راهبند و رو به مسیر ماشین بگذار، نه رو به هم؛ وگرنه پالس یکی را دیگری میشنود.
- آستانه:
CAR_CMرا بر اساس عرض مسیر ماکت تنظیم کن؛ عددی کمی کمتر از فاصلهی سنسور تا دیوارهی مقابل. - آدرس LCD: اگر LCD چیزی نشان نداد، آدرس
0x27را به0x3Fتغییر بده و پیچ کنتراست پشت ماژول را بچرخان. راهنمای کامل در LCD I2C با آردوینو است.
اشکالهای رایج
- شمارنده دو تا دو تا کم میشود:
CLEAR_MSخیلی کوتاه است و فاصلهی بین دیده شدن دو سنسور را «خالی» حساب میکند. آن را بیشتر کن. - راهبند باز میشود و فوراً بسته میشود: سنسورها جابهجا وصل شدهاند؛ TRIG و ECHO هر سنسور را چک کن.
- سروو میلرزد: تغذیه ضعیف است یا سیم سیگنال شل است.
- کتابخانه پیدا نمیشود: روی کامپیوتر LiquidCrystal_I2C را از Library Manager نصب کن. صفحهی کتابخانهها توضیح میدهد.
ایده برای کاملتر کردن
تعداد جای خالی را در EEPROM ذخیره کن تا با قطع برق پاک نشود. برای هر جای پارک یک سنسور جدا بگذار و روی LCD نشان بده کدام جا خالی است. یا پروژه را به ESP32 ببر و ظرفیت را در یک صفحهی وب نشان بده؛ ایدهاش در ۳۰ ایده پروژه اینترنت اشیا و راه ساختن صفحهاش در وبسرور ESP32 است. برای حرکت نرمتر راهبند، آموزش سروو با آردوینو را ببین.
پارکینگ هوشمند را در تینکرکد فارسی بساز 🅿️سؤالهای رایج
چرا دو سنسور لازم است و یکی کافی نیست؟
با یک سنسور نمیشود فهمید ماشین وارد شد یا خارج، یا جلوی در آمد و دور زد. ترتیب دیده شدن ماشین در دو سنسورِ دو طرف راهبند، جهت حرکت را مشخص میکند و شمارش را درست نگه میدارد.
اگر اشتراک Plus نداشته باشم، این پروژه در شبیهساز اجرا میشود؟
آردوینو اونو، سنسور اولتراسونیک و سروو رایگاناند. LCD I2C جزو ماژولهای حرفهای است؛ میتوانی خطهای LCD را حذف کنی و همان پیامها را در سریال مانیتور ببینی، یا از LCD معمولی با کتابخانهی LiquidCrystal استفاده کنی.
برای ماکت واقعی از چه سنسوری استفاده کنم؟
برای ماکت کوچک، سنسور اولتراسونیک HC-SR04 یا ماژول مادون قرمز تشخیص مانع هر دو جواب میدهند. اولتراسونیک فاصله را عددی میدهد و آستانهاش در کد تنظیم میشود؛ مادون قرمز ارزانتر و کوچکتر است ولی به رنگ و نور حساس است.