/*
* =============================================
* SMART PARKING SYSTEM - ESP32
* =============================================
* Components:
* - 4x HC-SR04 Ultrasonic Sensors
* > 3 for Zone 1, Zone 2, Zone 3 (slot detection)
* > 1 for entrance (vehicle detection)
* - MFRC522 RFID Reader (SPI)
* - LCD1602 with I2C backpack
* - Micro Servo Motor (gate control)
*
* Gate Logic:
* - Opens ONLY when: valid RFID card + vehicle detected at entrance
* - Closes automatically after vehicle passes
*
* LCD Display modes:
* - Idle: Line1: Free spaces X/3 Line2: Z1:F Z2:F Z3:O
* - Access Granted: Line1: ACCESS GRANTED Line2: Free spaces: X
* - Access Denied: Line1: ACCESS DENIED Line2: Invalid card
* - Lot Full: Line1: PARKING FULL Line2: No free spaces
* =============================================
*
* PIN CONNECTIONS:
*
* HC-SR04 (Entrance) HC-SR04 (Zone 1)
* TRIG -> GPIO 5 TRIG -> GPIO 26
* ECHO -> GPIO 18 ECHO -> GPIO 27
*
* HC-SR04 (Zone 2) HC-SR04 (Zone 3)
* TRIG -> GPIO 14 TRIG -> GPIO 32
* ECHO -> GPIO 12 ECHO -> GPIO 33
*
* MFRC522 (SPI)
* SDA -> GPIO 15 (remapped to avoid I2C conflict)
* SCK -> GPIO 19
* MOSI -> GPIO 23
* MISO -> GPIO 25 (remapped to avoid I2C conflict)
* RST -> GPIO 17
* VCC -> 3.3V
* GND -> GND
*
* LCD1602 I2C
* SDA -> GPIO 21
* SCL -> GPIO 22
* VCC -> 5V
* GND -> GND
*
* Servo Motor
* Signal -> GPIO 13
* VCC -> 5V (external supply recommended)
* GND -> GND (shared with ESP32)
*
* Required Libraries (install via Arduino Library Manager):
* - MFRC522 by GithubCommunity
* - LiquidCrystal_I2C by Frank de Brabander
* - ESP32Servo by Kevin Harrington
* =============================================
*/
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
// ─── PIN DEFINITIONS ───────────────────────────────────────────────
#define ENTRANCE_TRIG 5
#define ENTRANCE_ECHO 18
#define ZONE1_TRIG 26
#define ZONE1_ECHO 27
#define ZONE2_TRIG 14
#define ZONE2_ECHO 12
#define ZONE3_TRIG 32
#define ZONE3_ECHO 33
#define RFID_SDA_SS 2
#define RFID_RST 17
#define SERVO_PIN 13
// ─── CONSTANTS ─────────────────────────────────────────────────────
#define TOTAL_SLOTS 3
#define SLOT_OCCUPIED_CM 15
#define VEHICLE_DETECT_CM 20
#define GATE_OPEN_ANGLE 90
#define GATE_CLOSE_ANGLE 0
#define GATE_OPEN_MS 4000
#define LCD_UPDATE_MS 1000
// ─── LCD DISPLAY MODES ─────────────────────────────────────────────
#define LCD_IDLE 0
#define LCD_GRANTED 1
#define LCD_DENIED 2
#define LCD_FULL 3
// ─── AUTHORIZED RFID CARDS ─────────────────────────────────────────
// Wokwi: type "01 02 03 04" in the MFRC522 card UID field
const byte AUTHORIZED_CARDS[][4] = {
{0x01, 0x02, 0x03, 0x04}, // Card 1 - UID: 01 02 03 04
};
const int NUM_AUTHORIZED = sizeof(AUTHORIZED_CARDS) / sizeof(AUTHORIZED_CARDS[0]);
// ─── OBJECT INSTANCES ──────────────────────────────────────────────
MFRC522 rfid(RFID_SDA_SS, RFID_RST);
LiquidCrystal_I2C lcd(0x27, 16, 2); // Try 0x3F if LCD doesn't respond
Servo gateServo;
// ─── GLOBAL STATE ──────────────────────────────────────────────────
bool slotOccupied[TOTAL_SLOTS] = {false, false, false};
bool gateOpen = false;
unsigned long gateOpenTime = 0;
unsigned long lastLCDUpdate = 0;
// ─── UTILITY: Read distance from HC-SR04 ───────────────────────────
long readDistance(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
if (duration == 0) return 999;
return duration * 0.034 / 2;
}
// ─── UTILITY: Check if UID is authorized ───────────────────────────
bool isAuthorized(byte *uid, byte uidSize) {
if (uidSize != 4) return false;
for (int i = 0; i < NUM_AUTHORIZED; i++) {
bool match = true;
for (int j = 0; j < 4; j++) {
if (uid[j] != AUTHORIZED_CARDS[i][j]) { match = false; break; }
}
if (match) return true;
}
return false;
}
// ─── UTILITY: Read RFID card ───────────────────────────────────────
// Returns: 1 = authorized, -1 = unauthorized, 0 = no card
int readRFID() {
if (!rfid.PICC_IsNewCardPresent()) return 0;
if (!rfid.PICC_ReadCardSerial()) return 0;
byte *uid = rfid.uid.uidByte;
byte uidSize = rfid.uid.size;
Serial.print("[RFID] Scanned UID:");
for (byte i = 0; i < uidSize; i++) {
Serial.print(uid[i] < 0x10 ? " 0" : " ");
Serial.print(uid[i], HEX);
}
Serial.println();
int result = isAuthorized(uid, uidSize) ? 1 : -1;
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
return result;
}
// ─── GATE CONTROL ──────────────────────────────────────────────────
void openGate() {
gateServo.write(GATE_OPEN_ANGLE);
gateOpen = true;
gateOpenTime = millis();
Serial.println("[GATE] Opened");
}
void closeGate() {
gateServo.write(GATE_CLOSE_ANGLE);
gateOpen = false;
Serial.println("[GATE] Closed");
}
// ─── LCD UPDATE ────────────────────────────────────────────────────
void updateLCD(int availableSlots, int mode) {
lcd.clear();
switch (mode) {
case LCD_GRANTED:
lcd.setCursor(0, 0);
lcd.print("ACCESS GRANTED ");
lcd.setCursor(0, 1);
lcd.print("Free spaces: ");
lcd.print(availableSlots);
break;
case LCD_DENIED:
lcd.setCursor(0, 0);
lcd.print("ACCESS DENIED ");
lcd.setCursor(0, 1);
lcd.print("Invalid card ");
break;
case LCD_FULL:
lcd.setCursor(0, 0);
lcd.print("PARKING FULL ");
lcd.setCursor(0, 1);
lcd.print("No free spaces ");
break;
case LCD_IDLE:
default:
// Line 1: Free spaces X/3
lcd.setCursor(0, 0);
lcd.print("Free spaces:");
lcd.print(availableSlots);
lcd.print("/");
lcd.print(TOTAL_SLOTS);
// Line 2: Z1:F Z2:F Z3:O
lcd.setCursor(0, 1);
for (int i = 0; i < TOTAL_SLOTS; i++) {
lcd.print("Z");
lcd.print(i + 1);
lcd.print(":");
lcd.print(slotOccupied[i] ? "O" : "F");
if (i < TOTAL_SLOTS - 1) lcd.print(" ");
}
break;
}
}
// ─── SETUP ─────────────────────────────────────────────────────────
void setup() {
Serial.begin(115200);
Serial.println("\n=== Smart Parking System Starting ===");
int trigPins[] = {ENTRANCE_TRIG, ZONE1_TRIG, ZONE2_TRIG, ZONE3_TRIG};
int echoPins[] = {ENTRANCE_ECHO, ZONE1_ECHO, ZONE2_ECHO, ZONE3_ECHO};
for (int i = 0; i < 4; i++) {
pinMode(trigPins[i], OUTPUT);
pinMode(echoPins[i], INPUT);
}
SPI.begin(19, 4, 23, 2);
rfid.PCD_Init();
byte version = rfid.PCD_ReadRegister(MFRC522::VersionReg);
Serial.print("[RFID] Version: 0x");
Serial.println(version, HEX);
Serial.println("[RFID] Ready - Authorized UID: 01 02 03 04");
Wire.begin();
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Parking System");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
Serial.println("[LCD] Initialized");
gateServo.attach(SERVO_PIN);
gateServo.write(GATE_CLOSE_ANGLE);
Serial.println("[SERVO] Gate closed");
delay(2000);
Serial.println("[SYSTEM] Ready");
}
// ─── MAIN LOOP ─────────────────────────────────────────────────────
void loop() {
// 1. Read zone sensors
long d1 = readDistance(ZONE1_TRIG, ZONE1_ECHO);
long d2 = readDistance(ZONE2_TRIG, ZONE2_ECHO);
long d3 = readDistance(ZONE3_TRIG, ZONE3_ECHO);
slotOccupied[0] = (d1 < SLOT_OCCUPIED_CM);
slotOccupied[1] = (d2 < SLOT_OCCUPIED_CM);
slotOccupied[2] = (d3 < SLOT_OCCUPIED_CM);
int availableSlots = 0;
for (int i = 0; i < TOTAL_SLOTS; i++) {
if (!slotOccupied[i]) availableSlots++;
}
// 2. Read entrance sensor
long entranceDist = readDistance(ENTRANCE_TRIG, ENTRANCE_ECHO);
bool vehicleAtEntrance = (entranceDist < VEHICLE_DETECT_CM);
// 3. Gate auto-close timer
if (gateOpen && (millis() - gateOpenTime >= GATE_OPEN_MS)) {
closeGate();
}
// 4. RFID check (only when gate is closed and vehicle is present)
if (!gateOpen && vehicleAtEntrance) {
int cardResult = readRFID();
if (cardResult == 1) {
Serial.println("[RFID] Authorized");
if (availableSlots > 0) {
Serial.println("[GATE] Opening - Access granted");
updateLCD(availableSlots, LCD_GRANTED);
delay(1500);
openGate();
} else {
Serial.println("[PARKING] Lot full");
updateLCD(0, LCD_FULL);
delay(2500);
}
} else if (cardResult == -1) {
Serial.println("[RFID] Unauthorized - Access denied");
updateLCD(availableSlots, LCD_DENIED);
delay(2500);
}
}
// 5. Idle LCD refresh
if (!gateOpen && (millis() - lastLCDUpdate >= LCD_UPDATE_MS)) {
updateLCD(availableSlots, LCD_IDLE);
lastLCDUpdate = millis();
Serial.printf("[STATUS] Free:%d/3 | Z1:%ldcm Z2:%ldcm Z3:%ldcm | Entrance:%ldcm\n",
availableSlots, d1, d2, d3, entranceDist);
}
delay(100);
}