DIY Smart Bus Project | Arduino Robotics with Auto Door, GPS

DIY Smart Bus Project for Kids | Arduino Robotics with Auto Door, GPS & More
🎓 Ages 10+ ⏱️ Weekend Build 🔧 Beginner–Intermediate

Build Your Own Smart School Bus Robot! 🚌

A fun DIY smart bus project using Arduino — with a passenger counter, auto door, ticket dispenser, footboard safety, driver fatigue alarm, panic button, GPS display, and talking voice announcements. Perfect for school science fairs and robotics club!

Cartoon smart school bus SMART BUS

Ever wondered how a real smart bus counts passengers, opens its own door, or announces the next stop all by itself? In this DIY robotics project, you'll build a mini model bus that does all of that using an Arduino Mega, some simple sensors, and a bit of code. No previous robotics experience needed — just follow each "bus stop" below in order!

🗺️ Our Route (What We're Building)

  1. 🧰 Full Parts List
  2. 🧠 The Brain: Arduino Mega
  3. 1 Passenger Counter
  4. 2 Auto Door
  5. 3 Footboard Safety
  6. 4 Ticket Dispenser
  7. 5 Driver Fatigue Alarm
  8. 6 Emergency Panic Button
  9. 7 GPS Display
  10. 8 Voice Announcements
  11. 🧩 Full Combined Code
  12. Testing Checklist
  13. FAQ
🧰

Full Parts List (Shopping List)

Here's everything you need for the whole bus. Don't worry — most parts cost just a few dollars and are reusable in future projects too!

PartQtyUsed For
Arduino Mega 25601The "brain" — controls everything
IR obstacle sensor module4Passenger counter (2), footboard (1), fatigue alarm (1)
HC-SR04 ultrasonic sensor1Auto door
SG90 micro servo motor2Door motor, ticket dispenser
16x2 LCD with I2C backpack1GPS display / passenger count
NEO-6M GPS module1Location tracking
DFPlayer Mini MP3 module + micro SD card1Voice announcements
Small 8-ohm speaker1Playing announcements
Push button (big red arcade button ideal)2Panic button, ticket request button
Active buzzer2Fatigue alarm, panic alarm
LED (red, green)2Status indicators
220Ω resistor2Protecting the LEDs
Breadboard + jumper wires1 setAll wiring
9V battery / power bank + on-off switch1Powering the whole bus
Cardboard or foam board bus model1The bus body itself!
🛒 Tip: You can find most of these bundled together in an "Arduino Mega starter kit" online, which is usually cheaper than buying each part separately.
🧠

The Brain: Arduino Mega 2560

We use an Arduino Mega instead of a regular Uno because our smart bus has 8 features running at once — the Mega gives us enough pins and enough memory to handle all of them together.

Arduino Mega PinConnected To
2, 3Passenger counter IR sensors (entry, exit)
4Footboard safety IR sensor
5Driver fatigue IR sensor
6, 7Ultrasonic sensor (Trig, Echo) — auto door
8Door servo motor (signal)
9Ticket dispenser servo motor (signal)
10Ticket request button
11Panic button
12Panic buzzer
13Fatigue alarm buzzer
A0Red status LED (panic)
A1Green status LED (running normally)
SDA (20), SCL (21)16x2 I2C LCD screen
Serial1 (TX1/RX1, pins 18/19)NEO-6M GPS module
Serial2 (TX2/RX2, pins 16/17)DFPlayer Mini MP3 module
💡 Fun fact: The Arduino Mega has 54 digital pins — that's more than 5 times the pins of an Arduino Uno, which only has 14!
1

🚏 Passenger Counter

📢 What it does: Counts how many passengers get on and off, so the display always shows how many people are currently on the bus.

How it works: Two IR (infrared) sensors are placed at the door — one slightly in front of the other. When someone walks in, the sensors are triggered in one order (entry → count goes up). When someone walks out, they're triggered in the opposite order (exit → count goes down).

Arduino Mega IR Sensor (Entry) → Pin 2 IR Sensor (Exit) → Pin 3 16x2 LCD SDA/SCL
  1. Mount the two IR sensors near the door frame, about 10cm apart — one closer to inside, one closer to outside.
  2. Connect each sensor's VCC to Arduino 5V, GND to GND.
  3. Connect the entry sensor's OUT pin to Arduino digital pin 2, and the exit sensor's OUT pin to pin 3.
  4. Connect the LCD's SDA and SCL pins to the Mega's SDA (20) and SCL (21) pins.
  5. Upload the code below and test by walking your hand past the sensors in each direction.
// Passenger Counter
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);
const int entryPin = 2;
const int exitPin = 3;
int passengerCount = 0;

void setup() {
  pinMode(entryPin, INPUT);
  pinMode(exitPin, INPUT);
  lcd.init();
  lcd.backlight();
}

void loop() {
  if (digitalRead(entryPin) == LOW) {
    passengerCount++;
    delay(500); // avoid double counting
  }
  if (digitalRead(exitPin) == LOW && passengerCount > 0) {
    passengerCount--;
    delay(500);
  }
  lcd.setCursor(0, 0);
  lcd.print("Passengers: ");
  lcd.print(passengerCount);
}
2

🚪 Auto Door

📢 What it does: Opens the door automatically when a passenger walks up, and closes it after they've boarded.

How it works: An ultrasonic sensor measures the distance to anything in front of the door. If something is closer than 20cm, a servo motor rotates to swing the door open. After a short delay with nothing detected, it closes again.

Arduino Mega HC-SR04 Trig=6, Echo=7 SG90 Servo Signal → Pin 8
  1. Mount the ultrasonic sensor facing outward at the door, at about waist height.
  2. Connect Trig to pin 6 and Echo to pin 7, VCC to 5V, GND to GND.
  3. Attach the servo's horn to your cardboard door hinge with a small stick or wire arm.
  4. Connect the servo's signal wire to pin 8, power wire to 5V, ground wire to GND.
  5. Upload the code and test by moving your hand toward the door.
// Auto Door
#include <Servo.h>

const int trigPin = 6;
const int echoPin = 7;
Servo doorServo;

void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  doorServo.attach(8);
  doorServo.write(0); // door closed
}

long getDistanceCM() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  return duration * 0.034 / 2;
}

void loop() {
  long distance = getDistanceCM();
  if (distance > 0 && distance < 20) {
    doorServo.write(90); // open door
    delay(3000);
  } else {
    doorServo.write(0); // close door
  }
  delay(200);
}
3

🦶 Footboard Safety

📢 What it does: Warns if a passenger is standing on the footboard (the step near the door) while the bus is supposed to be moving — a real safety feature used in actual smart buses!

How it works: An IR sensor under the footboard detects if someone is standing there. If detected, a buzzer sounds and a warning shows on the LCD, reminding the "driver" to wait until everyone is fully inside.

Arduino Mega Footboard IR OUT → Pin 4 Buzzer → Pin 13
  1. Place the IR sensor just under the footboard/step edge, facing upward.
  2. Wire VCC to 5V, GND to GND, OUT to pin 4.
  3. Connect a buzzer's positive leg to pin 13, and negative leg to GND.
// Footboard Safety
const int footPin = 4;
const int fatigueBuzzer = 13;

void setup() {
  pinMode(footPin, INPUT);
  pinMode(fatigueBuzzer, OUTPUT);
}

void loop() {
  if (digitalRead(footPin) == LOW) { // someone on footboard
    digitalWrite(fatigueBuzzer, HIGH);
  } else {
    digitalWrite(fatigueBuzzer, LOW);
  }
}
⚠️ Safety note: In our combined code below, this buzzer pin is shared and separated logically so it doesn't clash with the fatigue alarm.
4

🎫 Ticket Dispenser

📢 What it does: Presses a button to get a small paper ticket pushed out — just like a real bus ticket machine!

How it works: A push button tells the Arduino "a passenger wants a ticket." The Arduino rotates a servo motor which nudges a small stack of paper strips forward, dispensing one ticket, then returns to its starting position.

Arduino Mega Push Button → Pin 10 SG90 Servo → Pin 9
  1. Wire the push button between pin 10 and GND (we'll use the internal pull-up resistor in code).
  2. Roll a small strip of paper (the "ticket roll") and rest it against the servo horn.
  3. Connect the servo's signal wire to pin 9.
  4. Press the button and watch the servo nudge a ticket forward!
// Ticket Dispenser
#include <Servo.h>

const int ticketButton = 10;
Servo ticketServo;

void setup() {
  pinMode(ticketButton, INPUT_PULLUP);
  ticketServo.attach(9);
  ticketServo.write(0);
}

void loop() {
  if (digitalRead(ticketButton) == LOW) {
    ticketServo.write(90);  // push ticket out
    delay(700);
    ticketServo.write(0);   // reset
    delay(500);
  }
}
5

😴 Driver Fatigue Alarm

📢 What it does: Watches the driver's eyes and sounds an alarm if they seem to be closed for too long — helping keep drowsy driving in check.

How it works: A small IR sensor is aimed at the driver's eye area (in our model, a toy figure). If it detects the eye is "closed" (blocked) continuously for more than a couple of seconds, it sounds an alarm and flashes a warning on the LCD.

Arduino Mega Eye-blink IR OUT → Pin 5 Alarm Buzzer → Pin 13
  1. Position the IR sensor about 5-8cm from the driver figure's "eyes."
  2. Connect OUT to pin 5, VCC to 5V, GND to GND.
  3. Reuse the buzzer from the footboard circuit — the code below manages timing so both alarms don't conflict.
// Driver Fatigue Alarm
const int eyePin = 5;
const int alarmBuzzer = 13;
unsigned long eyesClosedStart = 0;
bool eyesClosed = false;

void setup() {
  pinMode(eyePin, INPUT);
  pinMode(alarmBuzzer, OUTPUT);
}

void loop() {
  if (digitalRead(eyePin) == LOW) { // eyes "closed" / blocked
    if (!eyesClosed) {
      eyesClosed = true;
      eyesClosedStart = millis();
    }
    if (millis() - eyesClosedStart > 2000) {
      digitalWrite(alarmBuzzer, HIGH); // sound alarm after 2 sec
    }
  } else {
    eyesClosed = false;
    digitalWrite(alarmBuzzer, LOW);
  }
}
💡 Fun fact: Real fatigue-detection systems in cars and buses often track how long the eyes stay closed and how often the driver blinks — just like our sensor does here, but with a camera!
6

🆘 Emergency Panic Button

📢 What it does: One big red button that any passenger can press in an emergency — it triggers a loud buzzer and a flashing red light immediately.

How it works: This is the simplest but most important circuit. When the button is pressed, the Arduino instantly turns on a buzzer and flashes an LED, and shows "EMERGENCY!" on the LCD until it's manually reset.

Arduino Mega Panic Button → Pin 11 Buzzer → Pin 12 Red LED → Pin A0
  1. Mount a big, obvious button (arcade-style if possible) somewhere passengers can reach.
  2. Wire it between pin 11 and GND, using the internal pull-up resistor in code.
  3. Connect a separate buzzer to pin 12 and a red LED (with a 220Ω resistor) to pin A0.
// Emergency Panic Button
const int panicButton = 11;
const int panicBuzzer = 12;
const int redLED = A0;
bool panicActive = false;

void setup() {
  pinMode(panicButton, INPUT_PULLUP);
  pinMode(panicBuzzer, OUTPUT);
  pinMode(redLED, OUTPUT);
}

void loop() {
  if (digitalRead(panicButton) == LOW) {
    panicActive = true;
  }
  if (panicActive) {
    digitalWrite(panicBuzzer, HIGH);
    digitalWrite(redLED, (millis() / 300) % 2); // flash the LED
  } else {
    digitalWrite(panicBuzzer, LOW);
    digitalWrite(redLED, LOW);
  }
}
⚠️ In a real bus, this alert would also notify the driver and nearby authorities. In our model, we simulate this with the buzzer, flashing light, and an "EMERGENCY" message on the LCD.
7

📍 GPS Display

📢 What it does: Shows the bus's live location (latitude and longitude) on the LCD screen, just like a real vehicle tracking system.

How it works: A GPS module listens to satellite signals and sends location data over a serial connection. The Arduino reads this data using the TinyGPS++ library and prints the coordinates to the LCD.

Arduino Mega NEO-6M GPS TX→RX1(19), RX→TX1(18) 16x2 LCD SDA/SCL
  1. Connect the GPS module's TX pin to the Mega's RX1 (pin 19), and RX pin to the Mega's TX1 (pin 18).
  2. Power the module with 5V and GND.
  3. Place the GPS antenna where it can "see" the sky — indoors it may take a while to get a signal (this is normal!).
// GPS Display
#include <TinyGPS++.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

TinyGPSPlus gps;
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  Serial1.begin(9600); // GPS module
  lcd.init();
  lcd.backlight();
}

void loop() {
  while (Serial1.available() > 0) {
    gps.encode(Serial1.read());
  }
  if (gps.location.isUpdated()) {
    lcd.setCursor(0, 0);
    lcd.print("Lat:");
    lcd.print(gps.location.lat(), 4);
    lcd.setCursor(0, 1);
    lcd.print("Lng:");
    lcd.print(gps.location.lng(), 4);
  }
}
💡 Fun fact: GPS satellites orbit about 20,200 km above Earth, and your module needs a clear line of sight to at least 4 of them to get an accurate fix!
8

🔊 Voice Announcements

📢 What it does: Plays a recorded voice announcing the next stop, just like on a real city bus.

How it works: Pre-recorded MP3 clips (like "Next stop: Main Street") are stored on a micro SD card plugged into a DFPlayer Mini module. The Arduino tells the module which track to play and when.

Arduino Mega DFPlayer Mini TX→RX2(17), RX→TX2(16) Speaker (8-ohm)
  1. Copy your recorded MP3 clips onto a micro SD card, named 0001.mp3, 0002.mp3, and so on.
  2. Insert the card into the DFPlayer Mini and wire the speaker to its SPK pins.
  3. Connect DFPlayer TX to Mega RX2 (pin 17) and RX to Mega TX2 (pin 16).
  4. Trigger announcements whenever the auto door opens (a natural "next stop" moment)!
// Voice Announcements
#include <DFRobotDFPlayerMini.h>

DFRobotDFPlayerMini mp3;

void setup() {
  Serial2.begin(9600);
  mp3.begin(Serial2);
  mp3.volume(20); // 0 to 30
}

void announceStop(int trackNumber) {
  mp3.play(trackNumber); // plays 000X.mp3
}
🧩 See the Full Combined Arduino Code (all 8 features together)

Once every feature works on its own, combine them into one sketch like this. Adjust pin numbers if you changed anything above.

// ===== SMART BUS — FULL COMBINED CODE =====
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <TinyGPS++.h>
#include <DFRobotDFPlayerMini.h>

// --- Pin setup ---
const int entryPin = 2, exitPin = 3, footPin = 4, eyePin = 5;
const int trigPin = 6, echoPin = 7, doorServoPin = 8, ticketServoPin = 9;
const int ticketButton = 10, panicButton = 11, panicBuzzer = 12, sharedBuzzer = 13;
const int redLED = A0, greenLED = A1;

Servo doorServo, ticketServo;
LiquidCrystal_I2C lcd(0x27, 16, 2);
TinyGPSPlus gps;
DFRobotDFPlayerMini mp3;

int passengerCount = 0;
bool panicActive = false;
unsigned long eyesClosedStart = 0;
bool eyesClosed = false;

void setup() {
  pinMode(entryPin, INPUT); pinMode(exitPin, INPUT);
  pinMode(footPin, INPUT); pinMode(eyePin, INPUT);
  pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT);
  pinMode(ticketButton, INPUT_PULLUP);
  pinMode(panicButton, INPUT_PULLUP);
  pinMode(panicBuzzer, OUTPUT); pinMode(sharedBuzzer, OUTPUT);
  pinMode(redLED, OUTPUT); pinMode(greenLED, OUTPUT);

  doorServo.attach(doorServoPin); doorServo.write(0);
  ticketServo.attach(ticketServoPin); ticketServo.write(0);

  lcd.init(); lcd.backlight();
  Serial1.begin(9600);  // GPS
  Serial2.begin(9600);  // DFPlayer
  mp3.begin(Serial2);
  mp3.volume(20);

  digitalWrite(greenLED, HIGH);
}

long getDistanceCM() {
  digitalWrite(trigPin, LOW); delayMicroseconds(2);
  digitalWrite(trigPin, HIGH); delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  return duration * 0.034 / 2;
}

void loop() {
  // 1. Passenger counter
  if (digitalRead(entryPin) == LOW) { passengerCount++; delay(400); }
  if (digitalRead(exitPin) == LOW && passengerCount > 0) { passengerCount--; delay(400); }

  // 2. Auto door
  long dist = getDistanceCM();
  if (dist > 0 && dist < 20) {
    doorServo.write(90);
    mp3.play(1); // "next stop" clip
    delay(3000);
  } else {
    doorServo.write(0);
  }

  // 3. Footboard safety + 5. Fatigue alarm share one buzzer, panic overrides both
  bool footWarning = (digitalRead(footPin) == LOW);
  if (digitalRead(eyePin) == LOW) {
    if (!eyesClosed) { eyesClosed = true; eyesClosedStart = millis(); }
  } else { eyesClosed = false; }
  bool fatigueWarning = eyesClosed && (millis() - eyesClosedStart > 2000);

  // 4. Ticket dispenser
  if (digitalRead(ticketButton) == LOW) {
    ticketServo.write(90); delay(700); ticketServo.write(0); delay(300);
  }

  // 6. Panic button (highest priority)
  if (digitalRead(panicButton) == LOW) { panicActive = true; }

  if (panicActive) {
    digitalWrite(panicBuzzer, HIGH);
    digitalWrite(redLED, (millis() / 300) % 2);
    digitalWrite(greenLED, LOW);
    lcd.setCursor(0, 0); lcd.print("** EMERGENCY! **");
  } else {
    digitalWrite(panicBuzzer, LOW);
    digitalWrite(sharedBuzzer, footWarning || fatigueWarning ? HIGH : LOW);
    digitalWrite(greenLED, HIGH);

    // 7. GPS display (only shown when no alerts)
    while (Serial1.available() > 0) { gps.encode(Serial1.read()); }
    lcd.setCursor(0, 0);
    lcd.print("Pax:"); lcd.print(passengerCount);
    lcd.print(" ");
    if (gps.location.isValid()) {
      lcd.setCursor(8, 0);
      lcd.print(gps.location.lat(), 2);
    }
    lcd.setCursor(0, 1);
    if (footWarning) lcd.print("Wait! Footboard!");
    else if (fatigueWarning) lcd.print("Driver: Wake up!");
    else lcd.print("Bus running OK  ");
  }

  delay(150);
}

Testing Checklist

Before you show off your smart bus, go through this checklist with a parent, teacher, or robotics mentor:

  1. ☐ Passenger counter goes up when "entering" and down when "exiting"
  2. ☐ Door opens when something is closer than 20cm, and closes after a few seconds
  3. ☐ Footboard buzzer sounds when the footboard sensor is blocked
  4. ☐ Ticket dispenser servo moves forward and back when the button is pressed
  5. ☐ Fatigue alarm triggers only after eyes stay "closed" for 2+ seconds (not instantly)
  6. ☐ Panic button instantly triggers buzzer + flashing LED, and stays on until reset
  7. ☐ GPS shows changing coordinates once it gets a satellite signal (best done outdoors)
  8. ☐ Voice announcement plays when the door opens
⚠️ Always build and test electronics with an adult nearby, especially when soldering or using a power source. Double-check wiring before powering on!

Frequently Asked Questions

Do I need to know how to code to build this?

Not much! Each code snippet above is ready to use — you just need to install the Arduino IDE, install the listed libraries, and upload the code. It's a great first robotics project for learning the basics.

Can I build this with an Arduino Uno instead of a Mega?

You can, but you'll need to build fewer features at once or use a multiplexer, since the Uno has far fewer pins. We recommend starting with the passenger counter, auto door, and panic button on an Uno, then upgrading to a Mega for the full 8-feature build.

Why isn't my GPS module getting a signal?

GPS modules need a clear view of the sky to talk to satellites. If you're indoors, place the antenna near a window, or test outside — it can take a few minutes for the first "fix."

What age group is this project best for?

This project works well for students aged 10 and up, especially with adult help for wiring and soldering. It's a popular choice for school science fairs and robotics clubs.

How much does this project cost to build?

Most component kits for this build cost a modest amount online, since IR sensors, servos, and buttons are inexpensive. The GPS module and MP3 player are usually the priciest parts.

🚌 Built with curiosity, sensors, and a bit of code. Happy building — and remember to always work safely with an adult around!

Comments

Product Cards
Buddy Bot eBook
⭐ New 2026 Release
Build Your
Own Robot!
3D design, wiring &
Arduino coding.
Young inventors love it!
🖨️
3D Print
All parts
Wire it
Circuit guide
💻
Code it
Arduino IDE
🤖
Watch it
Walk & react
📋 Your Details
Enter your name
Valid 10-digit no.
Enter a valid email
Special Website Offer
₹499 300
🌍 International: $5 USD
One-time · Instant digital delivery
🔒 Secured by Razorpay · Your data is safe
📄 Download Free Sample Copy
🔒 Secured by Razorpay · Your data is safe
🍓
Raspberry Pi Pico Mastery
21 Projects
⚡ Launch Price — 80% OFF
Learn Pico
Build 21 Projects!
MicroPython · Wokwi
IoT · Certificate
Perfect for beginners!
🖥️
Wokwi
No hardware
🐍
MicroPy
From zero
🔨
21 Projects
IoT + sensors
📄
Certificate
Verified cert
📋 Your Details
Enter your name
Valid 10-digit no.
Enter a valid email
Special Launch Offer
₹999 200 80% OFF
🌍 International: $5 USD
One-time · Lifetime access · No subscription
🔒 Secured by Razorpay · UPI · Cards · NetBanking
🎉

You're in!

Payment successful! Your Buddy Bot eBook is ready. Time to build!

📖 Access Your eBook Now
🎉

Enrolled!

Payment successful! Lifetime access to all 21 Pico Projects is yours!

🍓 Go to My Course