0% found this document useful (0 votes)
11 views21 pages

Car Patking System

This document contains code for a smart parking system using an ESP32 microcontroller, which manages parking slots, entry and exit gates, and user interactions through a web server. It includes functionalities such as sensor detection, servo motor control for gates, reservation management, and revenue tracking. The system also provides visual and auditory alerts for various states and conditions, ensuring efficient parking management.

Uploaded by

vickyvikram76565
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views21 pages

Car Patking System

This document contains code for a smart parking system using an ESP32 microcontroller, which manages parking slots, entry and exit gates, and user interactions through a web server. It includes functionalities such as sensor detection, servo motor control for gates, reservation management, and revenue tracking. The system also provides visual and auditory alerts for various states and conditions, ensuring efficient parking management.

Uploaded by

vickyvikram76565
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

#include <WiFi.

h>
#include <WebServer.h>
#include <ESP32Servo.h>
#include <ArduinoJson.h>
#include <SPIFFS.h>
#include <vector>
#include <string>

// WiFi credentials
const char* ssid = "A";
const char* password = "55667799";

// Create WebServer object on port 80


WebServer server(80);

// Servo motors
Servo entryServo;
Servo exitServo;

const int carEnterPin = 0; // G3 - Entry IR Sensor


const int carExitPin = 4; // G4 - Exit IR Sensor
const int slot1Pin = 5; // G15 - Slot 1 IR Sensor
const int slot2Pin = 18; // G19 - Slot 2 IR Sensor
const int slot3Pin = 19; // G22 - Slot 3 IR Sensor
const int slot4Pin = 21; // G21 - Slot 4 IR Sensor
const int slot5Pin = 22; // G13 - Slot 5 IR Sensor
const int slot6Pin = 23; // G12 - Slot 6 IR Sensor
const int entryServoPin = 13; // G23 - Tower Pro SG90 Entry Servo
const int exitServoPin = 12; // G34 - Tower Pro SG90 Exit Servo
const int buzzerPin = 14; // G14 - Status/Alert Buzzer
const int ledPin = 15; // G18 - System Status LED

// Constants - Optimized for Tower Pro SG90 Servo Motors


const int CLOSE_ANGLE = 0; // 0 degrees for closed position
const int OPEN_ANGLE = 90; // 90 degrees for open position
const unsigned long SENSOR_DEBOUNCE = 200;
const unsigned long GATE_OPEN_DURATION = 4000; // 4 seconds gate open time
const int TOTAL_SLOTS = 6;
const int PRICE_PER_HOUR = 50;

// SG90 Servo specifications


const int SERVO_MIN_PULSE = 500; // Minimum pulse width in microseconds
const int SERVO_MAX_PULSE = 2500; // Maximum pulse width in microseconds

// Gate status variables


bool entryGateOpen = false;
bool exitGateOpen = false;
bool allSlotsFull = false;
unsigned long entryGateOpenTime = 0;
unsigned long exitGateOpenTime = 0;

// Updated Slot status structure with better timing management


struct ParkingSlot {
bool occupied;
bool reserved;
unsigned long occupiedTime;
unsigned long totalTime;
bool lastState;
String vehicleNumber;
String phoneNumber;
unsigned long reservedDuration; // in seconds
unsigned long reservationTime; // when the reservation was made
unsigned long expectedArrival; // expected arrival timestamp
};

ParkingSlot slots[TOTAL_SLOTS] = {
{false, false, 0, 0, false, "", "", 0, 0, 0},
{false, false, 0, 0, false, "", "", 0, 0, 0},
{false, false, 0, 0, false, "", "", 0, 0, 0},
{false, false, 0, 0, false, "", "", 0, 0, 0},
{false, false, 0, 0, false, "", "", 0, 0, 0},
{false, false, 0, 0, false, "", "", 0, 0, 0}
};

// Sensor pins array for easy iteration


const int slotPins[TOTAL_SLOTS] = {slot1Pin, slot2Pin, slot3Pin, slot4Pin,
slot5Pin, slot6Pin};

// Previous sensor states for edge detection


bool lastEntryState = false;
bool lastExitState = false;

// Debouncing variables
unsigned long lastSensorRead = 0;
unsigned long lastDataUpdate = 0;
unsigned long lastLedBlink = 0;
bool ledState = false;

// Statistics
int totalCarsToday = 0;
float averageParkingTime = 0;
unsigned long systemStartTime = 0;
unsigned long totalRevenue = 0;
std::vector<std::string> recentActivity;

// Own feature: Simulated users, but for simplicity, assume single user
String currentUserPhone = "";

void setup() {
Serial.begin(115200);
delay(1000);

// Initialize SPIFFS
if(!SPIFFS.begin(true)){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}

// Initialize pins
pinMode(carEnterPin, INPUT_PULLUP);
pinMode(carExitPin, INPUT_PULLUP);
pinMode(buzzerPin, OUTPUT);
pinMode(ledPin, OUTPUT);

for(int i = 0; i < TOTAL_SLOTS; i++) {


pinMode(slotPins[i], INPUT_PULLUP);
}
// Attach Tower Pro SG90 servos with proper pulse width settings
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
entryServo.setPeriodHertz(50); // Standard 50Hz for SG90
exitServo.setPeriodHertz(50); // Standard 50Hz for SG90
entryServo.attach(entryServoPin, SERVO_MIN_PULSE, SERVO_MAX_PULSE);
exitServo.attach(exitServoPin, SERVO_MIN_PULSE, SERVO_MAX_PULSE);

// Initialize servos to closed position


entryServo.write(CLOSE_ANGLE);
exitServo.write(CLOSE_ANGLE);

// Connect to WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
// Blink LED while connecting
digitalWrite(ledPin, !digitalRead(ledPin));
}
Serial.println();
Serial.print("Connected to WiFi. IP Address: ");
Serial.println(WiFi.localIP());

// Initialize web server routes


setupWebServer();

// Start server
server.begin();
Serial.println("Web server started");

systemStartTime = millis();

// Initialize previous states


delay(500);
lastEntryState = !digitalRead(carEnterPin);
lastExitState = !digitalRead(carExitPin);
for(int i = 0; i < TOTAL_SLOTS; i++) {
slots[i].lastState = !digitalRead(slotPins[i]);
}

// System ready - LED solid ON, welcome beep


digitalWrite(ledPin, HIGH);
playWelcomeBeep();

Serial.println("Smart Parking System with 6 slots initialized");


Serial.println("Hardware: 38-pin ESP32 with Tower Pro SG90 servos");
Serial.println("Pin Configuration:");
Serial.println(" Entry IR: G2, Exit IR: G4");
Serial.println(" Slots IR: G5, G18, G19, G21, G22, G23");
Serial.println(" Entry Servo: G13, Exit Servo: G12");
Serial.println(" Buzzer: G14 (Alerts/Status), LED: G15 (System Status)");
Serial.println("LED Usage: Solid=Ready, Blink=Activity, Fast Blink=Full
Parking");
Serial.println("Buzzer Usage: 1 beep=Car detected, 2 beeps=Gate open, 3
beeps=Parking full");
Serial.println("Access web interface at: http://" + WiFi.localIP().toString());
}
void loop() {
server.handleClient();

// Check sensors with debouncing


if (millis() - lastSensorRead > SENSOR_DEBOUNCE) {
checkSensors();
updateParkingTimes();
lastSensorRead = millis();
}

// Update statistics every 5 seconds


if (millis() - lastDataUpdate > 5000) {
updateStatistics();
lastDataUpdate = millis();
}

// Handle LED status indication


updateLedStatus();

// Auto-close gates after timeout


handleGateTimeouts();
}

// Updated checkSensors function with better timing logic


void checkSensors() {
// Read sensors (inverted for IR sensors)
bool entryState = !digitalRead(carEnterPin);
bool exitState = !digitalRead(carExitPin);

// Check current time


unsigned long currentTime = millis();

// Check if all slots are full (considering reservations)


allSlotsFull = true;
for(int i = 0; i < TOTAL_SLOTS; i++) {
if(!slots[i].occupied && !slots[i].reserved) {
allSlotsFull = false;
break;
}
}

// Handle entry gate


if (entryState && !entryGateOpen && !allSlotsFull) {
Serial.println("Car at entry - Opening gate");
entryServo.write(OPEN_ANGLE);
entryGateOpen = true;
entryGateOpenTime = millis();
playBeep(2);
} else if (entryState && allSlotsFull && !entryGateOpen) {
playBeep(3);
Serial.println("Parking Full - Entry denied");
}

// Handle exit gate


if (exitState && !exitGateOpen) {
Serial.println("Car at exit - Opening gate");
exitServo.write(OPEN_ANGLE);
exitGateOpen = true;
exitGateOpenTime = millis();
playBeep(2);
}

// Handle parking slots with reservation timing


for(int i = 0; i < TOTAL_SLOTS; i++) {
bool currentState = !digitalRead(slotPins[i]);

if (currentState != slots[i].lastState) {
if (currentState && !slots[i].occupied) {
// Car entered slot
slots[i].occupied = true;
slots[i].occupiedTime = currentTime;

if (slots[i].reserved) {
Serial.println("Slot " + String(i+1) + ": OCCUPIED (Reserved booking
activated)");
// Start the timer for reserved duration
} else {
Serial.println("Slot " + String(i+1) + ": OCCUPIED (Walk-in customer)");
}

playBeep(1);
recentActivity.push_back(std::string("Slot ") + std::to_string(i+1) + "
occupied");
if (recentActivity.size() > 5)
recentActivity.erase(recentActivity.begin());

} else if (!currentState && slots[i].occupied) {


// Car left slot
unsigned long parkedTime = currentTime - slots[i].occupiedTime;

if (slots[i].reserved && slots[i].reservedDuration > 0) {


// Check if parking time exceeded reserved duration
unsigned long reservedTimeMs = slots[i].reservedDuration * 1000;
if (parkedTime > reservedTimeMs) {
// Calculate penalty for overstaying
unsigned long overTime = parkedTime - reservedTimeMs;
float penalty = (overTime / 60000.0) * 10; // ₹10 per minute penalty
totalRevenue += penalty;
Serial.println("Slot " + String(i+1) + ": VACATED (Overstayed by " +
formatTime(overTime / 1000) + ", Penalty: ₹" +
String(penalty) + ")");
} else {
Serial.println("Slot " + String(i+1) + ": VACATED (On time)");
}
}

slots[i].occupied = false;
slots[i].reserved = false; // Release reservation
slots[i].reservedDuration = 0;
slots[i].reservationTime = 0;
slots[i].expectedArrival = 0;
slots[i].vehicleNumber = "";
slots[i].phoneNumber = "";

if(slots[i].occupiedTime > 0) {
slots[i].totalTime += parkedTime;
if (!slots[i].reserved) {
// Add revenue for walk-in customer
totalRevenue += (parkedTime / 3600000.0) * PRICE_PER_HOUR;
}
}

Serial.println("Slot " + String(i+1) + ": AVAILABLE (Parked for " +


formatTime(parkedTime / 1000) + ")");
playBeep(1);
recentActivity.push_back(std::string("Slot ") + std::to_string(i+1) + "
available");
if (recentActivity.size() > 5)
recentActivity.erase(recentActivity.begin());
}
slots[i].lastState = currentState;
}

// Auto-release reservations that exceed expected arrival time by 30 minutes


if (slots[i].reserved && !slots[i].occupied && slots[i].expectedArrival > 0) {
if (currentTime > (slots[i].expectedArrival + (30 * 60 * 1000))) { // 30
minutes grace period
Serial.println("Slot " + String(i+1) + ": Reservation auto-cancelled (No-
show)");
slots[i].reserved = false;
slots[i].reservedDuration = 0;
slots[i].reservationTime = 0;
slots[i].expectedArrival = 0;
slots[i].vehicleNumber = "";
slots[i].phoneNumber = "";
// Partial refund (50% penalty for no-show)
totalRevenue -= (slots[i].reservedDuration / 3600) * PRICE_PER_HOUR * 0.5;
recentActivity.push_back(std::string("Slot ") + std::to_string(i+1) + "
reservation cancelled (no-show)");
if (recentActivity.size() > 5)
recentActivity.erase(recentActivity.begin());
}
}
}
}

void handleGateTimeouts() {
// Auto-close entry gate after timeout
if (entryGateOpen && (millis() - entryGateOpenTime > GATE_OPEN_DURATION)) {
bool entryState = !digitalRead(carEnterPin);
if (!entryState) { // No car detected at entry
Serial.println("Entry gate auto-closing");
entryServo.write(CLOSE_ANGLE);
entryGateOpen = false;
totalCarsToday++;
} else {
// Extend timeout if car still present
entryGateOpenTime = millis();
}
}

// Auto-close exit gate after timeout


if (exitGateOpen && (millis() - exitGateOpenTime > GATE_OPEN_DURATION)) {
bool exitState = !digitalRead(carExitPin);
if (!exitState) { // No car detected at exit
Serial.println("Exit gate auto-closing");
exitServo.write(CLOSE_ANGLE);
exitGateOpen = false;
} else {
// Extend timeout if car still present
exitGateOpenTime = millis();
}
}
}

void updateLedStatus() {
unsigned long currentTime = millis();

if (allSlotsFull) {
// Fast blink when parking is full
if (currentTime - lastLedBlink > 250) {
ledState = !ledState;
digitalWrite(ledPin, ledState);
lastLedBlink = currentTime;
}
} else if (entryGateOpen || exitGateOpen) {
// Slow blink when gates are active
if (currentTime - lastLedBlink > 500) {
ledState = !ledState;
digitalWrite(ledPin, ledState);
lastLedBlink = currentTime;
}
} else {
// Solid ON when system is ready and available
digitalWrite(ledPin, HIGH);
ledState = true;
}
}

void updateParkingTimes() {
for(int i = 0; i < TOTAL_SLOTS; i++) {
if(slots[i].occupied && slots[i].occupiedTime > 0) {
// Update current parking time for occupied slots
}
}
}

void updateStatistics() {
// Calculate average parking time
unsigned long totalTimeSum = 0;
int completedParkings = 0;

for(int i = 0; i < TOTAL_SLOTS; i++) {


if(slots[i].totalTime > 0) {
totalTimeSum += slots[i].totalTime;
completedParkings++;
}
}

if(completedParkings > 0) {
averageParkingTime = (float)totalTimeSum / completedParkings / 1000.0 /
60.0; // in minutes
}
}
void playBeep(int times) {
for(int i = 0; i < times; i++) {
digitalWrite(buzzerPin, HIGH);
delay(100);
digitalWrite(buzzerPin, LOW);
delay(100);
}
}

void playWelcomeBeep() {
// Special startup sequence
for(int i = 0; i < 3; i++) {
digitalWrite(buzzerPin, HIGH);
delay(150);
digitalWrite(buzzerPin, LOW);
delay(50);
}
}

String formatTime(unsigned long seconds) {


unsigned long hours = seconds / 3600;
unsigned long minutes = (seconds % 3600) / 60;
unsigned long secs = seconds % 60;

String timeStr = "";


if(hours > 0) timeStr += String(hours) + "h ";
if(minutes > 0) timeStr += String(minutes) + "m ";
timeStr += String(secs) + "s";

return timeStr;
}

String getCurrentTime() {
unsigned long uptime = (millis() - systemStartTime) / 1000;
unsigned long hours = uptime / 3600;
unsigned long minutes = (uptime % 3600) / 60;
unsigned long secs = uptime % 60;
return String(hours) + ":" + (minutes < 10 ? "0" : "") + String(minutes) + ":" +
(secs < 10 ? "0" : "") + String(secs) + " PM"; // Simulate time
}

void setupWebServer() {
// Serve main page
server.on("/", handleRoot);

// API endpoints
server.on("/api/status", handleStatus);
server.on("/api/reset", HTTP_POST, handleReset);
server.on("/api/book", HTTP_POST, handleBook);
server.on("/api/report", HTTP_POST, handleReport);
server.on("/api/cancel", HTTP_POST, handleCancel);

// Serve static files


server.onNotFound(handleNotFound);
}

void handleRoot() {
String html = generateWebPage();
server.send(200, "text/html", html);
}

// Updated handleStatus function to include timing information


void handleStatus() {
DynamicJsonDocument doc(1024);

// System status
doc["timestamp"] = millis();
doc["totalSlots"] = TOTAL_SLOTS;
int physicalOccupied = 0;
int reserved = 0;
for(int i = 0; i < TOTAL_SLOTS; i++) {
if(slots[i].occupied) physicalOccupied++;
if(slots[i].reserved) reserved++;
}
doc["occupiedSlots"] = physicalOccupied;
doc["reservedSlots"] = reserved;
doc["availableSlots"] = TOTAL_SLOTS - physicalOccupied - reserved;
doc["allSlotsFull"] = allSlotsFull;
doc["averageParkingTime"] = averageParkingTime;
doc["revenue"] = totalRevenue;
doc["occupancyRate"] = ((physicalOccupied + reserved) * 100) / TOTAL_SLOTS;
doc["lastUpdated"] = getCurrentTime();

// Slot details with enhanced timing


JsonArray slotsArray = doc.createNestedArray("slots");
for(int i = 0; i < TOTAL_SLOTS; i++) {
JsonObject slot = slotsArray.createNestedObject();
slot["id"] = i + 1;
slot["occupied"] = slots[i].occupied;
slot["reserved"] = slots[i].reserved;
slot["reservedDuration"] = slots[i].reservedDuration; // in seconds
slot["reservationTime"] = slots[i].reservationTime;
slot["expectedArrival"] = slots[i].expectedArrival;

if(slots[i].occupied && slots[i].occupiedTime > 0) {


slot["currentTime"] = (millis() - slots[i].occupiedTime) / 1000;
slot["occupiedTimestamp"] = slots[i].occupiedTime;
} else {
slot["currentTime"] = 0;
slot["occupiedTimestamp"] = 0;
}
slot["totalTime"] = slots[i].totalTime / 1000;
slot["vehicleNumber"] = slots[i].vehicleNumber;
}

// Recent activity
JsonArray activityArray = doc.createNestedArray("recentActivity");
for(auto& act : recentActivity) {
activityArray.add(act);
}

String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}

void handleReset() {
// Reset statistics
totalCarsToday = 0;
averageParkingTime = 0;
totalRevenue = 0;
systemStartTime = millis();

for(int i = 0; i < TOTAL_SLOTS; i++) {


slots[i].totalTime = 0;
}
recentActivity.clear();

server.send(200, "text/plain", "Statistics reset");


Serial.println("Statistics reset via web interface");
}

// Updated handleBook function with arrival time parsing


void handleBook() {
if(server.hasArg("slot") && server.hasArg("duration") && server.hasArg("vehicle")
&& server.hasArg("phone") && server.hasArg("arrival")) {
int slotId = server.arg("slot").toInt() - 1;
unsigned long duration = server.arg("duration").toInt() * 3600; // hours to
seconds
String vehicle = server.arg("vehicle");
String phone = server.arg("phone");
String arrivalStr = server.arg("arrival");

if(slotId >= 0 && slotId < TOTAL_SLOTS && !slots[slotId].occupied && !


slots[slotId].reserved) {
slots[slotId].reserved = true;
slots[slotId].reservedDuration = duration;
slots[slotId].vehicleNumber = vehicle;
slots[slotId].phoneNumber = phone;
slots[slotId].reservationTime = millis();

// Parse arrival time (simplified - in real implementation, parse ISO string)


// For now, set expected arrival to current time + 15 minutes as example
slots[slotId].expectedArrival = millis() + (15 * 60 * 1000);

totalRevenue += (duration / 3600) * PRICE_PER_HOUR; // Prepaid


recentActivity.push_back(vehicle.c_str() + (std::string(" booked slot ") +
std::to_string(slotId + 1)));
if (recentActivity.size() > 5) recentActivity.erase(recentActivity.begin());

server.send(200, "text/plain", "Slot reserved successfully");


Serial.println("Slot " + String(slotId + 1) + " reserved for " + vehicle + "
(" + String(duration/3600) + "h)");
} else {
server.send(400, "text/plain", "Cannot reserve this slot");
}
} else {
server.send(400, "text/plain", "Missing parameters");
}
}

void handleCancel() {
if(server.hasArg("slot")) {
int slotId = server.arg("slot").toInt() - 1;
if(slotId >= 0 && slotId < TOTAL_SLOTS && slots[slotId].reserved && !
slots[slotId].occupied) {
totalRevenue -= (slots[slotId].reservedDuration / 3600) * PRICE_PER_HOUR; //
Refund simulation
slots[slotId].reserved = false;
slots[slotId].reservedDuration = 0;
slots[slotId].reservationTime = 0;
slots[slotId].expectedArrival = 0;
slots[slotId].vehicleNumber = "";
slots[slotId].phoneNumber = "";
recentActivity.push_back(std::string("Slot ") + std::to_string(slotId + 1) +
" reservation cancelled");
if (recentActivity.size() > 5) recentActivity.erase(recentActivity.begin());
server.send(200, "text/plain", "Reservation cancelled");
} else {
server.send(400, "text/plain", "Cannot cancel");
}
} else {
server.send(400, "text/plain", "Missing slot");
}
}

void handleReport() {
if(server.hasArg("message")) {
String message = server.arg("message");
Serial.println("User Report: " + message);
server.send(200, "text/plain", "Message sent");
} else {
server.send(400, "text/plain", "Missing message");
}
}

void handleNotFound() {
server.send(404, "text/plain", "404: Not Found");
}

String generateWebPage() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta charset='utf-8'><meta name='viewport' content='width=device-width,
initial-scale=1'>";
html += "<title>Parking Dashboard</title>";
html += "<style>";
html += "* { box-sizing: border-box; margin: 0; padding: 0; }";
html += "body { background: #1a1d28; color: #fff; font-family: 'Segoe UI', sans-
serif; padding: 20px; }";
html += ".header { display: flex; justify-content: space-between; align-items:
center; margin-bottom: 20px; }";
html += ".header h1 { font-size: 1.5em; }";
html += ".header p { opacity: 0.7; font-size: 0.9em; }";
html += ".update-info { font-size: 0.8em; opacity: 0.7; }";
html += ".refresh { background: none; border: none; color: #fff; cursor: pointer;
}";
html += ".stats { display: grid; grid-template-columns: repeat(4, 1fr); gap:
10px; margin-bottom: 20px; }";
html += ".stat-card { background: #2a2f3d; padding: 10px; border-radius: 8px;
text-align: left; }";
html += ".stat-card .icon { font-size: 1.5em; margin-bottom: 5px; }";
html += ".stat-card .value { font-size: 1.2em; font-weight: bold; }";
html += ".stat-card .change { font-size: 0.8em; color: #28a745; }";
html += ".legend { display: flex; align-items: center; gap: 10px; margin-bottom:
10px; font-size: 0.9em; }";
html += ".dot { width: 10px; height: 10px; border-radius: 50%; }";
html += ".green { background: #28a745; } .red { background: #dc3545; } .orange
{ background: #ffc107; }";
html += ".parking-section { margin-bottom: 10px; }";
html += ".parking-section h3 { font-size: 1em; }";
html += ".slots-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap:
10px; margin-bottom: 20px; }";
html += ".slot-card { background: #2a2f3d; padding: 10px; border-radius: 8px;
text-align: center; position: relative; cursor: pointer; transition: all 0.3s ease;
}";
html += ".slot-card:hover { transform: translateY(-2px); }";
html += ".slot-card.available:hover { box-shadow: 0 4px 8px rgba(40, 167, 69,
0.3); }";
html += ".slot-card .dot { position: absolute; top: 10px; left: 10px; }";
html += ".slot-card .name { font-weight: bold; margin-bottom: 5px; }";
html += ".slot-card .status { font-size: 0.8em; opacity: 0.8; margin-bottom: 3px;
}";
html += ".slot-card .timer { font-size: 0.7em; color: #ffc107; margin-bottom:
3px; }";
html += ".slot-card .price { font-size: 0.8em; color: #28a745; }";
html += ".recent-activity { background: #2a2f3d; padding: 10px; border-radius:
8px; margin-bottom: 20px; }";
html += ".recent-activity h3 { font-size: 1em; margin-bottom: 10px; }";
html += ".activity-item { display: flex; justify-content: space-between; font-
size: 0.8em; opacity: 0.8; margin-bottom: 5px; }";
html += ".quick-actions { background: #2a2f3d; padding: 10px; border-radius: 8px;
}";
html += ".quick-actions h3 { font-size: 1em; margin-bottom: 10px; }";
html += ".action-btn { background: #3a3f4d; color: #fff; border: none; padding:
10px; border-radius: 5px; width: 100%; margin-bottom: 10px; cursor: pointer; text-
align: left; }";
html += ".modal { display: none; position: fixed; z-index: 1; left: 0; top: 0;
width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.4);
padding-top: 60px; }";
html += ".modal-content { background-color: #2a2f3d; margin: 5% auto; padding:
20px; border-radius: 8px; width: 80%; max-width: 500px; }";
html += ".modal-header { display: flex; align-items: center; margin-bottom: 20px;
}";
html += ".modal-header .back-btn { background: none; border: none; color: #fff;
font-size: 1.5em; cursor: pointer; margin-right: 10px; }";
html += ".modal-header h2 { flex: 1; text-align: center; }";
html += ".steps { display: flex; justify-content: center; align-items: center;
gap: 10px; margin-bottom: 20px; }";
html += ".step { width: 30px; height: 30px; border-radius: 50%; background:
#6c757d; text-align: center; line-height: 30px; color: #fff; font-weight: bold; }";
html += ".step.active { background: #007bff; }";
html += ".step.completed { background: #28a745; }";
html += ".step-line { width: 40px; height: 2px; background: #6c757d; }";
html += ".step-line.completed { background: #28a745; }";
html += ".step-content { display: none; }";
html += ".step-content.active { display: block; }";
html += ".slot-info { background: #3a3f4d; padding: 15px; border-radius: 8px;
margin-bottom: 20px; display: flex; align-items: center; gap: 15px; }";
html += ".slot-info .icon { font-size: 2.5em; color: #28a745; }";
html += ".slot-info .details { flex: 1; }";
html += ".slot-info .status { background: #28a745; color: #fff; padding: 3px 8px;
border-radius: 3px; font-size: 0.8em; }";
html += ".form-group { margin-bottom: 15px; }";
html += ".form-group label { display: block; font-size: 0.9em; margin-bottom:
5px; color: #ccc; }";
html += ".form-group input, .form-group select { width: 100%; padding: 12px;
border: none; border-radius: 5px; background: #3a3f4d; color: #fff; font-size: 1em;
}";
html += ".form-group input:focus, .form-group select:focus { outline: 2px solid
#007bff; }";
html += ".prediction-box { background: #3a3f4d; padding: 12px; border-radius:
8px; margin-top: 10px; }";
html += ".prediction-box .label { font-size: 0.8em; color: #ccc; margin-bottom:
5px; }";
html += ".prediction-box .value { font-size: 1.1em; color: #28a745; font-weight:
bold; }";
html += ".payment-methods { display: grid; grid-template-columns: repeat(2, 1fr);
gap: 10px; margin-bottom: 20px; }";
html += ".payment-method { background: #3a3f4d; padding: 15px; border-radius:
8px; text-align: center; cursor: pointer; transition: all 0.3s ease; }";
html += ".payment-method:hover { background: #4a4f5d; }";
html += ".payment-method.active { background: #007bff; }";
html += ".total-amount { display: flex; justify-content: space-between; align-
items: center; background: #3a3f4d; padding: 15px; border-radius: 8px; margin-
bottom: 20px; }";
html += ".total-amount .value { color: #28a745; font-weight: bold; font-size:
1.2em; }";
html += ".buttons { display: flex; gap: 10px; }";
html += ".buttons button { flex: 1; padding: 12px; border: none; border-radius:
5px; cursor: pointer; font-size: 1em; }";
html += ".back-btn { background: #6c757d; color: #fff; }";
html += ".next-btn { background: #007bff; color: #fff; }";
html += ".pay-btn { background: linear-gradient(to right, #007bff, #6f42c1);
color: #fff; }";
html += ".my-bookings { background: #2a2f3d; padding: 10px; border-radius: 8px;
display: none; }";
html += ".my-bookings h3 { font-size: 1em; margin-bottom: 10px; }";
html += ".booking-item { background: #3a3f4d; padding: 10px; border-radius: 5px;
margin-bottom: 10px; display: flex; justify-content: space-between; align-items:
center; }";
html += ".cancel-btn { background: #dc3545; color: #fff; border: none; padding:
5px 10px; border-radius: 3px; cursor: pointer; }";
html += ".report-modal .modal-content { max-width: 400px; }";
html += ".report-modal textarea { width: 100%; height: 120px; padding: 12px;
border: none; border-radius: 5px; background: #3a3f4d; color: #fff; margin-bottom:
15px; resize: vertical; }";
html += ".report-modal textarea:focus { outline: 2px solid #007bff; }";
html += "@media (max-width: 768px) { .stats { grid-template-columns: repeat(2,
1fr); } .slots-grid { grid-template-columns: repeat(2, 1fr); } .payment-methods
{ grid-template-columns: 1fr; } }";
html += "</style></head><body>";

// Header
html += "<div class='header'>";
html += "<div>";
html += "<h1>Parking Dashboard</h1>";
html += "<p>Real-time parking management system</p>";
html += "</div>";
html += "<div class='update-info'>";
html += "Last updated: <span id='last-updated'></span> <button class='refresh'
onclick='updateData()'>⭮ Refresh</button>";
html += "</div>";
html += "</div>";
// Stats
html += "<div class='stats'>";
html += "<div class='stat-card'><div class='icon'>🚗</div><div class='value'
id='available-slots'>0/6</div>Available Slots<div class='change' id='available-
change'>+0 from last hour</div></div>";
html += "<div class='stat-card'><div class='icon'>👥</div><div class='value'
id='active-bookings'>0</div>Active Bookings<div class='change' id='bookings-
change'>0 ending soon</div></div>";
html += "<div class='stat-card'><div class='icon'>📈</div><div class='value'
id='occupancy-rate'>0%</div>Occupancy Rate<div class='change' id='occupancy-
change'>0% change today</div></div>";
html += "<div class='stat-card'><div class='icon'>💰</div><div class='value'
id='revenue'>₹0</div>Revenue Today<div class='change'
id='revenue-change'>+0%</div></div>";
html += "</div>";

// Legend
html += "<div class='legend'>";
html += "<span class='dot green'></span> Available <span class='dot red'></span>
Occupied <span class='dot orange'></span> Reserved <i>Live Updates</i>";
html += "</div>";

// Parking Slots (Removed booking button)


html += "<div class='parking-section'>";
html += "<h3> Parking Slots - Level 1 - Ground Floor</h3>";
html += "</div>";
html += "<div class='slots-grid'>";
for(int i = 0; i < TOTAL_SLOTS; i++) {
html += "<div id='slot" + String(i+1) + "' class='slot-card'>";
html += "<span class='dot'></span>";
html += "<div class='name'>A" + String(i+1) + "</div>";
html += "<div class='status'>Available</div>";
html += "<div class='timer' id='timer" + String(i+1) + "'></div>";
html += "<div class='price'>₹50/hr</div>";
html += "</div>";
}
html += "</div>";

// Recent Activity
html += "<div class='recent-activity'>";
html += "<h3>🔔 Recent Activity</h3>";
html += "<div id='activity-list'></div>";
html += "</div>";

// Quick Actions (Removed New Booking button)


html += "<div class='quick-actions'>";
html += "<h3>Quick Actions</h3>";
html += "<button class='action-btn' onclick='toggleMyBookings()'>📋 My
Bookings</button>";
html += "<button class='action-btn' onclick='openReportModal()'>📝 Report
Issue</button>";
html += "</div>";

// My Bookings
html += "<div class='my-bookings' id='my-bookings'>";
html += "<h3>My Bookings</h3>";
html += "<div id='bookings-list'></div>";
html += "</div>";
// 3-Step Booking Modal
html += "<div id='bookingModal' class='modal'>";
html += "<div class='modal-content'>";
html += "<div class='modal-header'>";
html += "<button class='back-btn' onclick='closeBookingModal()'>←</button>";
html += "<h2>Book Parking Slot</h2>";
html += "</div>";

// Steps indicator
html += "<div class='steps'>";
html += "<div class='step active' id='step1'>1</div>";
html += "<div class='step-line' id='line1'></div>";
html += "<div class='step' id='step2'>2</div>";
html += "<div class='step-line' id='line2'></div>";
html += "<div class='step' id='step3'>3</div>";
html += "</div>";

// Step 1: Slot Selection and Basic Details


html += "<div class='step-content active' id='step-content-1'>";
html += "<div class='slot-info'>";
html += "<div class='icon'>🚗</div>";
html += "<div class='details'>";
html += "<div class='name' id='selected-slot'>Select a Slot</div>";
html += "<div>Ground Floor - Section A</div>";
html += "<div class='price'>₹50/hour</div>";
html += "<div class='status'>Available</div>";
html += "</div>";
html += "</div>";
html += "<div class='form-group'>";
html += "<label>Parking Duration</label>";
html += "<select id='duration' onchange='updatePredictions()'>";
html += "<option value='1'>1 hour - ₹50</option>";
html += "<option value='2'>2 hours - ₹100</option>";
html += "<option value='3'>3 hours - ₹150</option>";
html += "<option value='4'>4 hours - ₹200</option>";
html += "<option value='6'>6 hours - ₹300</option>";
html += "<option value='8'>8 hours - ₹400</option>";
html += "</select>";
html += "</div>";
html += "<div class='form-group'>";
html += "<label>Expected Arrival Time</label>";
html += "<input type='datetime-local' id='arrival-time'
onchange='updatePredictions()'>";
html += "</div>";
html += "<div class='prediction-box'>";
html += "<div class='label'>Predicted Exit Time:</div>";
html += "<div class='value' id='predicted-exit'>--:--</div>";
html += "</div>";
html += "<div class='buttons'>";
html += "<button class='back-btn' onclick='closeBookingModal()'>Cancel</button>";
html += "<button class='next-btn' onclick='nextStep(1)'>Next</button>";
html += "</div>";
html += "</div>";

// Step 2: Vehicle Details


html += "<div class='step-content' id='step-content-2'>";
html += "<div class='form-group'>";
html += "<label>Vehicle Number</label>";
html += "<input type='text' id='vehicle-number' placeholder='MH01AB1234'
style='text-transform: uppercase;'>";
html += "</div>";
html += "<div class='form-group'>";
html += "<label>Phone Number</label>";
html += "<input type='text' id='phone-number' placeholder='+91 9876543210'>";
html += "</div>";
html += "<div class='buttons'>";
html += "<button class='back-btn' onclick='previousStep(2)'>Back</button>";
html += "<button class='next-btn' onclick='nextStep(2)'>Next</button>";
html += "</div>";
html += "</div>";

// Step 3: Payment
html += "<div class='step-content' id='step-content-3'>";
html += "<div class='form-group'>";
html += "<label>Payment Method</label>";
html += "<div class='payment-methods'>";
html += "<div class='payment-method active' data-method='upi'>📱 UPI
Payment</div>";
html += "<div class='payment-method' data-method='card'>💳 Credit/Debit
Card</div>";
html += "<div class='payment-method' data-method='net'>🏦 Net Banking</div>";
html += "<div class='payment-method' data-method='wallet'>👛 Digital
Wallet</div>";
html += "</div>";
html += "</div>";
html += "<div class='total-amount'>";
html += "<div>Total Amount: <span id='total-hours'>1 hour x
₹50/hour</span></div>";
html += "<div class='value' id='total-value'>₹50</div>";
html += "</div>";
html += "<div class='buttons'>";
html += "<button class='back-btn' onclick='previousStep(3)'>Back</button>";
html += "<button class='pay-btn' onclick='payAndBook()'>Pay <span id='pay-
amount'>₹50</span></button>";
html += "</div>";
html += "</div>";
html += "</div>";
html += "</div>";

// Report Modal with back button


html += "<div id='reportModal' class='modal report-modal'>";
html += "<div class='modal-content'>";
html += "<div class='modal-header'>";
html += "<button class='back-btn' onclick='closeReportModal()'>←</button>";
html += "<h2>Report Issue</h2>";
html += "</div>";
html += "<textarea id='reportMessage' placeholder='Describe the issue you are
facing...'></textarea>";
html += "<div class='buttons'>";
html += "<button class='back-btn' onclick='closeReportModal()'>Cancel</button>";
html += "<button class='pay-btn' onclick='sendReport()'>Send Report</button>";
html += "</div>";
html += "<p id='reportStatus' style='margin-top: 10px; text-align: center; color:
#28a745;'></p>";
html += "</div>";
html += "</div>";

html += "<script>";
html += "let selectedSlot = null; let selectedMethod = 'upi'; let totalAmount =
50; let currentStep = 1;";

// Step navigation functions


html += "function nextStep(step) {";
html += " if (step === 1 && (!selectedSlot || !document.getElementById('arrival-
time').value)) {";
html += " alert('Please select a slot and arrival time'); return;";
html += " }";
html += " if (step === 2 && (!document.getElementById('vehicle-number').value
|| !document.getElementById('phone-number').value)) {";
html += " alert('Please fill all vehicle details'); return;";
html += " }";
html += " document.getElementById('step-content-' +
step).classList.remove('active');";
html += " document.getElementById('step' + step).classList.remove('active');";
html += " document.getElementById('step' + step).classList.add('completed');";
html += " document.getElementById('line' + step).classList.add('completed');";
html += " currentStep = step + 1;";
html += " document.getElementById('step-content-' +
currentStep).classList.add('active');";
html += " document.getElementById('step' +
currentStep).classList.add('active');";
html += "}";

html += "function previousStep(step) {";


html += " document.getElementById('step-content-' +
step).classList.remove('active');";
html += " document.getElementById('step' + step).classList.remove('active');";
html += " currentStep = step - 1;";
html += " document.getElementById('step-content-' +
currentStep).classList.add('active');";
html += " document.getElementById('step' +
currentStep).classList.remove('completed');";
html += " document.getElementById('step' +
currentStep).classList.add('active');";
html += " if (currentStep > 1) document.getElementById('line' +
currentStep).classList.remove('completed');";
html += "}";

// Prediction function
html += "function updatePredictions() {";
html += " let arrivalTime = document.getElementById('arrival-time').value;";
html += " let duration = parseInt(document.getElementById('duration').value);";
html += " if (arrivalTime && duration) {";
html += " let arrival = new Date(arrivalTime);";
html += " let exit = new Date(arrival.getTime() + (duration * 60 * 60 * 1000));";
html += " document.getElementById('predicted-exit').innerText =
exit.toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit' });";
html += " totalAmount = duration * 50;";
html += " document.getElementById('total-hours').innerText = duration + ' hours x
₹50/hour';";
html += " document.getElementById('total-value').innerText = '₹' + totalAmount;";
html += " document.getElementById('pay-amount').innerText = '₹' + totalAmount;";
html += " }";
html += "}";

// Initialize arrival time to current time and set minimum date


html += "function initializeArrivalTime() {";
html += " let now = new Date();";
html += " now.setMinutes(now.getMinutes() - now.getTimezoneOffset());";
html += " let arrivalInput = document.getElementById('arrival-time');";
html += " arrivalInput.value = now.toISOString().slice(0, 16);";
html += " arrivalInput.min = now.toISOString().slice(0, 16);"; // Prevent past
dates
html += " updatePredictions();";
html += "}";

html += "function openBookingModal() {";


html += " currentStep = 1;";
html += " document.querySelectorAll('.step').forEach(s =>
s.classList.remove('active', 'completed'));";
html += " document.querySelectorAll('.step-line').forEach(l =>
l.classList.remove('completed'));";
html += " document.querySelectorAll('.step-content').forEach(sc =>
sc.classList.remove('active'));";
html += " document.getElementById('step1').classList.add('active');";
html += " document.getElementById('step-content-1').classList.add('active');";
html += " initializeArrivalTime();";
html += " document.getElementById('bookingModal').style.display = 'block';";
html += "}";

html += "function closeBookingModal() {";


html += " document.getElementById('bookingModal').style.display = 'none';";
html += " selectedSlot = null;";
html += " document.getElementById('selected-slot').innerText = 'Select a Slot';";
html += "}";

html += "function openReportModal()


{ document.getElementById('reportModal').style.display = 'block'; }";
html += "function closeReportModal()
{ document.getElementById('reportModal').style.display = 'none';
document.getElementById('reportMessage').value = '';
document.getElementById('reportStatus').innerText = ''; }";
html += "function toggleMyBookings() { document.getElementById('my-
bookings').style.display = document.getElementById('my-bookings').style.display ===
'block' ? 'none' : 'block'; }";

// Payment method selection


html += "document.querySelectorAll('.payment-method').forEach(m => {";
html += " m.addEventListener('click', () => {";
html += " document.querySelectorAll('.payment-method').forEach(mm =>
mm.classList.remove('active'));";
html += " m.classList.add('active');";
html += " selectedMethod = m.dataset.method;";
html += " });";
html += "});";

html += "function payAndBook() {";


html += " let duration = document.getElementById('duration').value;";
html += " let vehicle = document.getElementById('vehicle-
number').value.toUpperCase();";
html += " let phone = document.getElementById('phone-number').value;";
html += " let arrival = document.getElementById('arrival-time').value;";
html += " if (selectedSlot && duration && vehicle && phone && arrival) {";
html += " let arrivalDate = new Date(arrival);";
html += " if (arrivalDate < new Date()) {";
html += " alert('Arrival time cannot be in the past!'); return;";
html += " }";
html += " fetch('/api/book', { method: 'POST', headers: { 'Content-Type':
'application/x-www-form-urlencoded' }, body: 'slot=' + selectedSlot + '&duration='
+ duration + '&vehicle=' + encodeURIComponent(vehicle) + '&phone=' +
encodeURIComponent(phone) + '&arrival=' + encodeURIComponent(arrival) })";
html += " .then(res => res.text()).then(data => { alert('Booking confirmed!
Payment of ₹' + totalAmount + ' processed.\\nSlot: A' + selectedSlot + '\\nVehicle:
' + vehicle + '\\nDuration: ' + duration + ' hours'); closeBookingModal();
updateData(); });";
html += " } else { alert('Please complete all steps and ensure arrival time is
not in the past'); }";
html += "}";

// Timer calculation function with booking status


html += "function calculateRemainingTime(occupiedTime, reservedDuration,
isOccupied, arrivalTime) {";
html += " let now = Date.now();";
html += " if (!isOccupied && reservedDuration > 0) {";
html += " return 'Booked - Awaiting';";
html += " }";
html += " if (isOccupied && occupiedTime && reservedDuration) {";
html += " let endTime = occupiedTime + (reservedDuration * 1000);";
html += " let remaining = endTime - now;";
html += " if (remaining <= 0) return 'Time Expired';";
html += " let hours = Math.floor(remaining / (60 * 60 * 1000));";
html += " let minutes = Math.floor((remaining % (60 * 60 * 1000)) / (60 *
1000));";
html += " let seconds = Math.floor((remaining % (60 * 1000)) / 1000);";
html += " if (hours > 0) return hours + 'h ' + minutes + 'm left';";
html += " if (minutes > 0) return minutes + 'm ' + seconds + 's left';";
html += " return seconds + 's left';";
html += " }";
html += " return '';";
html += "}";

html += "function updateData() {";


html += " fetch('/api/status')";
html += " .then(res => res.json())";
html += " .then(data => {";
html += " document.getElementById('available-slots').innerText =
data.availableSlots + '/' + data.totalSlots;";
html += " document.getElementById('active-bookings').innerText =
data.occupiedSlots + data.reservedSlots;";
html += " document.getElementById('occupancy-rate').innerText =
data.occupancyRate + '%';";
html += " document.getElementById('revenue').innerText = '₹' + data.revenue;";
html += " document.getElementById('last-updated').innerText = data.lastUpdated;";

// Update activity list


html += " let activityList = document.getElementById('activity-list');
activityList.innerHTML = '';";
html += " data.recentActivity.forEach(act => { let item =
document.createElement('div'); item.className = 'activity-item'; item.innerText =
act; activityList.appendChild(item); });";

// Update bookings list


html += " let bookingsList = document.getElementById('bookings-list');
bookingsList.innerHTML = '';";
// Update slots with enhanced timer functionality and status management
html += " for (let i = 0; i < 6; i++) {";
html += " let slotData = data.slots[i];";
html += " let slotDiv = document.getElementById('slot' + (i + 1));";
html += " let dot = slotDiv.querySelector('.dot');";
html += " let status = slotDiv.querySelector('.status');";
html += " let timer = document.getElementById('timer' + (i + 1));";

html += " let occupiedTime = slotData.occupied ? (data.timestamp -


(slotData.currentTime * 1000)) : 0;";
html += " let remainingTime = calculateRemainingTime(occupiedTime,
slotData.reservedDuration, slotData.occupied, 0);";

html += " if (slotData.occupied && slotData.reserved) {";


html += " if (remainingTime === 'Time Expired') {";
html += " dot.className = 'dot red'; status.innerText = 'Overstaying';";
html += " timer.innerText = 'Time Expired - ₹10/min penalty';";
html += " } else {";
html += " dot.className = 'dot red'; status.innerText = 'Occupied';";
html += " timer.innerText = remainingTime;";
html += " }";
html += " slotDiv.classList.remove('available'); slotDiv.onclick = null;";
html += " }";
html += " else if (slotData.occupied && !slotData.reserved) {";
html += " dot.className = 'dot red'; status.innerText = 'Occupied
(Unreserved)';";
html += " timer.innerText = 'No booking found';";
html += " slotDiv.classList.remove('available'); slotDiv.onclick = null;";
html += " }";
html += " else if (!slotData.occupied && slotData.reserved) {";
html += " dot.className = 'dot orange'; status.innerText = 'Booked';";
html += " timer.innerText = 'Awaiting arrival';";
html += " slotDiv.classList.remove('available'); slotDiv.onclick = null;";
html += " }";
html += " else {";
html += " dot.className = 'dot green'; status.innerText = 'Available';";
html += " slotDiv.classList.add('available'); timer.innerText = '';";
html += " slotDiv.onclick = () => { selectedSlot = i + 1;
document.getElementById('selected-slot').innerText = 'Slot A' + (i + 1);
openBookingModal(); };";
html += " }";

// Add to bookings list if reserved


html += " if (slotData.reserved) {";
html += " let bookingItem = document.createElement('div'); bookingItem.className
= 'booking-item';";
html += " bookingItem.innerHTML = '<div>Slot A' + (i + 1) + ' - ' +
slotData.vehicleNumber + '</div><button class=\"cancel-btn\"
onclick=\"cancelBooking(' + (i + 1) + ')\">Cancel</button>';";
html += " bookingsList.appendChild(bookingItem);";
html += " }";
html += " }";
html += " });";
html += "}";

html += "function cancelBooking(slot) {";


html += " if (confirm('Are you sure you want to cancel this booking?')) {";
html += " fetch('/api/cancel', { method: 'POST', headers: { 'Content-Type':
'application/x-www-form-urlencoded' }, body: 'slot=' + slot })";
html += " .then(res => res.text()).then(data => { alert(data);
updateData(); });";
html += " }";
html += "}";

html += "function sendReport() {";


html += " let msg = document.getElementById('reportMessage').value.trim();";
html += " if (msg) {";
html += " fetch('/api/report', { method: 'POST', headers: { 'Content-Type':
'application/x-www-form-urlencoded' }, body: 'message=' +
encodeURIComponent(msg) })";
html += " .then(res => res.text()).then(data => {";
html += " document.getElementById('reportStatus').innerText = 'Report sent
successfully!';";
html += " setTimeout(() => { closeReportModal(); }, 2000);";
html += " });";
html += " } else {";
html += " alert('Please enter a message');";
html += " }";
html += "}";

// Auto-update data every 1 second for real-time timer updates


html += "setInterval(updateData, 1000); updateData();";
html += "</script>";

html += "</body></html>";

return html;
}

You might also like