Automatic Toll Gate System Using Arduino

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


PCB Gerber Files

Download the PCB Gerber files for manufacturing your custom circuit board:

Note: The Gerber files are ready for PCB manufacturing. Use these files with any PCB manufacturer to get professional circuit boards made.

Order PCBs using JLCPCB's manufacturing service:

Order PCBs from JLCPCB

Assembly Steps

1
Upload the Code

Open Arduino IDE, install required libraries (Servo, Adafruit_GFX, Adafruit_SSD1306), copy-paste the provided code, select Arduino UNO and port, then click upload.

2
PCB Manufacturing (Optional)

Use the Gerber files to manufacture a custom PCB for professional assembly. This step is optional - you can also use a breadboard.

3
Circuit Connections

Follow the circuit diagram to connect all components. Double-check ultrasonic sensor connections (Trig: Pin 11, Echo: Pin 12) and servo connections (Pin 13).

4
OLED Display Setup

Connect the OLED display via I2C (SDA to A4, SCL to A5). Ensure you install the Adafruit SSD1306 and GFX libraries.

5
LED Connections

Connect green LED to Pin 2 and red LED to Pin 4 through 220Ω resistors. Green = Gate Open, Red = Gate Closed.

6
Power Supply

Connect 7.4V battery with 25V 2200μF capacitor for stable power. The capacitor smooths out voltage fluctuations.

7
Testing & Calibration

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:

Video Content: Shows complete system assembly, circuit connections, code uploading, and real-time demonstration of vehicle detection, gate operation, and counting system.

Arduino Code

#include 
#include 
#include 
#include 

// 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.

Comments

Popular posts from this blog

Arduino Code

Arduino Code Car Parking System

Solar Tracking System