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 β β
β βββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
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
| # | 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 |
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.
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
| 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 |
| Tool | Version | Purpose |
|---|---|---|
| Python | 3.10+ | Backend runtime |
| Node.js | 18+ | Frontend build |
| Mosquitto | 2.0+ | MQTT broker |
| PlatformIO | Latest | ESP32 firmware builds |
| Git | Latest | Version control |
git clone <repository-url>
cd LabEx_V1.2# Install Mosquitto, then:
mosquitto -c py_backend/mosquitto.confcd py_backend
# Create virtual environment
python -m venv .venv
# Activate (Windows)
.venv\Scripts\activate
# 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 --reloadThe 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 devAccess 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
| 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 } |
| 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 inprocessor_manager.py - Frontend: Add experiment config in
experimentConfig.jsβ UI auto-generates - Firmware: Follow the pattern in
LabExpert_Sensor_ESP32_CODES/
- Backend: Add new processor in
- 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