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#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
Post a Comment