Skip to content
fazrigadingPublic

About

[WIP] Turn an Android or iOS phone into a steering wheel and dashboard control panel for PC racing and trucking simulators. Runs on Linux and Windows desktops paired with an Android or iOS phone; macOS planned.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

WheelDeck

Project Status: Alpha — v0.1.0-alpha.1 prereleased from main.

Turn an Android phone into a steering wheel and dashboard control panel for PC racing and trucking simulators. A desktop companion app translates the phone's input into a virtual game controller the simulator reads natively.

Runs on Linux and Windows desktops paired with an Android phone.

Primary target: Euro Truck Simulator 2.

How it works

Architecture diagram of fazrigading/wheeldeck

%% Generated by https://gitdiagram.com/fazrigading/wheeldeck
flowchart TD

subgraph group_mobile["Mobile Experience"]
  node_android_client["Android Client<br/>[MainActivity.kt]"]
  node_ios_client["iOS Client"]
end

subgraph group_desktop["Desktop Shell"]
  node_desktop_app["Desktop App<br/>[CompositionRoot.cs]"]
  node_desktop_ui["Desktop UI<br/>[MainWindow.cs]"]
end

subgraph group_network["Network Session"]
  node_mdns["mDNS Advertiser<br/>[MdnsAdvertiser.cs]"]
  node_websocket["WebSocket Listener"]
  node_pairing_service["Pairing Service<br/>[PairingService.cs]"]
  node_session_gate["Session Gate<br/>[SessionGate.cs]"]
  node_heartbeat["Heartbeat Monitor"]
end

subgraph group_pairing["Pairing State"]
  node_protocol["Protocol Messages<br/>[SessionMessages.cs]"]
  node_pairing_manager["Pairing Manager<br/>[PairingManager.cs]"]
  node_pairing_store[("Pairing Store")]
end

subgraph group_output["Input Output"]
  node_input_mapper["Input Mapper<br/>[InputMapper.cs]"]
  node_windows_backend["Windows Backend"]
  node_linux_backend["Linux Backend<br/>[UinputBackend.cs]"]
end

node_desktop_user(("Desktop User"))
node_simulator(("Racing Simulator"))

node_desktop_user -->|"configures"| node_desktop_ui
node_mdns -->|"advertises"| node_android_client
node_mdns -.->|"advertises"| node_ios_client
node_android_client -->|"streams messages"| node_websocket
node_ios_client -.->|"streams messages"| node_websocket
node_websocket -->|"decodes messages"| node_protocol
node_websocket -->|"dispatches pairing"| node_pairing_service
node_websocket -->|"dispatches input"| node_session_gate
node_pairing_service -->|"validates pairing"| node_pairing_manager
node_pairing_manager -->|"reads and writes"| node_pairing_store
node_pairing_service -->|"binds session"| node_session_gate
node_session_gate -->|"checks authorization"| node_pairing_manager
node_session_gate -->|"forwards input"| node_input_mapper
node_session_gate -->|"signals heartbeat"| node_heartbeat
node_input_mapper -.->|"uses backend"| node_windows_backend
node_input_mapper -.->|"uses backend"| node_linux_backend
node_windows_backend -->|"emits controller"| node_simulator
node_linux_backend -->|"emits joystick"| node_simulator
node_desktop_app -->|"starts server"| node_websocket
node_desktop_app -->|"starts discovery"| node_mdns

click node_android_client "https://github.com/fazrigading/wheeldeck/blob/main/mobile/android/app/src/main/kotlin/dev/fazrigading/wheeldeck/MainActivity.kt"
click node_ios_client "https://github.com/fazrigading/wheeldeck/tree/main/mobile"
click node_desktop_app "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.App/CompositionRoot.cs"
click node_desktop_ui "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.App/MainWindow.cs"
click node_mdns "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Network/MdnsAdvertiser.cs"
click node_websocket "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Network/WebSocketListener.cs"
click node_pairing_service "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Network/PairingService.cs"
click node_session_gate "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Network/SessionGate.cs"
click node_heartbeat "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Network/HeartbeatMonitor.cs"
click node_protocol "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Protocol/SessionMessages.cs"
click node_pairing_manager "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Pairing/PairingManager.cs"
click node_pairing_store "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Pairing/JsonFilePairingStore.cs"
click node_input_mapper "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Core/Input/InputMapper.cs"
click node_windows_backend "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Backends/Windows/HidMaestroBackend.cs"
click node_linux_backend "https://github.com/fazrigading/wheeldeck/blob/main/desktop/WheelDeck.Backends/Linux/UinputBackend.cs"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_android_client,node_ios_client,node_desktop_user toneBlue
class node_desktop_app,node_desktop_ui toneAmber
class node_mdns,node_websocket,node_pairing_service,node_session_gate,node_heartbeat toneMint
class node_protocol,node_pairing_manager,node_pairing_store toneRose
class node_input_mapper,node_windows_backend,node_linux_backend,node_simulator toneIndigo
Loading
  1. Phone captures gyroscope steering, touch pedals, and dashboard button presses
  2. Phone discovers the desktop server on the local network via mDNS
  3. One-time PIN pairing establishes a session
  4. State and button messages stream over WebSocket in real time
  5. Desktop app drives a virtual Xbox controller (Windows/HIDMaestro) or joystick (Linux/uinput)
  6. Simulator reads the virtual controller natively — no game plugins needed

Components

Directory Stack Description
android/ Kotlin 2.x + Jetpack Compose Phone app — sensor capture, WebSocket client, on-screen dashboard
desktop/ C#/.NET 10 + Avalonia UI Desktop server — WebSocket listener, virtual controller backend, pairing
protocol/schema/ JSON Schema Shared message formats and control enums (single source of truth)

Release status

  • Current: v0.1.0-alpha.1 (tag on main). GitHub Release is a prerelease with generated notes.
  • Tag push matching v* triggers .github/workflows/release.yml: Android APK, Windows x64 zip, Linux x64 tarball.
  • Android alpha is signed with debug keys (android/app/build.gradle.kts); real signing lands before stable.
  • The alpha above predates the Kotlin rewrite: it was cut from the Flutter app. The Kotlin app in android/ has never been run on a device. The next tag will be the first to ship it, and it should not be called a regression from alpha.

Migration: phone app Flutter → Kotlin

  • Done. The native Android app (Kotlin + Jetpack Compose, Material 3, single :app module) replaced the Flutter app, which is deleted. The Dart source is preserved on the backup/flutter-port branch.
  • Plan and per-task acceptance criteria: plan/finished/migration-kotlin-1.md. What the cutover cost, and what is still unverified: tasks/task-12-leftovers.md.
  • Seven TODO.md Mobile items were built as acceptance criteria rather than ported as bugs. Five are shipped; two are implemented but device-unverified.
  • iOS has no client. The release workflow built a PWA from the Flutter web build, and there is no web target now — it was P2 and never served natively.

Project structure

See docs/project-structure.md for the full repository layout.

Quick start

Phone app

Needs JDK 21 and an Android SDK with platform 37.

cd android
./gradlew installDebug

Desktop server

cd desktop
dotnet build
dotnet run --project WheelDeck.App

Linux: run scripts/linux/install-uinput-rules.sh from the repo root, then apply the commands it prints. Covers Fedora/RHEL, Ubuntu/Debian derivatives (Mint, Pop!_OS, Zorin), and Arch-based (CachyOS, Manjaro, EndeavourOS).

See docs/mobile-dev-guide.md and docs/desktop-dev-guide.md for full setup instructions including platform-specific driver requirements.

Supported platforms

Platform Status Driver Virtual controller
Android P0 Native Fully functional
Windows P1 HIDMaestro XBOX 360 Controller
Linux P0 uinput Virtual Joystick

Docs

Full specs, guides, and architecture decisions: docs/

On-device verification steps for the phone app: tasks/manual-checklist.md.

About

[WIP] Turn an Android or iOS phone into a steering wheel and dashboard control panel for PC racing and trucking simulators. Runs on Linux and Windows desktops paired with an Android or iOS phone; macOS planned.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages