Server application for QRET's propulsion test stand. Discovers and communicates with ESP32 sensor/control devices over a custom binary TCP protocol, collects sensor data, controls valves, manages IP cameras, and exposes everything through a REST API and CLI.
The server is designed to run on any linux machine as a headless hub between ESP32 devices and any number of clients.
flowchart LR
ESP1[ESP32<br>Sensors & Valves] -->|TCP :50000| Server
ESP2[ESP32<br>Sensors & Valves] -->|TCP :50000| Server
Cam[IP Cameras] -->|ONVIF / RTSP| Server
Server[Server<br>Jetson Nano]
subgraph Clients
direction TB
GUI[Desktop GUI]
Web[Web Client]
API[REST / WebSocket]
end
Server -->|FastAPI :8000| GUI
Server -->|WebRTC / RTSP| Web
Server -->|HTTP / WS| API
Here((YOU ARE HERE)) --> Server:::youAreHere
classDef youAreHere stroke:red, stroke-width:6px;
linkStyle 5 stroke:red,stroke-width:4px
style Here fill:transparent,stroke:none,color:red;
| Service | Description |
|---|---|
| server | Main application — device discovery (SSDP), TCP listener, FastAPI, CLI, in-process log stream |
| media | MediaMTX RTSP/WebRTC relay for camera streams |
- Docker (recommended)
- Or: Python 3.12+ with uv
- Local non-Docker qlcp builds also require CMake and a C compiler
docker compose -f compose.dev.yml upThis starts all necessary services with file watching — code changes in src/ and config.yaml trigger automatic restarts.
Follow server logs with:
docker compose -f compose.dev.yml logs -f serverdocker compose -f compose.prod.yml up -dPulls pre-built images from ghcr.io/queens-rocket-engineering-team/.
uv sync
uv run python -m prop_teststanduv sync installs the environment. The qlcp native library and CFFI protocol
extension are rebuilt automatically during package installation.
Run uv sync again to force a local protocol rebuild.
Run the mock device locally for testing with:
uv run python tests/mock_device.pyThe server reads config.yaml for service connections and camera definitions:
accounts:
camera:
username: propcam
password: ...
services:
mediamtx:
ip: localhost
api_port: 9997
cameras:
- ip: 192.168.1.5
onvif_port: 2020Override the path with the PROP_CONFIG environment variable (defaults to ./config.yaml).
ESP32 devices configure themselves — each device sends a JSON CONFIG packet on connection describing its sensors and controls.
| Command | Description |
|---|---|
uv run python -m prop_teststand |
Start the main server |
uv run python tests/mock_device.py |
Simulate an ESP32 device for testing |
Once the server is running, an interactive CLI provides commands like discover, list, stream <device> <Hz>, control <device> <name> <state>, and estop.
Devices communicate using a custom binary protocol over TCP (port 50000) and UDP (port 50001). Devices are discovered via SSDP multicast on 239.255.255.250:1900. On discovery, the device opens a TCP connection to the server and sends its CONFIG. The server then time-syncs the device and normal operation begins (streaming, control commands, heartbeats).
For more information on protocol specifications, see ctl-qlcp-lib.
For the microcontroller side of this project, see ctl-node-firmware.
This project is intended to be opened in VSCode. Install the recommended extensions when prompted.