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)
#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.
#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.
#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
}