A local ground control station for UAVs — map, telemetry, and serial link in one dashboard.
UAV Ground Control Station is a slim, locally operated GCS that runs on the operator’s machine. A shared React UI shows position, battery, radio link, flight mode, and session stats on a MapLibre map — whether data arrives over CRSF, MAVLink, or both.
| Runtime | Use case | Protocols | Serial access |
|---|---|---|---|
| Desktop (Tauri, recommended) | Primary operator runtime, TX16S, Windows COM* |
CRSF + MAVLink, GCS wake-up | Native Rust |
| Browser + Node | Development, MAVLink-direct links | MAVLink | apps/server (fallback) |
For RadioMaster TX16S with a USB telemetry mirror, the desktop app is the intended runtime (420000 baud, CRSF-first). See docs/adr/0001-dual-runtime-desktop-canonical.md.
This project is experimental ground-control software. Do not use it for unsafe, unsupervised, or illegal UAV operation. Always follow local regulations, manufacturer guidance, and safe test procedures.
Prefer bench testing and disconnected telemetry validation before using the software with real flight hardware.
- Live map with flight track (up to 5000 points), home reference, distance, and bottom-center Attitude HUD (pitch ladder, roll arc, heading tape, speed/altitude, climb bar, armed/mode)
- Telemetry sidebar — Text or Inst (mini gauges with the same telemetry fields as text mode); drag card headers (⠿) to reorder (shared order for both views, stored in
uav-gcs.sidebar.order); Reset restores recommended flight-priority order; alerts stay fixed at the top - Serial link — port picker (USB/PNP preferred), manual path entry, common baud rates
- Activity log — connection status, parser stats, frame message stats
- Optional video stream (MJPEG, etc.) via environment variables
- Session logging and reset for new flights
- Replay & Simulation — frontend-only, read-only telemetry sources that drive the same dashboard without hardware: replay recorded
.jsonl/.jsonlogs (start/pause/seek/step, speed and timing modes) or run deterministic seeded simulations. Seedocs/replay-mode.mdanddocs/adr/0003-frontend-only-replay-simulation.md - Shared data model
TelemetryStatefor desktop and browser
Desktop runtime: map, telemetry sidebar, frame message stats, and optional camera panel.
Telemetry data flow:
TX16S / Flight Controller
|
| USB serial telemetry
v
Desktop / Node parser
|
| normalized TelemetryState
v
React operator dashboard
Monorepo layout:
packages/shared Shared types & TelemetryState
apps/web React + Vite + Tailwind + MapLibre (shared UI)
apps/desktop Tauri v2 + Rust (CRSF/MAVLink, COM*, wake-up) ← canonical
apps/server Fastify + WebSocket + serialport (MAVLink, dev/fallback)
Domain language and terms: CONTEXT.md.
- Node.js ≥ 20.19
- pnpm 10.x (
corepack enablerecommended) - Desktop build: Rust and Tauri v2 prerequisites
- Windows: run maintenance commands (
pnpm install,lint,typecheck) in WSL; run Tauri dev/build for realCOM*hardware in Windows PowerShell
git clone https://github.com/gitfeber/UAVGroundControlStation.git
cd UAVGroundControlStation
pnpm installPre-built installers (no local Rust toolchain required):
- Open GitHub Releases for this repository.
- Download the asset for your OS (Windows
.msi/ setup.exe, or Linux.deb/.AppImage). - Install and launch UAV Ground Control Station.
Each push to main runs tests first; if they pass, CI publishes a preview prerelease with Windows and Linux installers (tag like v0.2.1-build.42, job publish in .github/workflows/ci.yml). Stable releases use a version tag such as v0.2.1 (see .github/workflows/release.yml and the release process below).
From source:
pnpm dev:desktopRelease build (local MSI/installer):
pnpm build:desktopArtifacts: apps/desktop/src-tauri/target/release/bundle/
Maintainers — stable release (optional):
Automatic preview prereleases happen on every green main push. For a non-prerelease “stable” release:
git tag v0.2.1
git push origin v0.2.1The release workflow builds Windows and Linux installers and attaches them to the GitHub release (no macOS builds in CI). You can also trigger it manually under Actions → Release → Run workflow.
If the release job fails with Resource not accessible by integration, enable Settings → Actions → General → Workflow permissions → Read and write permissions for the repository.
cp .env.example .env
pnpm dev- Backend:
http://localhost:3001 - Frontend:
http://localhost:5173
| Scenario | Typical baud rate |
|---|---|
| TX16S telemetry mirror (CRSF-first) | 420000 |
| Flight controller USB (MAVLink direct) | 115200 or 460800 |
Symptom: status shows “Serial linked” but no map/telemetry → usually wrong baud rate or wrong runtime (for TX16S, use desktop).
Host COM* ports are often unreachable from WSL-hosted Node. For TX16S and CRSF, use the desktop app, not the browser stack over WSL.
- Windows:
COM* - macOS:
/dev/cu.*,/dev/tty.* - Linux:
/dev/ttyACM*,/dev/ttyUSB*
Empty system ports without device metadata are hidden; unusual paths can be entered manually.
.env at the repository root (see .env.example):
| Variable | Description |
|---|---|
VITE_API_BASE_URL |
Node server REST API (default: http://localhost:3001) |
VITE_WS_URL |
WebSocket for telemetry (default: ws://localhost:3001/ws) |
VITE_MAP_STYLE_URL |
Optional: full MapLibre style URL |
VITE_SATELLITE_TILE_URL |
Optional: raster satellite tile URL |
VITE_VIDEO_URL / VITE_VIDEO_KIND |
Optional: camera stream (e.g. MJPEG) |
Server: PORT (default 3001), HOST (default 127.0.0.1) in apps/server. The server exposes unauthenticated serial-control endpoints; it binds loopback only. Setting HOST to a routable address (e.g. 0.0.0.0) is a deliberate opt-in that lets any device on the network open or close the link to flight hardware — see docs/adr/0002-server-loopback-only.md.
pnpm lint
pnpm typecheck
pnpm build # browser stack
pnpm build:desktop # desktop installerIf you open the repository root in VS Code, rust-analyzer is configured via .vscode/settings.json to use the Tauri crate at apps/desktop/src-tauri/Cargo.toml.
Agent and architecture rules: AGENTS.md.
Three workflows split the responsibilities, so it is always clear what publishes a release and what does not:
| Workflow | Trigger | Publishes? |
|---|---|---|
branch-checks.yml |
Pull requests | No — validation only (typecheck, lint, tests, build) |
ci.yml |
Push to main/master |
Preview prerelease (tag v<version>-build.<run>) after tests pass |
release.yml |
Pushed v* tag or manual dispatch |
Stable release (prerelease: false) |
In short:
-
Pull requests run
branch-checks.ymland never publish. -
Merges to
mainautomatically create a preview prerelease with Windows and Linux installers, clearly marked as an automated build. -
Stable releases are created by pushing a semantic version tag:
git tag v0.2.1 git push origin v0.2.1
You can also start the stable Release workflow manually from the GitHub Actions tab (Actions → Release → Run workflow).
Both ci.yml and release.yml set generateReleaseNotes: true, so GitHub appends auto-generated notes grouped by the categories defined in .github/release.yml (Features, Fixes, Documentation, Maintenance, Dependencies, Other Changes).
apps/desktop/ Tauri + Rust (primary serial link)
apps/server/ Node backend (dev/fallback)
apps/web/ React dashboard
packages/shared/ API and telemetry contracts
docs/adr/ architecture decisions
No public license is provided. All rights reserved unless stated otherwise.

