Distributed scoring, display and court-management platform for commercial padel operations.
PadelSystem is a commercial multi-court scoring and display platform developed for padel courts located in Colima, Mexico. It coordinates multiple courts through a central server, browser-based management portal, Raspberry Pi displays and ESP32 smartwatches while maintaining one authoritative match state.
Client identity, production credentials, venue information and deployment-specific configuration are intentionally excluded from this public repository.
The following views were captured from the running applications using fictional demonstration data.
PadelSystem was designed as an operational product rather than a classroom or research prototype. Its principal requirements are:
- support for multiple independent courts;
- simple scoring input from wearable devices;
- real-time court displays;
- administration and client-facing browser portals;
- authentication and role-based access;
- event deduplication and undo history;
- resilient display operation during temporary network loss;
- centralized media and advertisement synchronization;
- deployment on affordable, locally managed hardware.
flowchart LR
A[ESP32 smartwatch] -->|Scoring events| S[FastAPI server]
B[Admin / client browser] <-->|REST + session auth| S
S <-->|Court state + media sync| C[Raspberry Pi display]
S --> D[(SQLAlchemy database)]
S --> E[Scoring engine]
S --> F[Device and media services]
The server is the system of record. Smartwatches send idempotent events, displays subscribe to court state, and web clients manage courts, matches, devices, users and media.
| Layer | Technology |
|---|---|
| Central server | FastAPI · Python · SQLAlchemy · SQLite |
| Web application | React · TypeScript · Vite |
| Real-time communication | WebSockets · JSON protocol |
| Authentication | Password hashing · signed bearer/session tokens · roles |
| Court display | Raspberry Pi · PySide6 · asyncio |
| Wearable firmware | ESP32 · ESP-IDF · LVGL |
| Legacy wearable prototype | Arduino · ESP32 · arduinoWebSockets |
| Testing | pytest · fake device clients · pure scoring-engine tests |
PadelSystem/
├── server/
│ ├── app/
│ │ ├── models/ # SQLAlchemy domain models
│ │ ├── schemas/ # REST and WebSocket contracts
│ │ ├── scoring/ # Pure, testable scoring engine
│ │ ├── services/ # Match, device, media and auth logic
│ │ ├── ws/ # Watch/display connection handling
│ │ ├── routers/ # REST and browser-facing endpoints
│ │ └── templates/ # Legacy administration UI
│ └── tests/
├── web/
│ └── src/ # React administration and client portals
├── pi_display/
│ └── app/
│ ├── widgets/ # Scoreboard, status and advertisement UI
│ └── media/ # Local media cache and playlist management
├── firmware/
│ ├── smartwatch_esp32_idf/ # Current ESP-IDF/LVGL firmware
│ └── smartwatch_esp32/ # Legacy Arduino implementation
├── shared/protocol/ # Message contracts and examples
├── docs/
│ ├── architecture.md
│ └── deployment.md
├── .env.example
└── README.md
- Authoritative server state: devices submit events rather than directly modifying scores.
- Idempotent watch events: each event has a unique ID, preventing duplicate points after reconnects or retries.
- Real-time synchronization: WebSockets propagate score, timer, device and media changes.
- Multi-court by design: courts and device pairs are data-driven rather than hardcoded.
- Offline-tolerant displays: Raspberry Pi clients retain the latest state and cached media during short outages.
- Pure scoring engine: padel/tennis rules are isolated from transport and persistence for deterministic testing.
- Migration path: the server can move from SQLite to PostgreSQL without changing the domain architecture.
The engine implements standard padel/tennis scoring:
- points:
0 → 15 → 30 → 40 → game; - deuce and advantage;
- sets won by two games;
- tiebreak at 6–6;
- configurable sets required to win;
- bounded undo history for operator recovery.
All real-time messages use JSON and include a type field.
Watch → server: hello, watch_event, heartbeat
Server → watch: hello_ack, ack, state_update
Display → server: hello, heartbeat
Server → display: hello_ack, state_update, no_match, media_sync
The full contract is documented in shared/protocol/message_schema.md.
cd web
npm install
npm run build
cd ..cd server
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp ../.env.example .env
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reloadOpen http://localhost:8000.
cd pi_display
export DISPLAY_ID=display_01
export DISPLAY_COURT_ID=court_01
export SERVER_WS_URL=ws://padel.local:8000
export SERVER_HTTP_URL=http://padel.local:8000
export DEVICE_TOKEN=replace-with-device-token
python3 -m app.mainCopy deployment-specific values into the firmware configuration before building:
firmware/smartwatch_esp32_idf/main/config/padel_watch_config.hpp
The checked-in file contains placeholders only. Production Wi-Fi credentials and device tokens must remain outside the repository.
Run the backend unit and protocol tests with:
cd server
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 .venv/bin/python -m pytest tests/ -vA fake watch client is included for end-to-end testing without physical hardware.
The present architecture is suitable for deployment on a trusted local network. Before exposing it over the public internet, the remaining production-hardening work includes:
- HTTPS/WSS termination;
- per-device credentials instead of one shared device token;
- rotation and external management of all secrets;
- PostgreSQL for larger or multi-site deployments;
- deployment monitoring, backup and recovery procedures.
See docs/deployment.md for the current deployment model.
Active client product / deployment preparation.
The system remains under development as hardware, installation and operational requirements are validated with the client.




