Authors: Pavan G (@Sin-Kid)
A state-of-the-art, end-to-end IoT-enabled Smart Bus Tracking and Passenger Management System. The platform leverages real-time GPS tracking, RFID-based boarding/alighting detection, distance-based fare calculation, predictive analytics for passenger occupancy, and a modern fleet management dashboard.
The system consists of five main components interacting in real time:
+-----------------------------+
| ESP8266 IoT Hardware |
| (NEO-6M GPS & Dual RC522) |
+--------------+--------------+
|
| HTTPS REST API (RPC / Express)
v
+--------------+--------------+
| Supabase Backend |
| - PostgreSQL Database |
| - Real-time Subscriptions |
| - PL/pgSQL RPC Functions |
+-------+--------------+------+
| |
Real-time subscriptions| | REST API / JSON
v v
+-------------------------------+--+ +--+---------------------------+
| React Admin Web Dashboard | | React Native Expo App |
| - Live fleet tracking on map | | - Passenger live bus map |
| - Route/schedule management | | - Visual seat occupancy |
| - Bus visual seat occupancy map | | - Balance & top-up wallet |
| - Flask ML prediction panel | | - Gemini AI route chatbot |
+----------------------------------+ +------------------------------+
^
| Predictions API
v
+---------------+---------------+
| Flask ML Predictor API |
| - scikit-learn Random Forest |
| - Boarding/alighting forecasts|
+-------------------------------+
- Telemetry Upload: The NodeMCU ESP8266 on the bus fetches GPS coordinates from the NEO-6M sensor every 10 seconds and invokes the
handle_telemetryRPC endpoint. - Passenger Boarding: Passengers tap their RFID card on the Entry MFRC522 Scanner. The hardware validates the card, verifies a minimum balance of βΉ50, opens an active trip, and increments the bus passenger count in Supabase.
- Passenger Alighting: Passengers tap on the Exit MFRC522 Scanner when leaving. The system computes the fare, deducts it from the card balance, terminates the active trip, logs the transaction, and decrements the passenger count.
- Live Visualization: The React Admin Web and React Native Expo Apps subscribe to Supabase tables using WebSockets. When telemetry or occupancy changes, the UI updates instantly without polling.
- Demand Forecasting: The Admin panel queries the Flask Python ML service to predict passenger boarding and alighting at each upcoming stop using Random Forest Regressors.
The PostgreSQL database schema is defined in schema.sql and includes the following primary tables, indexes, and relations:
erDiagram
BUSES ||--o{ BUS_ROUTES : "has"
BUSES ||--o{ BUS_STOPS : "has"
BUSES ||--o{ BUS_SCHEDULES : "has"
BUSES ||--o{ TRIPS : "operates"
BUSES ||--o{ TELEMETRY : "sends"
BUSES ||--o{ RFID_LOGS : "logs"
CARDS ||--o{ TRIPS : "takes"
CARDS ||--o{ TRANSACTIONS : "funds"
CARDS ||--o{ RFID_LOGS : "logs"
BUS_ROUTES ||--o{ BUS_STOPS : "contains"
BUS_ROUTES ||--o{ BUS_SCHEDULES : "schedules"
TRIPS ||--o{ TRANSACTIONS : "generates"
BUSES {
string id PK
string name
jsonb location
timestamptz last_seen
numeric speed
numeric heading
string status_message
uuid current_stop_id
string current_location_name
integer capacity
integer sim_occupied
integer sim_leaving
}
BUS_ROUTES {
uuid id PK
string bus_id FK
string name
jsonb stops
string source
string destination
}
BUS_STOPS {
uuid id PK
string bus_id FK
uuid route_id FK
string name
string code
numeric lat
numeric lon
integer order
string arrival_time
numeric price
}
BUS_SCHEDULES {
uuid id PK
string bus_id FK
uuid route_id FK
time departure_time
time arrival_time
text_array days_of_week
numeric fare
string status
}
CARDS {
string id PK
string name
string card_number
numeric balance
numeric total_recharges
numeric last_recharge
timestamptz last_recharge_date
timestamptz last_seen
string card_type
string status
uuid active_trip FK
}
TRIPS {
uuid id PK
string bus_id FK
string card_id FK
timestamptz start_time
jsonb start_location
string start_stop_name
timestamptz end_time
jsonb end_location
string end_stop_name
numeric distance_km
numeric fare
string status
}
TELEMETRY {
uuid id PK
string bus_id FK
jsonb location
numeric speed
numeric heading
timestamptz timestamp
}
TRANSACTIONS {
uuid id PK
string card_id FK
uuid trip_id FK
string bus_id FK
string type
numeric amount
timestamptz timestamp
string status
string payment_method
}
RFID_LOGS {
uuid id PK
string bus_id FK
string card_id FK
string event_type
timestamptz timestamp
}
To ensure fast queries during high concurrent hardware updates, the following indexes are deployed:
idx_bus_schedules_bus_id&idx_bus_schedules_route_id: Accelerates stop and route schedules search.idx_cards_card_number: Speeds up physical RFID card authorization matches.idx_telemetry_bus_id&idx_telemetry_timestamp: Optimizes location history queries for mapping.idx_rfid_logs_card_id&idx_rfid_logs_timestamp: Quick retrieval of boarding logs for user history screens.
The project implements specialized algorithms across the database layer, the Express middle tier, and the Python ML service.
To keep the embedded client logic light and secure, all logical checks run inside Supabase via Remote Procedure Call (RPC) functions:
-
handle_bus_entry(p_card_uid, p_bus_id, p_lat, p_lon)- Validation: Searches the
cardstable by UUID or custom card number. Returns an error if the card is not registered. - Financial Control: Rejects the card if the current balance is
< βΉ50(ensuring sufficient balance for the fare deduction at exit). - State Control: Checks if
active_tripis not null to prevent double entry scans. - Resolution: Invokes
resolve_stop_nameto record the station name. - Telemetry Sync: Creates a new row in
trips(ongoing), updates the card'sactive_trip, adds torfid_logs, and increments the bus'ssim_occupiedcounter (capped at bus capacity).
- Validation: Searches the
-
handle_bus_exit(p_card_uid, p_bus_id, p_lat, p_lon)- Validation: Fetches the card and verifies that an
active_tripexists. - Fare Collection: Charges a base flat fare of βΉ50 (configurable or matching Express dynamic algorithm).
- State Resolution: Sets the card's
active_tripback toNULL, updatestripsstatus tocompleted, records theend_time,end_location, andend_stop_name. - Accounting: Inserts a transaction record of type
tripand logs torfid_logs. - Telemetry Sync: Decrements the bus's
sim_occupiedcounter (bounded at a minimum of 0).
- Validation: Fetches the card and verifies that an
-
handle_telemetry(p_bus_id, p_lat, p_lon, p_speed)- Logs incoming coordinate samples into the historical
telemetrytable. - Updates the live location, heading, and current speed in the
busesrecord.
- Logs incoming coordinate samples into the historical
An Express API is also available to act as a custom device middleware, containing two critical algorithms:
When GPS telemetry is received, the backend compares coordinates with the route's predefined bus stops to determine the current stop name using the Haversine formula (distance over a sphere):
function haversineKm(lat1, lon1, lat2, lon2) {
const toRad = (v) => (v * Math.PI) / 180;
const R = 6371; // Earth's radius in kilometers
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}If the calculated distance is less than 0.5 km (500 meters) from a stop coordinate, the bus is officially recognized as arriving at that stop.
During the exit event /device/rfid, the API performs a dynamic progressive calculation. It locates the entry stop and exit stop indices within the route's stop list and sums up the custom price segments between them:
let fare = 10; // Default base fare fallback
if (ctx?.stopsList?.length > 0) {
const sIdx = ctx.stopsList.findIndex(s => s.name === trips.start_stop_name);
const eIdx = ctx.stopsList.findIndex(s => s.name === stopName);
if (sIdx !== -1 && eIdx !== -1) {
fare = 0;
// Handle both directions (forward and reverse indexing)
const [low, high] = sIdx < eIdx ? [sIdx, eIdx] : [eIdx, sIdx];
for (let i = low; i <= high; i++) {
fare += (Number(ctx.stopsList[i].price) || 10);
}
}
}The ML service operates on a Random Forest Regressor ensemble model implemented in Python using scikit-learn to forecast boarding and alighting.
stop_id: The ID of the station (spatial identifier).hour: Hour of the day (0-23) (temporal cycle).day_of_week: Day of week (0-6) (distinguishes weekdays from weekends).is_peak_hour: Binary flag (1 for morning peak 7-9 AM, evening peak 5-7 PM; 0 otherwise).current_occupancy: Live passenger count on the bus.stop_sequence: Normalized stop order (0.0 to 1.0) along the route.historical_avg: Rolling average passenger activity for the stop.
- Generates 180 days of hourly stop activity data with realistic peaks using a Normal distribution.
- Trains two separate Random Forest Regressors (200 decision trees, max depth of 15) for:
- Boarding Predictor: Estimates how many passengers will enter at the stop.
- Alighting Predictor: Estimates how many passengers will exit.
- Evaluates the model using MAE (Mean Absolute Error), RMSE (Root Mean Squared Error), and RΒ² scores.
- Exposes predictions via a Flask REST API.
The smart bus hardware uses a NodeMCU ESP8266 board acting as a main controller. It interfaces with two MFRC522 RFID readers (separating entry and exit gates) and a NEO-6M GPS module.
| Component | Pin Label | Connected to NodeMCU Pin | Function / Note |
|---|---|---|---|
| RFID 1 (ENTRY) | SDA (SS) | D2 (GPIO 4) | Chip Select (Active Low) for Boarding Reader |
| RST | D1 (GPIO 5) | Reset pin for Entry Reader | |
| SCK | D5 (GPIO 14) | SPI Shared Clock Line | |
| MOSI | D7 (GPIO 13) | SPI Shared Master Out Slave In | |
| MISO | D6 (GPIO 12) | SPI Shared Master In Slave Out | |
| GND | GND | Ground Connection | |
| 3.3V | 3.3V | Power (Max 3.3V - Do not connect to 5V!) | |
| RFID 2 (EXIT) | SDA (SS) | D8 (GPIO 15) | Chip Select (Active Low) for Alighting Reader |
| RST | D0 (GPIO 16) | Reset pin for Exit Reader | |
| SCK | D5 (GPIO 14) | SPI Shared Clock Line | |
| MOSI | D7 (GPIO 13) | SPI Shared Master Out | |
| MISO | D6 (GPIO 12) | SPI Shared Master In | |
| GND | GND | Ground Connection | |
| 3.3V | 3.3V | Shared 3.3V Power Line | |
| GPS (NEO-6M) | TX | D4 (GPIO 2) | NodeMCU Software Serial RX |
| RX | D3 (GPIO 0) | NodeMCU Software Serial TX | |
| VCC | 3.3V / 5V | VCC Power Input | |
| GND | GND | Shared Ground |
The firmware folder contains two alternative versions depending on your hardware assembly:
- Single RFID Reader with GPS (
smart_bus_single_rfid_gps): Best for single reader setups where the system automatically toggles entry/exit based on the active trip status. - Dual RFID Reader without GPS (
smart_bus_dual_rfid_no_gps): Best for setups with distinct Entry and Exit gates, utilizing two MFRC522 readers.
Key control loop features include:
- SPI Bus Multiplexing: Because both RFID reader modules share the SPI lines (
D5,D6,D7), the NodeMCU selects which reader it is communicating with by pulling its corresponding Slave Select pin (D2orD8) low. - GPS Frame Parsing: The firmware continuously reads incoming serial bytes from the GPS module in
loop()and pipes them to theTinyGPSPlusparser. Valid location fixes update internal latitude/longitude coordinates. - 5-Second Software Tap Debouncing: To prevent passengers from triggering double charges if their card lingers near the RF field, the system implements a software timer:
if (uid == lastUidEntry && (millis() - lastTimeEntry < 5000)) { Serial.println("Ignored Duplicate Entry Tap: " + uid); return; }
- Asynchronous Telemetry Upload: Telemetry data is sent to the server every 10 seconds via a non-blocking
millis()delta check. - HTTPS Client Security: The board uses
WiFiClientSecurewith.setInsecure()mode to skip SSL fingerprint validation, making it light enough to run on a cheap ESP8266 chip without certificate management.
smart-bus-supabase/
βββ admin-web/ # React Fleet Management Dashboard (Vite)
β βββ dist/ # Production build directory
β βββ public/ # Static assets (images, icons, custom diagrams)
β βββ src/
β β βββ components/ # Reusable UI dashboard elements
β β β βββ ThemeToggle.jsx # Dark/Light mode theme button
β β β βββ ConnectionStatus.jsx# Database synchronization status indicator
β β β βββ LogsViewer.jsx # Streaming logs table
β β β βββ Sidebar.jsx # Sidebar navigation component
β β β βββ MLPredictionPanel.jsx# Connects to Flask to render stop prediction charts
β β β βββ TimePicker.jsx # Time picker element for schedules
β β β βββ BusSeatMap.jsx # Interactive seat grid mapping capacity occupancy
β β β βββ RouteTimeline.jsx # Live visual representation of bus stops
β β β βββ PassengerFrequencyChart.jsx # Hourly passenger frequency chart
β β β βββ RoutePassengerChart.jsx # Route-by-route boarding/alighting stats
β β βββ context/
β β β βββ ThemeContext.jsx # React Context Provider for app theme state
β β βββ pages/ # Main Dashboard Views
β β β βββ Dashboard.jsx # Global fleet status overview page
β β β βββ BusesPage.jsx # Add, modify, or update bus capacities
β β β βββ RoutesPage.jsx # Map out stops and create routes
β β β βββ SchedulesPage.jsx # Dispatch schedules for buses
β β β βββ StatusPage.jsx # Raw telemetry tracking details page
β β βββ utils/
β β β βββ supabaseTest.js # DB connectivity utility
β β β βββ simulatedData.js # Local backup generators for testing
β β βββ App.jsx # Client entrypoint and route definitions
β β βββ main.jsx # DOM mounting
β β βββ styles.css # Custom styling tokens (vanilla CSS & layouts)
β βββ package.json
β βββ vite.config.js
β
βββ expo-user-app/ # React Native Mobile App for Passengers
β βββ user-app/
β βββ assets/ # App assets (icons, splash screens, seat maps)
β βββ components/ # Custom mobile view components
β βββ screens/ # Mobile application views
β β βββ WelcomeScreen.js # Welcome splash page
β β βββ LoginScreen.js # User registration / account sign-in
β β βββ HomeScreen.js # Main routing hub and quick links
β β βββ FindBusScreen.js # Search routes and upcoming buses
β β βββ BusListScreen.js # Displays list of active buses
β β βββ BusInfoScreen.js # Specific details on a single bus
β β βββ BusRouteScreen.js # Route stop list and timeline
β β βββ BusLiveScreen.js # Map view with live markers using React Native Maps
β β βββ BusOccupancyScreen.js# Renders physical seat layout occupancy map
β β βββ CardInfoScreen.js # Linked RFID card overview and stats
β β βββ CardHistoryScreen.js# Log of transactions and recent bus trips
β β βββ TopUpScreen.js # Top up virtual wallet page
β β βββ PaymentDummyScreen.js# Simulated payment gateway
β β βββ QRCodeScreen.js # Virtual RFID barcode generator
β β βββ ChatbotScreen.js # AI voice/text chatbot support for passengers
β β βββ OtherScreens.js # Settings and Profile
β βββ utils/ # Map rendering helpers
β βββ supabaseConfig.js # Supabase Mobile Client configuration
β βββ App.js # Navigation Container setup (Stack/Tab)
β βββ package.json
β
βββ functions/ # Serverless APIs & ML Services
β βββ index.js # Express.js REST middleware (device-telemetry & dynamic fare)
β βββ check_status.js # Utility checking Edge server status
β βββ ml_prediction/ # Python Random Forest forecasting service
β β βββ api.py # Flask API server exposing prediction endpoints
β β βββ generate_data.py # Generates historical dataset (training_data.csv)
β β βββ train_model.py # Builds & trains Random Forest Regressor models
β β βββ simple_bus_viz.py # Matplotlib script plotting occupancy reports
β β βββ import_matlab_data.py # Imports generated MATLAB dataset files
β β βββ requirements.txt # Python dependency file
β β βββ setup.sh # Bootstraps dependencies, generates data, trains model
β β βββ retrain.sh # Backs up old weights and triggers model retrain
β βββ package.json
β βββ node_modules/
β
βββ arduino/ # NodeMCU Microcontroller Firmware
β βββ smart_bus_single_rfid_gps/ # Firmware for Single RFID + GPS configuration
β β βββ smart_bus_single_rfid_gps.ino
β βββ smart_bus_dual_rfid_no_gps/ # Firmware for Dual RFID configuration (no GPS)
β β βββ smart_bus_dual_rfid_no_gps.ino
β βββ hardware_block_diagram.png # Visual breadboard diagram for pin wiring
β
βββ schema.sql # Complete Database Setup Script
βββ seed_data.sql # Sample Data for routes, stops, schedules, & cards
βββ SETUP.md # Exhaustive deployment steps
βββ HARDWARE_SETUP.md # Hardware-specific wiring schematics
Follow these commands to deploy, run, and develop the different parts of the system.
- Log in to Supabase Console.
- Create a new project.
- Open the SQL Editor in the dashboard.
- Copy the entire contents of
schema.sqlinto the SQL Editor and click Run (this creates the tables, indexes, and RPC database functions). - Copy the contents of
seed_data.sqlinto the editor and click Run (this populates test buses, schedules, routes, stops, and cards).
Deploy the Python environment to enable passenger demand predictions.
# Navigate to prediction directory
cd functions/ml_prediction
# Run setup script (installs requirements, generates training data, and trains model)
chmod +x setup.sh
./setup.sh
# Start the Flask prediction server
python3 api.pyThe forecasting server will start running at http://localhost:5000.
Run the Express middleware for stop resolution and dynamic pricing.
# Navigate to functions folder
cd functions
# Install dependencies
npm install
# Configure environment keys in .env (Use .env.example as template)
# Add your SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, and a custom DEVICE_TOKEN
# Start the service
npm startThe Express server will launch on port 3000.
Start the administrative React front-end application.
# Navigate to dashboard folder
cd admin-web
# Install package dependencies
npm install
# Set up environment configuration (.env)
# Create a .env file containing:
# VITE_SUPABASE_URL=https://your-project.supabase.co
# VITE_SUPABASE_ANON_KEY=your-anon-key
# Launch Vite local hot-reload server
npm run devThe dashboard will be active at http://localhost:5173.
Run the React Native Expo app on an emulator or a physical device via Expo Go.
# Navigate to mobile project folder
cd expo-user-app/user-app
# Install package dependencies
npm install
# Set up environment configuration (.env)
# Create a .env file containing:
# EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
# EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
# Launch Expo packager
npx expo startPress a to run on an Android emulator, i to run on iOS Simulator, or scan the terminal QR code using the Expo Go app on your iOS/Android smartphone.
- Open the Arduino IDE.
- Install the ESP8266 Board support via Preferences URL:
http://arduino.esp8266.com/stable/package_esp8266com_index.json - Install required libraries from the Library Manager:
MFRC522by GithubCommunityTinyGPSPlusby Mikal HartArduinoJsonby Benoit Blanchon
- Open either
arduino/smart_bus_single_rfid_gps/smart_bus_single_rfid_gps.inoorarduino/smart_bus_dual_rfid_no_gps/smart_bus_dual_rfid_no_gps.inodepending on your hardware assembly. - Edit the configuration settings in the
.inofile with your parameters:WIFI_SSIDWIFI_PASSSUPABASE_URL(Withouthttps://prefix, e.g.,xyz.supabase.co)SUPABASE_KEY(Supabase Anon public key)
- Select your board (e.g.,
NodeMCU 1.0 (ESP-12E Module)) and the correct COM Port. - Click Upload to flash the firmware.
To confirm that all systems are successfully set up and communicating:
- Verify Database population: Run this SQL query in the Supabase editor:
Expected counts: Buses (3), Routes (3), Stops (10), Cards (4), Schedules (3).
SELECT 'Buses' as table_name, COUNT(*) as count FROM buses UNION ALL SELECT 'Routes', COUNT(*) FROM bus_routes UNION ALL SELECT 'Stops', COUNT(*) FROM bus_stops UNION ALL SELECT 'Cards', COUNT(*) FROM cards UNION ALL SELECT 'Schedules', COUNT(*) FROM bus_schedules;
- Verify ML Endpoint: Send a GET request to
http://localhost:5000/health. It should return{"status": "healthy", "model_loaded": true}. - Verify Web Dashboard connection: Access
http://localhost:5173. The connection status dot in the corner should show a green "Connected" indicator showing real-time socket synchronizations are live. - Verify Mobile App execution: Log in on the Expo User App. Scan coordinates, and check that live buses show up on the map.
- Verify Hardware communication: Open the Arduino IDE Serial Monitor at
115200baud. Tap an RFID card to verify that it successfully printsPROCESSING ENTRYfollowed byServer Response (200).
This project is documented in an IEEE-format research paper:
- Title: IoT-Based Smart Bus Management and Real-Time Tracking System using Cloud Computing
- ML Performance: 76.7% prediction accuracy, 48% improvement over baseline
- Dataset: 6 months operational data, 10 stops per route
This project is developed for academic and research purposes.
Authors: Pavan G (@Sin-Kid)
For new contributors:
- Read GIT_SETUP.md for environment setup
- Never commit
.envfiles or credentials - Follow existing code style and structure
- Test changes locally before pushing
For issues or questions:
- Check documentation in respective folders
- Review
USAGE.mdfor feature guides - Ensure all environment variables are correctly configured