Back to All Blog Posts
Tutorial EltroNerd Engineering

Car Parking Management System with Slot Detection using ESP32

A Car Parking Management System with Slot Detection & License Plate Capture combines IoT sensors with computer vision to deliver a complete solution for modern parking challenges.

Car Parking Management System with Slot Detection & License Plate Capture

Efficient parking management is crucial in modern urban environments. A smart parking system not only detects slot occupancy but also identifies vehicles entering and exiting the premises. By integrating ESP32 with ultrasonic sensors for slot detection and an ESP32-CAM module for capturing license plates, you can build a cost-effective and scalable solution for parking automation.


System Overview

  • ESP32 (standard) manages slot sensors and communication.
  • ESP32-CAM captures the vehicle’s license plate at entry/exit gates.
  • Ultrasonic sensors detect whether a parking slot is occupied.
  • Wi-Fi/MQTT/WebSocket is used to send updates to a backend.
  • Cloud dashboard or mobile app displays slot availability and records vehicle logs with images.

Features

  • Real-time slot occupancy detection
  • License plate capture on entry and exit
  • Vehicle log history with timestamp and image
  • Centralized dashboard for administrators
  • Optional integration with payment systems and automatic gates
  • Scalable design supporting multiple slots and multiple parking sites

Hardware Components

  • ESP32 development board (for slot management)
  • ESP32-CAM module (for license plate capture)
  • Ultrasonic sensors (e.g., HC-SR04) or IR sensors
  • LED indicators (green/free, red/occupied)
  • Relay module (for barrier gate control)
  • Power supply with automotive-grade regulation
  • Wi-Fi router or LoRa gateway

Slot Detection Example (ESP32 + Ultrasonic Sensor)

c
#include <WiFi.h>
#include <HTTPClient.h>

#define TRIG_PIN 5
#define ECHO_PIN 18

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* serverUrl = "http://your-backend/api/parking/update";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting...");
  }
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void loop() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH);
  int distance = duration * 0.034 / 2;
  bool isOccupied = (distance < 20);

  String payload = "{\"slot_id\":1,\"status\":\"" + String(isOccupied ? "occupied" : "free") + "\"}";

  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverUrl);
    http.addHeader("Content-Type", "application/json");
    http.POST(payload);
    http.end();
  }

  delay(5000);
}

The ESP32-CAM can capture an image whenever a vehicle enters or exits. The image can then be uploaded to a backend server for license plate recognition (LPR) using OpenCV, TensorFlow, or an external API.

c
#include "esp_camera.h"
#include <WiFi.h>
#include <HTTPClient.h>

// Camera PIN configuration (AI Thinker module)
#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* serverUrl = "http://your-backend/api/parking/upload";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer   = LEDC_TIMER_0;
  config.pin_d0       = Y2_GPIO_NUM;
  config.pin_d1       = Y3_GPIO_NUM;
  config.pin_d2       = Y4_GPIO_NUM;
  config.pin_d3       = Y5_GPIO_NUM;
  config.pin_d4       = Y6_GPIO_NUM;
  config.pin_d5       = Y7_GPIO_NUM;
  config.pin_d6       = Y8_GPIO_NUM;
  config.pin_d7       = Y9_GPIO_NUM;
  config.pin_xclk     = XCLK_GPIO_NUM;
  config.pin_pclk     = PCLK_GPIO_NUM;
  config.pin_vsync    = VSYNC_GPIO_NUM;
  config.pin_href     = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn     = PWDN_GPIO_NUM;
  config.pin_reset    = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size   = FRAMESIZE_VGA;
  config.jpeg_quality = 10;
  config.fb_count     = 1;

  if (esp_camera_init(&config) != ESP_OK) {
    Serial.println("Camera init failed");
    return;
  }
}

void loop() {
  camera_fb_t* fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return;
  }

  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverUrl);
    http.addHeader("Content-Type", "image/jpeg");
    int res = http.POST(fb->buf, fb->len);
    if (res > 0) {
      Serial.println("Image uploaded successfully");
    }
    http.end();
  }

  esp_camera_fb_return(fb);
  delay(10000); // Capture every 10s or trigger via sensor
}

Combined Code Logic

Integration of slot detection and vehicle number plate capture into a single ESP32-CAM program.

  • Ultrasonic sensor detects if a slot is occupied.
  • ESP32-CAM captures the image of the car’s number plate when a vehicle is detected.
c
#include "esp_camera.h"
#include <WiFi.h>
#include <HTTPClient.h>

// WiFi credentials
const char* ssid = "your-SSID";
const char* password = "your-PASSWORD";

// Server endpoints
const char* slotServerURL = "http://your-server/parking/update";
const char* captureServerURL = "http://your-server/parking/capture";

// Ultrasonic pins
#define TRIG_PIN 12
#define ECHO_PIN 13

// Distance threshold for slot occupation (cm)
#define OCCUPIED_DISTANCE 15

long duration;
int distance;
bool carDetected = false;

// Camera configuration (ESP32-CAM AI Thinker)
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM     0
#define SIOD_GPIO_NUM    26
#define SIOC_GPIO_NUM    27

#define Y9_GPIO_NUM      35
#define Y8_GPIO_NUM      34
#define Y7_GPIO_NUM      39
#define Y6_GPIO_NUM      36
#define Y5_GPIO_NUM      21
#define Y4_GPIO_NUM      19
#define Y3_GPIO_NUM      18
#define Y2_GPIO_NUM       5
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22

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

  // Init ultrasonic pins
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  // Connect WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected");

  // Init camera
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_VGA; 
  config.jpeg_quality = 10;
  config.fb_count = 1;

  if (esp_camera_init(&config) != ESP_OK) {
    Serial.println("Camera init failed!");
    return;
  }
  Serial.println("Camera ready");
}

void loop() {
  // Measure distance
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  duration = pulseIn(ECHO_PIN, HIGH);
  distance = duration * 0.034 / 2;

  String slotStatus = (distance < OCCUPIED_DISTANCE) ? "occupied" : "free";

  // Send slot status
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(slotServerURL);
    http.addHeader("Content-Type", "application/json");
    String jsonData = "{\"slot\":\"1\", \"status\":\"" + slotStatus + "\"}";
    http.POST(jsonData);
    http.end();
  }

  // Capture number plate if car just arrived
  if (slotStatus == "occupied" && !carDetected) {
    carDetected = true;
    Serial.println("Car detected! Capturing image...");

    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      return;
    }

    if (WiFi.status() == WL_CONNECTED) {
      HTTPClient http;
      http.begin(captureServerURL);
      http.addHeader("Content-Type", "image/jpeg");
      http.POST(fb->buf, fb->len);
      http.end();
    }

    esp_camera_fb_return(fb);
  }

  // Reset flag when car leaves
  if (slotStatus == "free") {
    carDetected = false;
  }

  delay(5000); // Check every 5 seconds
}

Try this project in your browser

Compile code and simulate hardware output with EltroNerd Cloud IDE.

Launch Cloud IDE