/*
* ESP32 SMART PARKING SYSTEM WITH WiFi & IoT
*
* Features:
* - 3 Parking Slots with Ultrasonic Sensors
* - WiFi Web Server for remote monitoring
* - Real-time status updates
* - LCD Display (I2C)
* - Servo Motor Barrier Gate
* - LED Status Indicators
* - Buzzer Alerts
* - Entry/Exit Buttons
* - JSON API for mobile apps
* - Auto-sync with cloud (optional)
*
* ESP32 Advantages over Arduino UNO:
* - Built-in WiFi & Bluetooth
* - More GPIO pins
* - Faster processor (240MHz)
* - More memory (520KB RAM)
* - Dual-core processing
* - Web server capabilities
*/
#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
// ===========================
// WiFi CREDENTIALS
// ===========================
const char* ssid = "Wokwi-GUEST"; // Default for Wokwi
const char* password = ""; // No password for Wokwi
// ===========================
// PIN DEFINITIONS (ESP32)
// ===========================
// Ultrasonic Sensors
#define TRIG1 13 // Slot 1 Trigger
#define ECHO1 12 // Slot 1 Echo
#define TRIG2 14 // Slot 2 Trigger
#define ECHO2 27 // Slot 2 Echo
#define TRIG3 26 // Slot 3 Trigger
#define ECHO3 25 // Slot 3 Echo
// LED Indicators
#define LED_SLOT1 16 // Green - Slot 1 Available
#define LED_SLOT2 17 // Green - Slot 2 Available
#define LED_SLOT3 18 // Green - Slot 3 Available
#define LED_FULL 19 // Red - Parking Full
#define LED_WIFI 2 // Blue - WiFi Status
// Servo Motor (Barrier Gate)
#define SERVO_PIN 15
// Buzzer
#define BUZZER_PIN 23
// Buttons
#define ENTRY_BTN 32
#define EXIT_BTN 33
// I2C Pins (Default for ESP32)
// SDA = GPIO 21
// SCL = GPIO 22
// ===========================
// GLOBAL OBJECTS
// ===========================
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo barrierGate;
WebServer server(80);
// ===========================
// PARKING STATUS VARIABLES
// ===========================
bool slot1Occupied = false;
bool slot2Occupied = false;
bool slot3Occupied = false;
int availableSlots = 3;
const int totalSlots = 3;
const int THRESHOLD_DISTANCE = 15; // cm
// Gate control
bool gateOpen = false;
unsigned long gateOpenTime = 0;
const unsigned long GATE_AUTO_CLOSE = 3000; // 3 seconds
const int GATE_CLOSED = 0;
const int GATE_OPEN = 90;
// Statistics
unsigned long totalEntries = 0;
unsigned long totalExits = 0;
unsigned long systemUptime = 0;
// WiFi status
bool wifiConnected = false;
// ===========================
// SETUP
// ===========================
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("\n\n╔════════════════════════════════════════╗");
Serial.println("║ ESP32 SMART PARKING SYSTEM v2.0 ║");
Serial.println("║ With WiFi & IoT Capabilities ║");
Serial.println("╚════════════════════════════════════════╝\n");
// Initialize LCD
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ESP32 PARKING");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
// Configure GPIO pins
pinMode(TRIG1, OUTPUT);
pinMode(ECHO1, INPUT);
pinMode(TRIG2, OUTPUT);
pinMode(ECHO2, INPUT);
pinMode(TRIG3, OUTPUT);
pinMode(ECHO3, INPUT);
pinMode(LED_SLOT1, OUTPUT);
pinMode(LED_SLOT2, OUTPUT);
pinMode(LED_SLOT3, OUTPUT);
pinMode(LED_FULL, OUTPUT);
pinMode(LED_WIFI, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(ENTRY_BTN, INPUT_PULLUP);
pinMode(EXIT_BTN, INPUT_PULLUP);
// Initialize Servo
barrierGate.attach(SERVO_PIN);
barrierGate.write(GATE_CLOSED);
Serial.println("✓ GPIO Configured");
Serial.println("✓ Sensors Initialized");
Serial.println("✓ Servo Attached");
// Test LEDs
testLEDs();
// Connect to WiFi
connectWiFi();
// Setup Web Server
setupWebServer();
// Final display
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SYSTEM READY!");
lcd.setCursor(0, 1);
if (wifiConnected) {
lcd.print("IP:");
lcd.print(WiFi.localIP().toString().substring(0, 11));
} else {
lcd.print("WiFi: Offline");
}
delay(2000);
Serial.println("\n✓ System Armed and Monitoring!\n");
Serial.println("=== PARKING STATUS ===");
Serial.println("Total Slots: 3");
Serial.println("Available: 3");
if (wifiConnected) {
Serial.print("Web Interface: http://");
Serial.println(WiFi.localIP());
}
Serial.println("======================\n");
systemUptime = millis();
}
// ===========================
// MAIN LOOP
// ===========================
void loop() {
// Handle web server requests
server.handleClient();
// Check parking slots
checkParkingSlots();
// Update displays
updateLCD();
updateLEDs();
// Check buttons
if (digitalRead(ENTRY_BTN) == LOW) {
delay(50);
if (digitalRead(ENTRY_BTN) == LOW) {
handleEntry();
while(digitalRead(ENTRY_BTN) == LOW);
}
}
if (digitalRead(EXIT_BTN) == LOW) {
delay(50);
if (digitalRead(EXIT_BTN) == LOW) {
handleExit();
while(digitalRead(EXIT_BTN) == LOW);
}
}
// Auto-close gate
if (gateOpen && (millis() - gateOpenTime > GATE_AUTO_CLOSE)) {
closeGate();
}
// WiFi status LED blink
static unsigned long lastBlink = 0;
if (wifiConnected && millis() - lastBlink > 1000) {
digitalWrite(LED_WIFI, !digitalRead(LED_WIFI));
lastBlink = millis();
}
delay(200);
}
// ===========================
// WIFI CONNECTION
// ===========================
void connectWiFi() {
Serial.println("Connecting to WiFi...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connecting WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
lcd.setCursor(attempts % 16, 1);
lcd.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
wifiConnected = true;
digitalWrite(LED_WIFI, HIGH);
Serial.println("\n✓ WiFi Connected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("Signal Strength: ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
} else {
wifiConnected = false;
digitalWrite(LED_WIFI, LOW);
Serial.println("\n✗ WiFi Connection Failed");
Serial.println(" System will work in offline mode");
}
}
// ===========================
// WEB SERVER SETUP
// ===========================
void setupWebServer() {
if (!wifiConnected) return;
// Root page
server.on("/", handleRoot);
// API endpoints
server.on("/api/status", handleAPIStatus);
server.on("/api/open", handleAPIOpen);
server.on("/api/stats", handleAPIStats);
// Start server
server.begin();
Serial.println("✓ Web Server Started");
Serial.println(" Access at: http://" + WiFi.localIP().toString());
}
// ===========================
// WEB SERVER HANDLERS
// ===========================
void handleRoot() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta charset='UTF-8'>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
html += "<title>ESP32 Smart Parking</title>";
html += "<style>";
html += "body{font-family:Arial,sans-serif;margin:0;padding:20px;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff}";
html += ".container{max-width:800px;margin:0 auto;background:rgba(255,255,255,0.1);backdrop-filter:blur(10px);border-radius:20px;padding:30px;box-shadow:0 8px 32px rgba(0,0,0,0.3)}";
html += "h1{text-align:center;margin-bottom:10px;font-size:2.5em}";
html += ".subtitle{text-align:center;opacity:0.8;margin-bottom:30px}";
html += ".stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:15px;margin-bottom:30px}";
html += ".stat-card{background:rgba(255,255,255,0.2);padding:20px;border-radius:15px;text-align:center}";
html += ".stat-value{font-size:2.5em;font-weight:bold;margin:10px 0}";
html += ".stat-label{font-size:0.9em;opacity:0.8}";
html += ".slots{display:grid;grid-template-columns:repeat(3,1fr);gap:15px;margin-bottom:30px}";
html += ".slot{background:rgba(255,255,255,0.15);padding:25px;border-radius:15px;text-align:center;border:3px solid}";
html += ".slot.available{border-color:#4ade80}";
html += ".slot.occupied{border-color:#f87171}";
html += ".slot-number{font-size:1.5em;font-weight:bold;margin-bottom:10px}";
html += ".slot-status{font-size:1.2em;margin-top:10px}";
html += ".buttons{display:grid;grid-template-columns:repeat(2,1fr);gap:15px;margin-bottom:20px}";
html += "button{padding:20px;font-size:1.2em;border:none;border-radius:15px;cursor:pointer;font-weight:bold;transition:all 0.3s}";
html += ".btn-entry{background:#4ade80;color:#000}";
html += ".btn-exit{background:#f87171;color:#fff}";
html += "button:hover{transform:translateY(-2px);box-shadow:0 5px 15px rgba(0,0,0,0.3)}";
html += ".info{background:rgba(255,255,255,0.1);padding:15px;border-radius:10px;margin-top:20px;font-size:0.9em}";
html += ".refresh{text-align:center;margin-top:20px}";
html += "@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}";
html += ".live{animation:pulse 2s infinite}";
html += "</style>";
html += "<script>";
html += "function updateStatus(){";
html += "fetch('/api/status').then(r=>r.json()).then(d=>{";
html += "document.getElementById('available').textContent=d.available;";
html += "document.getElementById('occupied').textContent=d.occupied;";
html += "for(let i=1;i<=3;i++){";
html += "let slot=document.getElementById('slot'+i);";
html += "let status=d['slot'+i];";
html += "slot.className='slot '+(status?'occupied':'available');";
html += "slot.querySelector('.slot-status').textContent=status?'🚗 OCCUPIED':'✓ AVAILABLE';";
html += "}});";
html += "}";
html += "function openGate(){";
html += "fetch('/api/open').then(r=>r.text()).then(alert);";
html += "}";
html += "setInterval(updateStatus,2000);";
html += "</script>";
html += "</head><body>";
html += "<div class='container'>";
html += "<h1>🅿️ Smart Parking</h1>";
html += "<div class='subtitle live'>● LIVE MONITORING</div>";
html += "<div class='stats'>";
html += "<div class='stat-card'><div class='stat-value' id='available'>" + String(availableSlots) + "</div><div class='stat-label'>Available</div></div>";
html += "<div class='stat-card'><div class='stat-value' id='occupied'>" + String(totalSlots - availableSlots) + "</div><div class='stat-label'>Occupied</div></div>";
html += "<div class='stat-card'><div class='stat-value'>" + String(totalSlots) + "</div><div class='stat-label'>Total Slots</div></div>";
html += "</div>";
html += "<div class='slots'>";
for (int i = 1; i <= 3; i++) {
bool occupied = (i == 1 ? slot1Occupied : (i == 2 ? slot2Occupied : slot3Occupied));
html += "<div class='slot " + String(occupied ? "occupied" : "available") + "' id='slot" + String(i) + "'>";
html += "<div class='slot-number'>SLOT " + String(i) + "</div>";
html += "<div style='font-size:3em;margin:10px 0'>" + String(occupied ? "🚗" : "🅿️") + "</div>";
html += "<div class='slot-status'>" + String(occupied ? "🚗 OCCUPIED" : "✓ AVAILABLE") + "</div>";
html += "</div>";
}
html += "</div>";
html += "<div class='buttons'>";
html += "<button class='btn-entry' onclick='openGate()'>🚗 ENTRY</button>";
html += "<button class='btn-exit' onclick='openGate()'>🚗 EXIT</button>";
html += "</div>";
html += "<div class='info'>";
html += "📡 <strong>IP:</strong> " + WiFi.localIP().toString() + " | ";
html += "📶 <strong>Signal:</strong> " + String(WiFi.RSSI()) + " dBm | ";
html += "⏱️ <strong>Uptime:</strong> " + String((millis() - systemUptime) / 1000) + "s";
html += "</div>";
html += "<div class='refresh'>Auto-refreshing every 2 seconds...</div>";
html += "</div></body></html>";
server.send(200, "text/html", html);
}
void handleAPIStatus() {
String json = "{";
json += "\"available\":" + String(availableSlots) + ",";
json += "\"occupied\":" + String(totalSlots - availableSlots) + ",";
json += "\"total\":" + String(totalSlots) + ",";
json += "\"slot1\":" + String(slot1Occupied ? "true" : "false") + ",";
json += "\"slot2\":" + String(slot2Occupied ? "true" : "false") + ",";
json += "\"slot3\":" + String(slot3Occupied ? "true" : "false") + ",";
json += "\"gate\":\"" + String(gateOpen ? "open" : "closed") + "\"";
json += "}";
server.send(200, "application/json", json);
}
void handleAPIOpen() {
if (availableSlots > 0) {
openGate();
server.send(200, "text/plain", "Gate opened!");
} else {
server.send(403, "text/plain", "Parking full!");
}
}
void handleAPIStats() {
String json = "{";
json += "\"totalEntries\":" + String(totalEntries) + ",";
json += "\"totalExits\":" + String(totalExits) + ",";
json += "\"uptime\":" + String((millis() - systemUptime) / 1000) + ",";
json += "\"wifiRSSI\":" + String(WiFi.RSSI()) + ",";
json += "\"freeHeap\":" + String(ESP.getFreeHeap());
json += "}";
server.send(200, "application/json", json);
}
// ===========================
// ULTRASONIC DISTANCE READING
// ===========================
long getDistance(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
long distance = duration * 0.034 / 2;
return distance;
}
// ===========================
// CHECK PARKING SLOTS
// ===========================
void checkParkingSlots() {
long distance1 = getDistance(TRIG1, ECHO1);
long distance2 = getDistance(TRIG2, ECHO2);
long distance3 = getDistance(TRIG3, ECHO3);
bool prevSlot1 = slot1Occupied;
bool prevSlot2 = slot2Occupied;
bool prevSlot3 = slot3Occupied;
slot1Occupied = (distance1 > 0 && distance1 < THRESHOLD_DISTANCE);
slot2Occupied = (distance2 > 0 && distance2 < THRESHOLD_DISTANCE);
slot3Occupied = (distance3 > 0 && distance3 < THRESHOLD_DISTANCE);
if (prevSlot1 != slot1Occupied) logSlotChange(1, slot1Occupied, distance1);
if (prevSlot2 != slot2Occupied) logSlotChange(2, slot2Occupied, distance2);
if (prevSlot3 != slot3Occupied) logSlotChange(3, slot3Occupied, distance3);
availableSlots = 0;
if (!slot1Occupied) availableSlots++;
if (!slot2Occupied) availableSlots++;
if (!slot3Occupied) availableSlots++;
if (availableSlots == 0) alertParkingFull();
}
// ===========================
// UPDATE LCD
// ===========================
void updateLCD() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Available: ");
lcd.print(availableSlots);
lcd.print("/");
lcd.print(totalSlots);
lcd.setCursor(0, 1);
lcd.print("S1:");
lcd.print(slot1Occupied ? "X" : "O");
lcd.print(" S2:");
lcd.print(slot2Occupied ? "X" : "O");
lcd.print(" S3:");
lcd.print(slot3Occupied ? "X" : "O");
}
// ===========================
// UPDATE LEDs
// ===========================
void updateLEDs() {
digitalWrite(LED_SLOT1, !slot1Occupied);
digitalWrite(LED_SLOT2, !slot2Occupied);
digitalWrite(LED_SLOT3, !slot3Occupied);
digitalWrite(LED_FULL, availableSlots == 0);
}
// ===========================
// ENTRY/EXIT HANDLERS
// ===========================
void handleEntry() {
Serial.println("\n>>> ENTRY REQUEST <<<");
totalEntries++;
if (availableSlots > 0) {
Serial.println("✓ Space available - Opening gate");
openGate();
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WELCOME!");
lcd.setCursor(0, 1);
lcd.print("Gate Opening...");
delay(1500);
} else {
Serial.println("✗ Parking FULL - Entry denied");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SORRY!");
lcd.setCursor(0, 1);
lcd.print("PARKING FULL");
for (int i = 0; i < 3; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
delay(200);
}
delay(1000);
}
}
void handleExit() {
Serial.println("\n>>> EXIT REQUEST <<<");
totalExits++;
openGate();
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("THANK YOU!");
lcd.setCursor(0, 1);
lcd.print("Please Exit...");
delay(1500);
}
// ===========================
// GATE CONTROL
// ===========================
void openGate() {
if (!gateOpen) {
Serial.println(" → Gate Opening (90°)");
barrierGate.write(GATE_OPEN);
gateOpen = true;
gateOpenTime = millis();
}
}
void closeGate() {
if (gateOpen) {
Serial.println(" → Gate Closing (0°)");
barrierGate.write(GATE_CLOSED);
gateOpen = false;
}
}
// ===========================
// ALERT FUNCTIONS
// ===========================
void alertParkingFull() {
static unsigned long lastAlert = 0;
if (millis() - lastAlert > 5000) {
Serial.println("\n⚠️ WARNING: PARKING LOT FULL!");
for (int i = 0; i < 2; i++) {
digitalWrite(LED_FULL, HIGH);
delay(200);
digitalWrite(LED_FULL, LOW);
delay(200);
}
digitalWrite(LED_FULL, HIGH);
lastAlert = millis();
}
}
// ===========================
// LOGGING
// ===========================
void logSlotChange(int slotNumber, bool occupied, long distance) {
Serial.print("\n--- Slot ");
Serial.print(slotNumber);
Serial.println(" Status Change ---");
Serial.print("Status: ");
Serial.println(occupied ? "OCCUPIED" : "AVAILABLE");
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
Serial.print("Available Slots: ");
Serial.print(availableSlots);
Serial.print("/");
Serial.println(totalSlots);
Serial.println("-------------------------");
}
// ===========================
// TEST FUNCTIONS
// ===========================
void testLEDs() {
Serial.println("Testing LEDs...");
digitalWrite(LED_SLOT1, HIGH);
digitalWrite(LED_SLOT2, HIGH);
digitalWrite(LED_SLOT3, HIGH);
digitalWrite(LED_FULL, HIGH);
digitalWrite(LED_WIFI, HIGH);
delay(500);
digitalWrite(LED_SLOT1, LOW);
digitalWrite(LED_SLOT2, LOW);
digitalWrite(LED_SLOT3, LOW);
digitalWrite(LED_FULL, LOW);
digitalWrite(LED_WIFI, LOW);
delay(500);
Serial.println("✓ LED test complete\n");
}