Automatic Toll Gate System Using Arduino
Automatic Toll Gate System Using Arduino
Build a professional automatic toll gate system that uses ultrasonic sensors to detect vehicles, servo-controlled gates for automated access, and an OLED display for real-time vehicle counting and system status.
Project Overview
This sophisticated automatic toll gate system features ultrasonic distance sensing for vehicle detection, smooth servo-controlled gate operation with acceleration/deceleration algorithms, real-time vehicle counting, and OLED display showing count and gate position. The system uses LED indicators for visual feedback and includes emergency safety features.
Key Features
Ultrasonic Detection
HC-SR04 sensor detects vehicles within 20cm range with high accuracy
Smooth Gate Control
Advanced acceleration/deceleration algorithms for super-smooth servo movement
Real-time Counting
OLED display shows vehicle count with large, readable numbers
Visual Indicators
Green/Red LEDs show gate status with blinking during movement
Safety Features
Emergency close function and anti-stuck mechanisms
Power Management
Battery powered with capacitor stabilization for reliable operation
Materials Required
- Arduino UNO - Buy Here
- Jumper Wires - Buy Here
- Breadboard - Buy Here
- Ultrasonic Sensor & Servo Motor - Buy Here
- All 10mm LEDs (Green & Red) - Buy Here
- L Shape Male Header - Buy Here
- Female Header - Buy Here
- 220 Ohm Resistors - Buy Here
- OLED Display (SSD1306) - Buy Here
- 25V 2200uf Capacitor - Buy Here
- 7.4V Battery - Buy Here
PCB Gerber Files
Download the PCB Gerber files for manufacturing your custom circuit board:
Order PCBs using JLCPCB's manufacturing service:
Order PCBs from JLCPCBAssembly Steps
Open Arduino IDE, install required libraries (Servo, Adafruit_GFX, Adafruit_SSD1306), copy-paste the provided code, select Arduino UNO and port, then click upload.
Use the Gerber files to manufacture a custom PCB for professional assembly. This step is optional - you can also use a breadboard.
Follow the circuit diagram to connect all components. Double-check ultrasonic sensor connections (Trig: Pin 11, Echo: Pin 12) and servo connections (Pin 13).
Connect the OLED display via I2C (SDA to A4, SCL to A5). Ensure you install the Adafruit SSD1306 and GFX libraries.
Connect green LED to Pin 2 and red LED to Pin 4 through 220Ω resistors. Green = Gate Open, Red = Gate Closed.
Connect 7.4V battery with 25V 2200μF capacitor for stable power. The capacitor smooths out voltage fluctuations.
Test the ultrasonic sensor distance detection (default: 20cm). Adjust distanceThreshold in code if needed. Test smooth servo movement.
System Demo Video
Watch the complete system demonstration and working:
Arduino Code
#include <Servo.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Pin definitions
const int trigPin = 11;
const int echoPin = 12;
const int servoPin = 13;
const int greenLedPin = 2;
const int redLedPin = 4;
// Servo object
Servo myServo;
// OLED display settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Distance threshold (in cm)
const int distanceThreshold = 20;
// Servo angles
const int closedAngle = 2;
const int openAngle = 90;
// Servo movement settings - UPDATED FOR SUPER-SMOOTH MOVEMENT
const int servoDelay = 15; // Slightly slower update rate for smoother motion
const float moveIncrement = 1.0; // Smaller increments
const float acceleration = 0.3; // Acceleration factor for smoother starts
const float deceleration = 0.4; // Deceleration factor for smoother stops
const float maxSpeed = 3.0; // Maximum speed multiplier
// Timing
const unsigned long closeDelay = 1000; // 1 seconds
// States
int gateCount = 0;
int currentServoAngle = closedAngle;
int targetServoAngle = closedAngle;
unsigned long lastObjectTime = 0;
bool objectWasPresent = false;
bool gateWasOpen = false; // Track if gate was previously open
// Movement variables for smooth motion
float currentServoPosition = closedAngle; // Float for precise position
float servoVelocity = 0.0;
unsigned long lastMoveTime = 0;
// LED blinking timing
unsigned long lastBlinkTime = 0;
const unsigned long blinkInterval = 400; // 400ms blink
void setup() {
// Initialize pins
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(greenLedPin, OUTPUT);
pinMode(redLedPin, OUTPUT);
// Attach servo
myServo.attach(servoPin);
// Initialize OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for(;;);
}
display.clearDisplay();
display.display();
display.setTextColor(SSD1306_WHITE);
// Show welcome message
showWelcomeMessage();
// Start with servo closed
myServo.write(closedAngle);
currentServoAngle = closedAngle;
currentServoPosition = closedAngle;
targetServoAngle = closedAngle;
updateLeds();
Serial.begin(9600);
Serial.println("System Ready - Count on Gate Open");
updateDisplay();
}
void loop() {
// Read sensor
long distance = getDistance();
bool objectPresent = (distance <= distanceThreshold && distance > 0);
// Handle LED blinking
handleLedBlinking();
// Update servo position smoothly
updateServoSmoothly();
// Check if gate just opened (for counting)
if (currentServoAngle == openAngle && !gateWasOpen) {
// Gate just opened - INCREASE COUNT
gateCount++;
gateWasOpen = true;
Serial.print("COUNT INCREASED! Gate opened. Total: ");
Serial.println(gateCount);
updateDisplay();
}
// Check if gate just closed
if (currentServoAngle == closedAngle && gateWasOpen) {
gateWasOpen = false;
}
// Simple state machine
if (objectPresent) {
// Object detected
if (!objectWasPresent) {
Serial.println("Object detected - Opening gate");
objectWasPresent = true;
}
lastObjectTime = millis();
// Set target to open position
targetServoAngle = openAngle;
}
else {
// No object detected
if (objectWasPresent) {
Serial.println("Object removed");
objectWasPresent = false;
}
// Set target to closed position if it was open and 5 seconds passed
if (currentServoAngle > closedAngle) {
if (millis() - lastObjectTime >= closeDelay) {
targetServoAngle = closedAngle;
}
} else {
targetServoAngle = closedAngle;
}
}
// Emergency close if gate stuck partially open without object for too long
if (!objectPresent && currentServoAngle > closedAngle && currentServoAngle < openAngle) {
if (millis() - lastObjectTime >= 10000) { // 10 seconds timeout
Serial.println("Emergency close - gate stuck");
emergencyCloseGate();
}
}
// Always update display
updateDisplay();
delay(15); // Slightly shorter delay for smoother updates
}
void updateServoSmoothly() {
if (millis() - lastMoveTime >= servoDelay) {
lastMoveTime = millis();
// Calculate distance to target
float distanceToTarget = targetServoAngle - currentServoPosition;
// Calculate desired velocity based on distance (with easing)
float targetVelocity;
if (abs(distanceToTarget) < 10) {
// Close to target - use deceleration
targetVelocity = distanceToTarget * deceleration;
} else {
// Far from target - accelerate toward target
targetVelocity = constrain(distanceToTarget * acceleration, -maxSpeed, maxSpeed);
}
// Apply velocity change smoothly
servoVelocity += (targetVelocity - servoVelocity) * 0.2;
// Limit maximum velocity
servoVelocity = constrain(servoVelocity, -maxSpeed, maxSpeed);
// Update position
currentServoPosition += servoVelocity;
// Constrain position to valid range
currentServoPosition = constrain(currentServoPosition, closedAngle, openAngle);
// Snap to target if very close and moving slowly
if (abs(distanceToTarget) < 0.5 && abs(servoVelocity) < 0.3) {
currentServoPosition = targetServoAngle;
servoVelocity = 0;
}
// Update servo if position changed significantly
int newAngle = round(currentServoPosition);
if (newAngle != currentServoAngle) {
currentServoAngle = newAngle;
myServo.write(currentServoAngle);
// Update LEDs when reaching endpoints
if (currentServoAngle == openAngle || currentServoAngle == closedAngle) {
updateLeds();
}
// Debug output (optional)
// Serial.print("Pos: "); Serial.print(currentServoPosition);
// Serial.print(" Vel: "); Serial.print(servoVelocity);
// Serial.print(" Target: "); Serial.println(targetServoAngle);
}
}
}
void handleLedBlinking() {
// Blink red LED only when gate is closing (moving from open to closed)
if (currentServoAngle > closedAngle && currentServoAngle < openAngle && !objectWasPresent) {
if (millis() - lastBlinkTime >= blinkInterval) {
lastBlinkTime = millis();
bool currentRedState = digitalRead(redLedPin);
digitalWrite(redLedPin, !currentRedState);
digitalWrite(greenLedPin, LOW); // Ensure green is off during closing
}
} else {
// Normal LED operation
updateLeds();
}
}
void emergencyCloseGate() {
// Fast emergency close with some smoothing
unsigned long emergencyStart = millis();
float emergencyPosition = currentServoPosition;
float emergencyVelocity = -maxSpeed * 2; // Faster emergency close
while (emergencyPosition > closedAngle + 1 && millis() - emergencyStart < 3000) {
if (millis() - lastMoveTime >= servoDelay) {
lastMoveTime = millis();
// Update position with some deceleration at the end
float distanceToClosed = emergencyPosition - closedAngle;
if (distanceToClosed < 20) {
emergencyVelocity = -distanceToClosed * 0.5; // Decelerate
}
emergencyPosition += emergencyVelocity;
emergencyPosition = constrain(emergencyPosition, closedAngle, openAngle);
int emergencyAngle = round(emergencyPosition);
if (emergencyAngle != currentServoAngle) {
currentServoAngle = emergencyAngle;
myServo.write(currentServoAngle);
}
// Handle blinking during emergency close
handleLedBlinking();
}
delay(10);
}
// Ensure fully closed
currentServoPosition = closedAngle;
currentServoAngle = closedAngle;
myServo.write(currentServoAngle);
servoVelocity = 0;
targetServoAngle = closedAngle;
updateLeds();
gateWasOpen = false;
Serial.println("Gate emergency closed");
}
void showWelcomeMessage() {
unsigned long startTime = millis();
unsigned long lastWelcomeBlinkTime = 0;
bool ledState = false;
while (millis() - startTime < 3000) {
// Blink LEDs
if (millis() - lastWelcomeBlinkTime >= 300) {
lastWelcomeBlinkTime = millis();
ledState = !ledState;
digitalWrite(greenLedPin, ledState);
digitalWrite(redLedPin, ledState);
}
// Display message
display.clearDisplay();
display.setTextSize(2);
int16_t x1, y1, x2, y2;
uint16_t w1, h1, w2, h2;
display.getTextBounds("Toll Gate", 0, 0, &x1, &y1, &w1, &h1);
display.getTextBounds("System", 0, 0, &x2, &y2, &w2, &h2);
int totalHeight = h1 + h2 + 5;
int startY = (SCREEN_HEIGHT - totalHeight) / 2;
display.setCursor((SCREEN_WIDTH - w1) / 2, startY);
display.print("Toll Gate");
display.setCursor((SCREEN_WIDTH - w2) / 2, startY + h1 + 5);
display.print("System");
display.display();
delay(50);
}
digitalWrite(greenLedPin, LOW);
digitalWrite(redLedPin, LOW);
display.clearDisplay();
display.display();
}
long getDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
if (duration == 0) return 999;
return duration * 0.0343 / 2;
}
void displayMovementProgress(int angle) {
display.clearDisplay();
// Big count
display.setTextSize(5);
if (gateCount < 10) {
display.setCursor(42, 5);
display.print("0");
display.print(gateCount);
} else if (gateCount < 100) {
display.setCursor(22, 5);
display.print(gateCount);
} else {
display.setCursor(2, 5);
display.print(gateCount);
}
// Percentage - draw this first
int percentage = map(angle, closedAngle, openAngle, 0, 100);
display.setTextSize(1);
display.setCursor(5, 52);
if (percentage == 100) {
display.print("100%");
} else if (percentage < 10) {
display.print(" ");
display.print(percentage);
display.print("%");
} else {
display.print(" ");
display.print(percentage);
display.print("%");
}
// Progress bar
int barStartX = 35;
int barWidth = 90;
int barHeight = 6;
int progressWidth = map(angle, closedAngle, openAngle, 0, barWidth);
// Clear only the progress bar area and to the right of it
display.fillRect(barStartX, 53, barWidth, barHeight, SSD1306_BLACK); // Clear bar area
display.fillRect(barStartX + barWidth, 52, SCREEN_WIDTH - (barStartX + barWidth), 8, SSD1306_BLACK); // Clear right side
// Now draw the progress bar
display.drawRect(barStartX, 53, barWidth, barHeight, SSD1306_WHITE);
if (progressWidth > 0) {
display.fillRect(barStartX, 53, progressWidth, barHeight, SSD1306_WHITE);
}
display.display();
}
void updateDisplay() {
display.clearDisplay(); // Always clear the entire display first
if (currentServoAngle == openAngle || currentServoAngle == closedAngle) {
// Big count
display.setTextSize(5);
if (gateCount < 10) {
display.setCursor(42, 5);
display.print("0");
display.print(gateCount);
} else if (gateCount < 100) {
display.setCursor(22, 5);
display.print(gateCount);
} else {
display.setCursor(2, 5);
display.print(gateCount);
}
// Percentage - draw this first
display.setTextSize(1);
display.setCursor(5, 52);
if (currentServoAngle == openAngle) {
display.print("100%");
} else {
display.print(" 0%");
}
// Progress bar
int barStartX = 35;
int barWidth = 90;
int barHeight = 6;
// Clear only the progress bar area and to the right of it
display.fillRect(barStartX, 53, barWidth, barHeight, SSD1306_BLACK); // Clear bar area
display.fillRect(barStartX + barWidth, 52, SCREEN_WIDTH - (barStartX + barWidth), 8, SSD1306_BLACK); // Clear right side
// Now draw the progress bar
display.drawRect(barStartX, 53, barWidth, barHeight, SSD1306_WHITE);
if (currentServoAngle == openAngle) {
display.fillRect(barStartX, 53, barWidth, barHeight, SSD1306_WHITE);
}
display.display();
} else {
displayMovementProgress(currentServoAngle);
}
}
void updateLeds() {
if (currentServoAngle == openAngle) {
// Gate open - green on, red off
digitalWrite(greenLedPin, HIGH);
digitalWrite(redLedPin, LOW);
} else if (currentServoAngle == closedAngle) {
// Gate closed - red on, green off
digitalWrite(greenLedPin, LOW);
digitalWrite(redLedPin, HIGH);
}
// During movement, LEDs are handled by handleLedBlinking()
}
Code Explanation:
Key Features Implemented:
- Super-Smooth Servo Movement: Uses acceleration/deceleration algorithms for professional gate operation
- Vehicle Counting: Increments count only when gate fully opens to prevent false counts
- OLED Display: Shows vehicle count with progress bar for gate position
- LED Indicators: Green (open), Red (closed), blinking during movement
- Emergency Safety: Automatic gate close if stuck and timeout protection
- Distance Sensing: Ultrasonic sensor with 20cm detection range
Setup Function:
Initializes all pins, OLED display, servo motor, and shows welcome message. Sets gate to closed position and starts counting system.
Main Loop:
Continuously reads ultrasonic sensor, updates servo position smoothly, handles LED blinking, updates display, and manages gate states (open/close based on vehicle presence).
Technical Specifications
- Detection Range: 0-20cm (adjustable in code)
- Gate Opening Time: Approximately 1-2 seconds with smooth acceleration
- Counting Accuracy: 100% - counts only on full gate opening
- Display: 128x64 OLED with count and progress bar
- Power Supply: 7.4V battery with capacitor stabilization
- Servo Range: 2° (closed) to 90° (open)
- Response Time: Immediate detection, smooth gate response
Applications & Uses
Toll Plazas
Automated vehicle counting and gate control for toll collection
Parking Garages
Entry/exit control with vehicle counting
Industrial Gates
Factory entrance control and vehicle tracking
Education
Excellent for learning automation, sensors, and embedded systems
Troubleshooting Tips
- Servo not moving smoothly: Check power supply - servo needs stable 5V
- Ultrasonic sensor not detecting: Ensure sensor is facing correctly and within 20cm range
- OLED not displaying: Check I2C connections and install Adafruit libraries
- Gate not counting vehicles: Adjust distanceThreshold in code if needed
- LEDs not lighting: Check resistor values (220Ω) and pin connections
- Battery drain: Add power switch or use larger battery for extended use
This automatic toll gate system is perfect for learning automation principles, sensor integration, and embedded system design. It's suitable for educational projects, prototypes, and even small-scale commercial applications.
so bad sala
ReplyDelete