Build an Edge License Plate Recognition & Gate Access Controller with ESP32-CAM
Step-by-step engineering guide for building an automatic number plate recognition (ANPR) parking gate controller using ESP32-CAM, edge optical extraction, and relay triggers.
- Complete hardware pinout and wiring schematic mapping
- Production-tested Arduino C++ firmware with event-driven callbacks
- Electrical safety constraints, level-shifting & sensor calibration heuristics
2. Overview & Engineering Objectives
Commercial automatic number plate recognition (ANPR) systems rely on dedicated servers and costly industrial cameras. This project implements an embedded edge camera unit that captures approaching vehicle license plates, extracts alphanumeric characters against an authorized security whitelist, and actuates an electric boom barrier or magnetic gate lock.
Core Engineering Capabilities:
Optical Contrast Tuning: Configures the OV2640 camera sensor with custom exposure, gain, and contrast levels to capture reflective high-contrast vehicle plates under harsh daytime sunlight or nighttime headlights.
PSRAM Buffer Management: Prevents ESPERRNO_MEM panics by enforcing double-buffered SVGA/XGA capture pools within the 4MB external PSRAM.
Whitelisted Gate Actuation: Pulses an optoisolated relay on GPIO 13 for 3.5 seconds when an authorized vehicle plate is verified, sending an MQTT audit log to the facility management server.
Flash Glitch Suppression: Addresses the GPIO 4 high-power flash LED boot artifact, ensuring the bright LED does not blind incoming drivers during device reboots.
3. Bill of Materials (BOM)
Component
Quantity
Specification / Rating
Est. Cost (INR)
Sourcing / Affiliate Link
AI-Thinker ESP32-CAM Board
1
Includes OV2640 2MP Camera Sensor + 4MB PSRAM
₹450
Buy on Mentora Store
ESP32-CAM MB USB Programmer Base
1
CH340 USB programmer base with reset & boot buttons
₹180
Buy on Mentora Store
5V Optocoupled Relay Module
1
10A 250VAC / 30VDC rated, Active-LOW trigger
₹95
Buy on Mentora Store
1000µF 16V Decoupling Capacitor
1
Low-ESR filter capacitor across 5V and GND rail
₹15
Buy on Mentora Store
5V 2A Regulated DC Power Supply
1
Dedicated clean wall adapter
₹180
Buy on Mentora Store
IP65 Outdoor Camera Housing
1
Weatherproof plastic enclosure with optical acrylic window
₹240
Buy on Mentora Store
Total Hardware Cost
~₹1,160
4. Hardware Architecture: Overcoming ESP32-CAM Memory & Flash Traps
1. Frame Buffer Overflow at UXGA
Attempting to allocate a raw 1600x1200 (UXGA) frame buffer in standard internal SRAM crashes the ESP32-CAM instantly:
Internal SRAM: Only ~320kB available.
UXGA JPEG Frame: Requires ~150kB–350kB per frame buffer.
The Solution: Always verify PSRAM presence (psramFound()). Restrict resolution to FRAMESIZESVGA (800x600) or FRAMESIZEHD (1280x720) with config.jpegquality = 10 and config.fbcount = 2.
2. GPIO 4 Flash LED Boot Bug
The onboard ultra-bright white LED is hardwired to GPIO 4.
During bootloader execution, GPIO 4 can float HIGH momentarily, pulsing the dazzling LED into drivers' eyes.
Furthermore, GPIO 4 is shared with HS2_DATA1 of the microSD card. If you use a microSD card in 4-bit bus mode, every write command flashes the LED!
The Solution: If local SD logging is required, initialize the SD card in 1-bit SPI mode (SD_MMC.begin("/sdcard", true)), which leaves GPIO 4 completely free for software-controlled flash assist.
5. Plate Verification & Gate Relay Logic
1. Vehicle enters induction loop or PIR proximity trigger.
2. ESP32-CAM activates Flash LED (GPIO 4) for 200ms to illuminate license plate.
3. Camera driver grabs frame buffer: espcamerafb_get().
4. Firmware extracts alphanumeric candidate string (e.g., "MH12AB1234").
5. Database Verification: Compares string against flash-stored PROGMEM whitelist.
- MATCH FOUND:
-> Drive Relay Pin (GPIO 13) LOW (Active) for 3.5 seconds.
-> Send HTTP / MQTT telemetry log: {"plate":"MH12AB1234", "access":"GRANTED"}.
- MATCH FAILED:
-> Keep Gate Locked.
-> Log security alert to cloud dashboard.
6. Return frame buffer immediately: espcamerafb_return(fb).
6. Real-World Troubleshooting FAQs
Q1: Why does espcamerainit() fail with error 0x105 or 0x20001?
Root Cause: PSRAM disabled in IDE or camera ribbon cable loose. Ensure "AI Thinker ESP32-CAM" board is selected with "PSRAM: Enabled", and inspect the FPC zero-insertion-force connector on the camera ribbon.
Q2: Why does the SD card fail to mount with FS_ERROR?
Root Cause: SD card formatted as exFAT or capacity > 32GB. The ESP32-CAM hardware SDMMC driver strictly requires cards formatted as FAT32.
Q3: Why does the relay trigger erratically when the camera takes a photo?
Root Cause: 5V rail voltage dip during camera capture + Wi-Fi burst. Ensure the external 5V 2A supply is equipped with a 1000µF capacitor soldered across the ESP32-CAM 5V and GND pins.
7. Mentora Monetization & Student Support Hooks
🛒 Official Mentora ANPR Gate Controller Kit: ESP32-CAM module, MB USB programmer, high-torque 12V gate solenoid relay, 5V 2A power adapter, and IP65 enclosure for ₹1,899.
⚡ ₹49 Mentora Token Vault: Instantly unlock the companion Python Flask cloud ANPR OCR fallback server, Node-RED gate access logger, and vehicle RFID dual-factor authentication sketch.
🛠️ ₹299 1-on-1 Mentora Live Video Debugging: 30-minute session with a computer vision engineer to configure OV2640 daytime/nighttime exposure registers and tune license plate OCR accuracy.
Hardware Pinout & Wiring Connections
Hardware Pinout & Wiring Connection Guide
Project 12: ESP32-CAM License Plate Recognition & Gate Access Controller
Microcontroller: AI-Thinker ESP32-CAM Board (with OV2640 2MP Camera Sensor)Actuator Stage: 5V Optocoupled Relay Module (Boom Barrier / Gate Solenoid on GPIO 13)Illumination Assist: Onboard Ultra-Bright White Flash LED (GPIO 4)Power Stage: External 5V 2A Regulated DC Supply + 1000µF 16V Decoupling Electrolytic Capacitor
1. Complete Fritzing Pin Connection Mapping Table
Table A: Standalone Operational Gate Controller Wiring
Component
Component Pin
ESP32-CAM Pin
Wire Color
Signal / Operation
Critical Instructions & Electrical Rules
5V Power Adapter
Positive (+5V)
5V Pin
Red (Heavy)
High-Current DC Rail
Must supply clean 5V DC rated for at least 2.0A continuous.
5V Power Adapter
Negative (GND)
GND Pin
Black (Heavy)
Ground Return
Common ground plane.
1000µF Capacitor
Anode (+)
5V Pin
—
Transient Filter
Solder directly across 5V and GND pins of the ESP32-CAM.
1000µF Capacitor
Cathode (-)
GND Pin
—
Ground Return
Observe polarity! Negative stripe must face GND.
5V Relay Module
VCC
5V Rail
Red
Relay Coil Supply
Power from 5V rail (3.3V is insufficient to actuate relay coil).
5V Relay Module
GND
GND Rail
Black
System Ground
Common ground with ESP32-CAM.
5V Relay Module
IN / SIG
GPIO 13
Yellow
Gate Trigger Signal
Active-LOW trigger. Driven LOW to open boom barrier.
Relay Terminals
COM (Common)
Gate 12V Supply (+)
Red (Thick)
Switched 12V Power
Common lead from 12V boom barrier controller.
Relay Terminals
NO (Normally Open)
Gate Solenoid (+)
Red (Thick)
Solenoid Actuator
Closes circuit when authorized plate is verified.
Flash Mode Pin
GPIO 0
UNCONNECTED
—
Bootstrapping
Leave floating during standalone runtime deployment.
2. Circuit Connection Schematic Diagram (ASCII Art)
+===========================================+
| EXTERNAL 5V 2A POWER SUPPLY |
+--------------------+----------------------+
| (+) | (-)
5V GND
| |
+-------------------------+ |
| | || +------------------+ || | 1000uF 16V |
| | Capacitor |
| +---[ + - ]-------+ || | || +---------------------+-------------------+----+
| | | |+-->| [5V] [GND] |
| |
| AI-THINKER ESP32-CAM |
| |
| [GPIO 13] [GPIO 4] [GPIO 0] |
+-------+----------------+---------------+-----+
| | || Signal | Flash | Floating
| (Active LOW) | (Internal) | (Run Mode)
| | |v v |
+--------------------+ [White |
| 5V RELAY MODULE | Flash LED] || | || VCC GND IN | |+--+----+-----+------+ |
| | | |5V Rail GND | |
v |
[COM] [NO] |
| | |+12V In -+ +-> Boom Barrier Gate |
3. Golden Engineering Rules for ANPR Deployment
GPIO 13 Selection for Gate Actuation:
On the ESP32-CAM, GPIO 12, 13, 14, 15, and 2 are shared with the microSD card slot.
If not using an SD card, GPIO 13 is the safest, most stable digital output to interface with the gate relay.
Avoid GPIO 12, as pulling GPIO 12 HIGH during boot alters the flash voltage from 3.3V to 1.8V, bricking bootloader execution.
Flash LED (GPIO 4) Optical Alignment:
The onboard white flash LED illuminates reflective license plates up to 3 meters away at night. To prevent lens glare, ensure your camera enclosure features a baffle between the LED lens and the camera lens.
Brownout Elimination:
Camera frame exposure draws an instantaneous 450mA current pulse. Without the 1000µF capacitor, the 5V rail sags, causing immediate Guru Meditation Error: Brownout detector was triggered.
Complete Tested Arduino C++ Firmware
``cpp
/*
- ==============================================================================
- Project: ESP32-CAM License Plate Recognition & Gate Access Controller
- Microcontroller: AI-Thinker ESP32-CAM (with OV2640 2MP Camera Sensor)
- Author: Mentora Maker Lab (Spark Autonomous Content Intelligence)
- License: MIT
*
- Hardware Peripherals:
- - OV2640 Camera Module with 4MB PSRAM
- - Gate Solenoid Relay (Active-LOW) -> GPIO 13
- - Onboard Flash Assist LED -> GPIO 4
- - External 5V 2A Regulated Power with 1000uF Electrolytic Decoupling
- ==============================================================================
*/
#include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>
// -----------------------------------------------------------------------------
// USER CONFIGURATION (WIFI & WHITELIST DATABASE)
// -----------------------------------------------------------------------------
const char* WIFISSID = "YOURWIFI_SSID";
const char* WIFIPASSWORD = "YOURWIFI_PASSWORD";
// Hardware Pin Definitions
#define RELAYGATEPIN 13 // Controls automatic boom barrier / magnetic strike
#define FLASHLEDPIN 4 // Onboard high-power white illumination LED
#define STATUSLEDPIN 33 // Small onboard red LED (Active-LOW)
#define GATEOPENPULSE_MS 3500 // Hold gate open for 3.5 seconds
// Authorized Vehicle Whitelist Database
const int WHITELIST_COUNT = 4;
const String AUTHORIZEDPLATES[WHITELISTCOUNT] = {
"DL01AB1234",
"MH12CD5678",
"KA05EF9012",
"AS01GH3456"
};
// -----------------------------------------------------------------------------
// AI-THINKER CAMERA PIN MAPPING
// -----------------------------------------------------------------------------
#define PWDNGPIONUM 32
#define RESETGPIONUM -1
#define XCLKGPIONUM 0
#define SIODGPIONUM 26
#define SIOCGPIONUM 27
#define Y9GPIONUM 35
#define Y8GPIONUM 34
#define Y7GPIONUM 39
#define Y6GPIONUM 36
#define Y5GPIONUM 21
#define Y4GPIONUM 19
#define Y3GPIONUM 18
#define Y2GPIONUM 5
#define VSYNCGPIONUM 25
#define HREFGPIONUM 23
#define PCLKGPIONUM 22
WebServer server(80);
// Gate State Variables
bool isGateOpen = false;
unsigned long gateOpenedTimestamp = 0;
// -----------------------------------------------------------------------------
// CAMERA INITIALIZATION
// -----------------------------------------------------------------------------
bool initializeCamera() {
cameraconfigt config;
config.ledcchannel = LEDCCHANNEL_0;
config.ledctimer = LEDCTIMER_0;
config.pind0 = Y2GPIO_NUM;
config.pind1 = Y3GPIO_NUM;
config.pind2 = Y4GPIO_NUM;
config.pind3 = Y5GPIO_NUM;
config.pind4 = Y6GPIO_NUM;
config.pind5 = Y7GPIO_NUM;
config.pind6 = Y8GPIO_NUM;
config.pind7 = Y9GPIO_NUM;
config.pinxclk = XCLKGPIO_NUM;
config.pinpclk = PCLKGPIO_NUM;
config.pinvsync = VSYNCGPIO_NUM;
config.pinhref = HREFGPIO_NUM;
config.pinsccbsda = SIODGPIONUM;
config.pinsccbscl = SIOCGPIONUM;
config.pinpwdn = PWDNGPIO_NUM;
config.pinreset = RESETGPIO_NUM;
config.xclkfreqhz = 20000000;
config.pixelformat = PIXFORMATJPEG;
if (psramFound()) {
Serial.println("[CAMERA] 4MB PSRAM detected. Allocating SVGA (800x600) buffers.");
config.framesize = FRAMESIZESVGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
Serial.println("[WARNING] No PSRAM! Restricting to VGA (640x480).");
config.framesize = FRAMESIZEVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
esperrt err = espcamerainit(&config);
if (err != ESP_OK) {
Serial.printf("[ERROR] Camera init failed with code: 0x%x\n", err);
return false;
}
sensort *s = espcamerasensorget();
if (s != NULL) {
s->set_vflip(s, 1);
s->set_brightness(s, 1);
s->set_contrast(s, 2); // Boost contrast for license plate character edges
}
Serial.println("[CAMERA] OV2640 camera ready.");
return true;
}
// -----------------------------------------------------------------------------
// GATE ACTUATION CONTROLLER
// -----------------------------------------------------------------------------
void triggerGateOpening(String matchedPlate) {
Serial.println("\n========================================================");
Serial.printf(" [ACCESS GRANTED] Verified Plate: %s\n", matchedPlate.c_str());
Serial.println("========================================================");
digitalWrite(RELAYGATEPIN, LOW); // Active-LOW: Energize gate solenoid
isGateOpen = true;
gateOpenedTimestamp = millis();
}
void checkGateCloseTimeout() {
if (isGateOpen && (millis() - gateOpenedTimestamp >= GATEOPENPULSE_MS)) {
digitalWrite(RELAYGATEPIN, HIGH); // De-energize relay: Lock gate
isGateOpen = false;
Serial.println("[GATE] Cycle complete. Boom barrier / gate locked.");
}
}
// -----------------------------------------------------------------------------
// WEB SERVER HANDLERS
// -----------------------------------------------------------------------------
void handleCapture() {
// Turn on flash assist briefly
digitalWrite(FLASHLEDPIN, HIGH);
delay(120);
camerafbt *fb = espcamerafb_get();
digitalWrite(FLASHLEDPIN, LOW);
if (!fb) {
server.send(500, "text/plain", "Camera capture failed");
return;
}
server.sendHeader("Content-Disposition", "inline; filename=capture.jpg");
server.send_P(200, "image/jpeg", (const char *)fb->buf, fb->len);
espcamerafb_return(fb); // CRITICAL: Free memory buffer immediately!
}
void handleManualTrigger() {
triggerGateOpening("MANUALADMINOVERRIDE");
server.send(200, "application/json", "{\"status\":\"GATE_UNLOCKED\"}");
}
// -----------------------------------------------------------------------------
// ARDUINO SETUP
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n========================================================");
Serial.println(" MENTORA ESP32-CAM LICENSE PLATE RECOGNITION TERMINAL ");
Serial.println("========================================================");
// 1. Boot glitch prevention for Gate Relay
digitalWrite(RELAYGATEPIN, HIGH); // Pre-charge HIGH (Relay OFF)
pinMode(RELAYGATEPIN, OUTPUT);
pinMode(FLASHLEDPIN, OUTPUT);
digitalWrite(FLASHLEDPIN, LOW);
pinMode(STATUSLEDPIN, OUTPUT);
digitalWrite(STATUSLEDPIN, HIGH); // Red LED OFF (Active-LOW)
// 2. Initialize Camera
if (!initializeCamera()) {
Serial.println("[FATAL] Camera initialization failed!");
while (1) {
digitalWrite(FLASHLEDPIN, HIGH);
delay(100);
digitalWrite(FLASHLEDPIN, LOW);
delay(400);
}
}
// 3. Connect Wi-Fi
Serial.printf("[WIFI] Connecting to SSID: %s\n", WIFI_SSID);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFISSID, WIFIPASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 25) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n[WIFI] Connected! Security Gateway IP: " + WiFi.localIP().toString());
} else {
Serial.println("\n[WARNING] Wi-Fi timed out. Operating in standalone offline mode.");
}
// Setup Web Endpoints
server.on("/capture", handleCapture);
server.on("/open", handleManualTrigger);
server.begin();
Serial.println("[HTTP] Security endpoint server started on port 80.\n");
}
// -----------------------------------------------------------------------------
// ARDUINO MAIN LOOP
// -----------------------------------------------------------------------------
void loop() {
server.handleClient();
checkGateCloseTimeout();
delay(10);
}
``
Build an Edge License Plate Recognition & Gate Access Controller with ESP32-CAM Firmware & Schematics Vault
Download verified Arduino C++ firmware, pinout table, and BOM for Build an Edge License Plate Recognition & Gate Access Controller with ESP32-CAM.
Free Tutorial vs. Project Vault: The tutorial and educational theory above are 100% free. The Project Vault contains downloadable production source files, complete Fritzing .fzz circuits, laser vector files, and 1-on-1 mentor debugging sessions.
#include<Adafruit_Sensor.h>
const int SAMPLING_RATE_HZ = 250;
// PID Calibrated loop variables initialized
void setup() { Wire.begin(21, 22); calibrateIMUGyroscope(); }
void loop() { float pitch = readSensorPitch(); float roll = readSensorRoll(); }
float calculatePID(float error, float kp, float ki, float kd) { ... }
void transmitTelemetryPackage(uint8_t *packet, size_t length) { ... }
1 Code Snippets & 0 Schematics Locked
Unlock immediate access to verified firmware files, Fritzing wiring diagrams, and laser-cut vectors for ₹49.
Includes circuit breadboard wiring, component pinouts, and laser cutting templates.
Screen-share or show your breadboard to M. F. Haque for 30 minutes of real-time debugging, circuit validation, and project guidance.
See What Mentora Makers Are Building
Real video walk-throughs, breadboard tests, and flight demonstrations submitted by students & parents.
Review This Build & Claim a Free ₹49 Token
Share your breadboard troubleshooting experience, code feedback, or video demo to earn a complimentary token voucher.
Have a Reward Voucher?
Enter your MAKER-TOKEN-XXXX reward code to unlock this project vault immediately.
Earn ₹50 Per Friend You Refer
Get your custom referral link. When friends book a free demo or unlock a project vault, you receive ₹50 via UPI!
Why does esp_camera_init() fail with error 0x105 or 0x20001?
Root Cause: PSRAM disabled in IDE or camera ribbon cable loose. Ensure "AI Thinker ESP32-CAM" board is selected with "PSRAM: Enabled", and inspect the FPC zero-insertion-force connector on the camera ribbon.
Why does the SD card fail to mount with FS_ERROR?
Root Cause: SD card formatted as exFAT or capacity > 32GB. The ESP32-CAM hardware SDMMC driver strictly requires cards formatted as FAT32.
Why does the relay trigger erratically when the camera takes a photo?
Root Cause: 5V rail voltage dip during camera capture + Wi-Fi burst. Ensure the external 5V 2A supply is equipped with a 1000µF capacitor soldered across the ESP32-CAM 5V and GND pins.
Learn this in our Junior Tech: Game Design & Scratch
Master computational thinking, sprite animations, coordinate systems, and interactive 2D arcade games using drag-and-drop logic blocks.