An IoT-powered laboratory experiment platform for physics education
LabExpert is a full-stack IoT platform that connects ESP32 sensor modules to a React web application through a Python backend , enabling real-time physics experiments in educational labs. Students and educators can select experiments, wirelessly connect to sensor hardware, collect live data, and visualize results β all from a browser.
π¬ 5 Experiment Categories β Distance, Oscillation, Temperature, Light Intensity, and AI-powered Motion Analysis
π‘ Real-Time Data Streaming β MQTT-based sensor data with binary packet optimization
π Live Graphing β Interactive Plotly.js charts with multi-axis visualization
π Over-the-Air Updates β Dual-partition OTA firmware deployment to ESP32 modules
π² BLE Provisioning β Zero-config WiFi setup for sensor modules via Bluetooth
π UDP Auto-Discovery β Automatic detection of sensor modules on the local network
π₯ Multi-User Support β Per-device session management with conflict prevention
π‘οΈ Admin Dashboard β User management, device monitoring, and system control
π± PWA Support β Installable as a native-like app on mobile and desktop
π Dark Mode β Full dark theme support across the application
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β React Frontend (PWA) β
β Vite 6 Β· React 19 Β· TailwindCSS Β· Plotly.js Β· Zustand β
β β
β ββββββββββββ ββββββββββββββββ βββββββββββββ ββββββββββββββββββββββββ β
β β Auth & β β User β β Experimentβ β Admin Dashboard β β
β β Signup β β Dashboard β β Interface β β & Management β β
β ββββββββββββ ββββββββββββββββ βββββββββββββ ββββββββββββββββββββββββ β
β β β β β β
β ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββββ β
β WebSocket + REST API β
βββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Python Backend (FastAPI) β
β Uvicorn Β· SQLAlchemy Β· paho-mqtt Β· Mosquitto Broker β
β β
β βββββββββββββββ ββββββββββββββ ββββββββββββββ βββββββββββββββββββββ β
β β Session & β β MQTT β β OTA β β Sensor Processor β β
β β Device Mgr β β Service β β Manager β β Pipeline β β
β βββββββββββββββ ββββββββββββββ ββββββββββββββ βββββββββββββββββββββ β
β βββββββββββββββ ββββββββββββββ ββββββββββββββ βββββββββββββββββββββ β
β β User Auth β β UDP β β BLE β β WebSocket Client β β
β β & Admin β β Discovery β β Service β β Manager β β
β βββββββββββββββ ββββββββββββββ ββββββββββββββ βββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββΌββββββββββββββββββββ
β MQTT (1883) β UDP (8888/8889) β HTTP OTA
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ESP32 Sensor Modules β
β β
β βββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββ β
β β OTA Bootloader β β Experiment Firmware β β
β β (Partition ota_0) β ββββΊ β (Partition ota_1) β β
β β β’ BLE Provisioning β β β’ TOF / Ultrasonic / Oscillation β β
β β β’ UDP Discovery β β β’ Temperature / Light β β
β β β’ OTA Web Server β β β’ MQTT Data Publishing β β
β βββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π Repository Structure
LabEx_V1.2/
β
βββ my-app/ # π₯οΈ React Frontend (PWA)
β βββ src/
β β βββ App.jsx # Root app with routing
β β βββ pages/
β β β βββ UserDashboard.jsx # Main user dashboard
β β β βββ AdminDashboard.jsx # Admin control panel
β β β βββ ExperimentInterface.jsx # Universal experiment UI
β β β βββ SensorProvisioning.jsx # BLE sensor setup wizard
β β β βββ ProgramSensor.jsx # Firmware programming UI
β β β βββ About.jsx # About page
β β βββ components/
β β β βββ Login.jsx / Signup.jsx # Authentication forms
β β β βββ ForgotPassword.jsx # Password recovery (OTP)
β β β βββ PlotlyGraph.jsx # Real-time graph engine
β β β βββ OSIInterface.jsx # Oscillation-specific UI
β β β βββ DeviceScanner.jsx # Device discovery UI
β β β βββ Header.jsx / Navbar.jsx # Navigation components
β β β βββ common/ # Shared UI components
β β βββ experiments/
β β β βββ experimentConfig.js # Experiment definitions (9 types)
β β β βββ experimentRegistry.js # Plugin system for experiments
β β βββ hooks/
β β β βββ useWebSocket.js # WebSocket connection hook
β β β βββ usePerformance.js # Performance monitoring
β β βββ stores/
β β β βββ authStore.js # Auth state (Zustand)
β β β βββ experimentStore.js # Experiment state (Zustand)
β β βββ context/
β β β βββ ThemeContext.jsx # Dark/light mode
β β β βββ FullscreenContext.jsx # Fullscreen graph mode
β β βββ services/ # API service layer
β β βββ utils/ # API client, helpers
β β βββ styles/ # CSS modules per page
β βββ vite.config.js # Vite + PWA config
β βββ package.json
β βββ server.js # Production Express server
β
βββ py_backend/ # βοΈ Python Backend
β βββ main.py # FastAPI entry point (1761 lines)
β βββ session_manager.py # Device registration & allocation
β βββ ws_client.py # WebSocket manager for frontend
β βββ ota_manager.py # Chunked OTA firmware delivery
β βββ sensor_service.py # ESP32 HTTP communication layer
β βββ services/
β β βββ mqtt_service.py # MQTT pub/sub + binary parsing
β β βββ udp_discovery_service.py # UDP broadcast device discovery
β β βββ ble_service.py # BLE WiFi provisioning
β β βββ user_service.py # User CRUD & authentication
β β βββ session_service.py # Token session management
β β βββ otp_service.py # OTP generation & verification
β β βββ file_service.py # File upload/download
β β βββ admin_auth_service.py # Admin JWT + CSRF security
β β βββ admin_user_service.py # Admin user management
β β βββ oscillation_service.py # Oscillation data analysis
β βββ processor/
β β βββ processor_manager.py # Processor factory & lifecycle
β β βββ sensor_base.py # Abstract base class
β β βββ sensor_displacement.py # TOF distance β kinematics
β β βββ sensor_oscillation.py # Period & frequency analysis
β β βββ sensor_disp_angle.py # Displacement + angle β energy
β β βββ sensor_galileo.py # Inclined plane analysis
β β βββ sensor_temperature.py # Temperature conversion
β β βββ sensor_video_oscillation.py # AI vision oscillation
β βββ config/
β β βββ database.py # SQLAlchemy + SQLite schema
β βββ utils/
β β βββ config.py # Paths & constants
β β βββ crypto.py # Encryption utilities
β β βββ network_utils.py # IP/SSID detection
β β βββ packet.py # Binary packet codec
β βββ firmware/
β β βββ firmware_registry.json # Firmware name β file mapping
β β βββ *.bin # Compiled ESP32 binaries
β βββ mosquitto.conf # MQTT broker configuration
β βββ requirements.txt # Python dependencies
β
βββ LabExpert_Sensor_ESP32_CODES/ # π ESP32 Firmware (separate repo)
β βββ ESP_32_OTA/ # OTA bootloader
β βββ THR_Firmware_bin_Generator/ # Temperature firmware
β βββ TOF_Firmware_bin_Generator/ # Time-of-Flight firmware
β βββ OSI_Firmware_bin_Generator/ # Oscillation firmware
β βββ UltraSonic_Firmware_bin_Generator/ # Ultrasonic firmware
β βββ shared/ # Shared ESP32 libraries
β βββ README.md # Detailed firmware documentation
β
βββ docs/ # π Documentation
β βββ admin-auth-api.md # Admin API reference
β βββ admin-auth-schema.md # Admin database schema
β
βββ requirements.txt # π¦ Root Python dependencies
βββ .gitignore
π§ͺ Supported Experiments
#
Experiment
Sub-Experiments
Sensor
Data Output
1
π Distance Measurement
Free Fall, Modern Galileo
VL53L1X TOF / HC-SR04
Distance, Velocity, Acceleration
2
π Oscillation Counter
Simple Pendulum, Compound Pendulum
LDR/Laser Gate
Period, Frequency, TΒ² vs L
3
π‘οΈ Temperature Monitoring
Live Temperature
DS18B20
Β°C, Β°F, K vs Time
4
π‘ Light Intensity
Intensity Monitor
BH1750
Lux vs Time
5
π Motion Analysis (AI)
Simple Pendulum, Galileo Experiment
Camera (Vision)
Angle, Distance, Velocity
Experiment Configuration System
Each experiment is defined declaratively in experimentConfig.js:
{
id : '1.1' ,
name : 'Free Fall Experiment' ,
firmware : 'TOFFFE.bin' ,
dataFields : [ 'time' , 'distance' , 'velocity' , 'acceleration' ] ,
graphConfig : { xAxis : 'time' , yAxes : [ 'distance' , 'velocity' , 'acceleration' ] } ,
tableConfig : { columns : [ ...] } ,
sensorOptions : [
{ type : 'TOF' , label : 'TOF Sensor' , firmware : 'TOFFFE.bin' } ,
{ type : 'ULT' , label : 'ULT Sensor' , firmware : 'ULTFFE.bin' }
] ,
defaultConfig : { frequency_hz : 20 , duration_s : 10 }
}
Adding a new experiment only requires editing the config file β no UI code changes needed .
π Communication Protocols
ESP32 Sensor Backend Frontend
β β β
βββ MQTT Binary Data ββββββΊβ β
β (timestamp, distance, βββ Process via βββββββββββΊβ
β sample#) β SensorProcessor β
β β β
β βββ WebSocket JSON ββββββββΊβ
β β (processed kinematics) β
β β β
ββββ MQTT Commands βββββββββββ WebSocket Commands βββ
β (start/stop/config) β (scan/select/config) β
β β β
ββββ UDP Discovery ββββββββ β
β (broadcast @ 8888) β β
βββ UDP Response ββββββββββΊβ β
β (device_id, sensor, β β
β IP, MAC @ 8889) β β
Topic Pattern
Direction
Purpose
labexpert/{device_id}/data
ESP32 β Backend
Sensor data (binary packed)
labexpert/{device_id}/data/binary
ESP32 β Backend
Binary sensor packets
labexpert/{device_id}/status
ESP32 β Backend
Device status updates
labexpert/{device_id}/config
Backend β ESP32
Experiment configuration
labexpert/{device_id}/command
Backend β ESP32
Start/Stop/Pause/Resume
labexpert/{device_id}/disconnect
Backend β ESP32
Cleanup & reboot to OTA
SQLite database with 11 tables managed via SQLAlchemy:
erDiagram
users {
TEXT id PK
TEXT name
TEXT email UK
TEXT password
TEXT role
INTEGER is_email_verified
TEXT profile_picture
}
sessions {
INTEGER id PK
TEXT user_id FK
TEXT token
DATETIME expires_at
}
experiment_runs {
TEXT id PK
TEXT user_id FK
TEXT experiment_type
TEXT sub_experiment
TEXT run_id
TEXT filename
}
device_allocations {
INTEGER id PK
TEXT device_id UK
TEXT user_id FK
DATETIME expires_at
}
available_sensors {
INTEGER id PK
TEXT sensor_id UK
INTEGER availability
INTEGER online_status
TEXT last_firmware
}
admin_users {
INTEGER id PK
TEXT email UK
TEXT password_hash
TEXT role
INTEGER must_change_password
}
users ||--o{ sessions : has
users ||--o{ experiment_runs : performs
users ||--o{ device_allocations : allocates
admin_users ||--o{ admin_sessions : has
Loading
Feature
Implementation
User Authentication
JWT tokens, bcrypt password hashing
Email Verification
OTP via email (yagmail)
Admin Auth
Separate JWT with CSRF protection
Password Security
Complexity validation, history tracking (last 5)
Rate Limiting
5 login attempts per 5 minutes per IP
Session Management
Auto-expiry, inactivity timeout (30 min admin)
CORS
Dynamic origin validation for local network
git clone < repository-url>
cd LabEx_V1.2
# Install Mosquitto, then:
mosquitto -c py_backend/mosquitto.conf
cd py_backend
# Create virtual environment
python -m venv .venv
# Activate (Windows)
.venv\S cripts\a ctivate
# Install dependencies
pip install -r requirements.txt
# Create .env file
cp .env.example .env # Or create with required vars
# Start the backend
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
The backend will:
Initialize the SQLite database (data/lab_expert.db)
Start the MQTT service (connects to Mosquitto)
Start UDP discovery service (port 8888/8889)
Seed default admin account (labexpert.us@gmail.com)
cd my-app
# Install dependencies
npm install
# Start development server
npm run dev
Access the application at http://localhost:5173
cd my-app
npm run build # Build to dist/
npm run start # Serve via Express (port 3000)
sequenceDiagram
actor Student
participant Frontend
participant Backend
participant MQTT as Mosquitto
participant ESP32
Student->>Frontend: Login / Signup
Frontend->>Backend: POST /api/login
Backend-->>Frontend: JWT Token
Student->>Frontend: Select Experiment
Frontend->>Backend: WS: scan_devices
Backend->>ESP32: UDP Broadcast (port 8888)
ESP32-->>Backend: UDP Response (ID, sensor, IP)
Backend-->>Frontend: WS: device_list
Student->>Frontend: Select Device
Frontend->>Backend: WS: select_device
Backend->>Backend: Allocate device to user
Student->>Frontend: Configure & Start
Frontend->>Backend: WS: flash_firmware
Backend->>ESP32: HTTP OTA (chunked upload)
ESP32-->>Backend: Reboot to experiment firmware
Backend->>MQTT: Publish config & start command
MQTT->>ESP32: Config + Start
ESP32->>MQTT: Binary sensor data
MQTT->>Backend: Forward data
Backend->>Backend: Process via SensorProcessor
Backend-->>Frontend: WS: processed kinematics
Student->>Frontend: View real-time graph
Student->>Frontend: Stop experiment
Frontend->>Backend: WS: stop_experiment
Backend->>MQTT: Stop command
ESP32-->>Backend: Cleanup, reboot to OTA
Loading
π§ Backend Service Architecture
Service
File
Responsibility
SessionManager
session_manager.py
Device registration, userβdevice allocation, UDP discovery orchestration
ClientWebSocketManager
ws_client.py
Frontend WS connections, experiment lifecycle commands, BLE provisioning
MQTTService
services/mqtt_service.py
MQTT pub/sub, binary data parsing, device status forwarding
UDPDiscoveryService
services/udp_discovery_service.py
Periodic broadcast discovery, online device registry
OTAManager
ota_manager.py
Firmware selection, chunked HTTP upload to ESP32, progress tracking
SensorProcessorManager
processor/processor_manager.py
Factory for 6 sensor processors, per-device lifecycle
UserService
services/user_service.py
User CRUD, bcrypt auth, profile management
AdminAuthService
services/admin_auth_service.py
Admin JWT, CSRF tokens, rate limiting, password history
OTPService
services/otp_service.py
Email OTP generation, verification, expiry
FileService
services/file_service.py
Experiment data export (CSV, JSON), profile pictures
BLEService
services/ble_service.py
BLE scanning, WiFi credential provisioning via NimBLE
Processor
Experiment
Input
Output
DisplacementProcessor
Free Fall / Distance
time, distance
velocity, acceleration, smoothed curves
OscillationProcessor
Simple/Compound Pendulum
cut times
period, frequency, TΒ² vs L
DispAngleProcessor
Displacement + Angle
time, distance, angle
energy, forces
GalileoProcessor
Modern Galileo
time, distance
velocity, acceleration on incline
TemperatureProcessor
Temperature Monitor
raw temp
Β°C, Β°F, K conversions
VideoOscillationProcessor
AI Motion Analysis
video frames
angle, position tracking
Method
Endpoint
Description
POST
/api/login
User login (email + password)
POST
/api/signup
Create new account
GET
/api/me
Get current user profile
PUT
/api/profile
Update user profile
POST
/api/forgot-password
Send OTP to email
POST
/api/verify-otp
Verify OTP code
POST
/api/reset-password
Reset password with OTP
Method
Endpoint
Description
POST
/api/admin/auth/login
Admin login (sets HttpOnly cookie)
GET
/api/admin/auth/me
Admin profile
POST
/api/admin/auth/change-password
Change admin password
POST
/api/admin/auth/logout
Admin logout (clears cookies)
GET
/api/admin/users
List all admin users
POST
/api/admin/users
Create new admin
Method
Endpoint
Description
GET
/api/devices
List available sensor devices
POST
/api/devices/{id}/allocate
Allocate device to user
POST
/api/devices/{id}/release
Release device
POST
/api/experiment/configure
Configure experiment parameters
POST
/api/experiment/start
Start data collection
POST
/api/experiment/stop
Stop data collection
GET
/api/system/info
System health & stats
Event
Direction
Payload
scan_devices
Client β Server
β
device_list
Server β Client
{ devices: [...] }
select_device
Client β Server
{ device_id }
flash_firmware
Client β Server
{ device_id, experiment_type }
flash_progress
Server β Client
{ progress, message }
configure_experiment
Client β Server
{ config, experiment_type }
start_experiment
Client β Server
{ config }
sensor_data
Server β Client
{ time, distance, velocity, ... }
stop_experiment
Client β Server
β
ble_scan
Client β Server
β
ble_provision
Client β Server
{ ssid, password }
π₯οΈ Frontend Architecture
Technology
Purpose
React 19
UI components & hooks
Vite 6
Build tool & dev server
TailwindCSS 3
Utility-first styling
Plotly.js
Interactive scientific graphs
Recharts
Dashboard charts
Zustand
Lightweight state management
React Router v7
Client-side routing
Axios
HTTP client
Lucide React
Icon library
react-qr-code
QR code generation
Vite PWA
Progressive Web App support
Route
Component
Auth
Description
/login
Login
Public
User login
/signup
Signup
Public
User registration
/forgot-password
ForgotPassword
Public
Password recovery (OTP)
/about
About
Public
About page
/dashboard
UserDashboard
Private
Main user dashboard
/experiment/:id
ExperimentRouter
Private
Dynamic experiment UI
/sensor
SensorProvisioning
Private
BLE sensor setup
/sensor/program
ProgramSensor
Private
Firmware programming
/admin/login
AdminLogin
Public
Admin authentication
/admin/manage
AdminManage
Admin
Admin management panel
The ESP32 firmware lives in LabExpert_Sensor_ESP32_CODES/ β see its own README.md for comprehensive documentation covering:
Dual-partition OTA bootloader architecture
5 firmware generators (THR, TOF, OSI, UltraSonic, BH1750)
Shared libraries (LedController, NVS credentials)
Complete GPIO pin mapping
HTTP API endpoints
Build & flash instructions
Package
Version
Purpose
fastapi
0.117.1
Async web framework
uvicorn
0.24.0
ASGI server
SQLAlchemy
2.0.23
ORM & database
paho-mqtt
2.1.0
MQTT client
bcrypt
4.0.1
Password hashing
pydantic
2.6.1
Data validation
numpy / scipy
Latest
Scientific computation
bleak
1.1.1
BLE communication
yagmail
0.15
Email (OTP delivery)
pyotp
2.8.0
OTP generation
websockets
15.0.1
WebSocket support
python-dotenv
1.0.0
Environment config
cryptography
46.0.3
JWT token handling
pillow
11.3.0
Image processing
Package
Version
Purpose
react
19.1.0
UI framework
vite
6.3.5
Build tool
plotly.js
3.2.0
Scientific graphing
recharts
3.2.1
Dashboard charts
zustand
5.0.8
State management
react-router-dom
7.6.2
Routing
axios
1.9.0
HTTP client
tailwindcss
3.4.1
CSS framework
vite-plugin-pwa
1.0.3
PWA support
# Backend tests
cd py_backend
python -m pytest tests/
# MQTT connectivity test
python test_mqtt.py
# UDP discovery verification
python verify_discovery.py
Fork the repository
Create a feature branch: git checkout -b feature/new-experiment
Follow the modular architecture:
Backend: Add new processor in processor/, register in processor_manager.py
Frontend: Add experiment config in experimentConfig.js β UI auto-generates
Firmware: Follow the pattern in LabExpert_Sensor_ESP32_CODES/
Test all three layers (firmware β backend β frontend)
Submit a pull request
This project is licensed under the MIT License .
MIT License
Copyright (c) 2025 LabExpert
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Built with β€οΈ for physics education β React Β· FastAPI Β· ESP32 Β· MQTT