Skip to content

Repository files navigation

LMU PIT WALL

🌐 Language: English Β· Русский

WEC-style real-time telemetry dashboard for Le Mans Ultimate (rFactor 2 engine). Built for endurance racing β€” your remote strategist watches the same data as a real pit-wall engineer.

tech engine platform license

A self-hosted alternative to services like mylmu. Reads the rF2 Shared Memory Map Plugin sections directly, streams a compact JSON snapshot over WebSocket at 50 Hz, and renders it as a hardcore monospace dashboard that mirrors the look of a real WEC pit wall.

Features

  • Live telemetry @ 50 Hz β€” gear, RPM, speed, throttle/brake/clutch, steering
  • 4-wheel tire panel β€” inner/center/outer temperatures with coldβ†’optimalβ†’hot colour mapping, pressure (PSI + kPa), wear, carcass and brake temps
  • Car status β€” fuel (with low/critical warning), water temp, oil temp, turbo boost
  • Timing tower β€” position, lap, current/last/best times, full S1/S2/S3 table with delta column, session-best (magenta) highlighting, active sector indicator
  • Strategy assist β€” rolling fuel-per-lap, laps-left estimate, stop count, penalties, gap to ahead/leader
  • Flag strip β€” LIVE / PIT / SPDLIM / OVERHEAT / FUEL! / YELLOW
  • Auto-reconnect WebSocket with exponential backoff
  • --demo mode β€” runs without LMU, perfect for testing the UI on macOS/Linux
  • Single-file frontend β€” no build step, no npm, no framework

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Shared Memory    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   WebSocket 50Hz   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Le Mans      β”‚ ──────────────────► β”‚  server.py   β”‚ ─────────────────► β”‚  index.html  β”‚
β”‚ Ultimate +   β”‚   $rFactor2SMMP_    β”‚  FastAPI +   β”‚      ws://         β”‚  Vanilla JS  β”‚
β”‚ rF2 SMMP     β”‚     Telemetry$      β”‚  ctypes      β”‚     /ws            β”‚  Monospace   β”‚
β”‚ Plugin       β”‚     Scoring$        β”‚  parser      β”‚                    β”‚  Dashboard   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                            ngrok / cloudflared
                                                                   β”‚
                                                                   β–Ό
                                                       Strategist's browser anywhere

Prerequisites

  1. Le Mans Ultimate installed (Windows).
  2. rF2 Shared Memory Map Plugin (TheIronWolfMod) β€” required so that the $rFactor2SMMP_Telemetry$ / $rFactor2SMMP_Scoring$ sections exist:
    • download rF2SharedMemoryMapPlugin64.dll from the original repo
    • drop it into <LMU install>\Bin64\Plugins\
    • enable the plugin inside the game (Settings β†’ Plugins)
  3. Python 3.10+.

Install

git clone https://github.com/<your-user>/le-mans-ultimate-telemetry.git
cd le-mans-ultimate-telemetry

python -m venv .venv
.\.venv\Scripts\Activate.ps1

pip install fastapi "uvicorn[standard]"

Run

# Demo mode β€” synthetic data, no LMU required (great for UI checks)
python server.py --demo

# Production β€” LMU is running, plugin is loaded
python server.py

# Custom port / rate
python server.py --host 0.0.0.0 --port 8000 --hz 60

Open http://127.0.0.1:8000/ β€” you should see the full dashboard. If LMU is in the main menu the UI will display AWAITING TELEMETRY; the server itself will not crash.

Remote access for your strategist

The backend must stay on the racing PC β€” only that machine has access to LMU's Shared Memory. Two pieces have to be live during a race:

1. Local Python server + ngrok tunnel

# terminal 1
python server.py

# terminal 2
ngrok http 8000
# β†’ https://<random>.ngrok-free.app

For 24h races, reserve a fixed ngrok subdomain so the URL never changes:

ngrok http --domain=your-reserved-name.ngrok-free.app 8000

2. Frontend (hosted on Cloudflare Pages)

The dashboard is already live at https://lmu-pitwall.pages.dev/. Your strategist opens that URL from anywhere. On the first visit they enter the WebSocket address once (or you send them a pre-baked link):

https://lmu-pitwall.pages.dev/?ws=wss://your-reserved-name.ngrok-free.app/ws

The URL is remembered in localStorage, so refreshes / reboots Just Work. A βš™ CONNECTION button in the footer lets them change it at any time.

For the strategist

A complete operating manual lives in two languages: STRATEGIST_GUIDE.md (English) / STRATEGIST_GUIDE.ru.md (Russian). Each covers the screen layout, tire-colour cheat sheet, lap-time colour code, 7 typical race scenarios and what to radio in each, troubleshooting, and a pit journal template. Send the right language to your engineer before the race.

Inside the dashboard, the ? GUIDE button (footer) opens an in-app condensed version of the same cheat sheet β€” strategist never has to leave the screen during a stint.

Deployment (Cloudflare Pages)

After cloning the repo:

npm install                # one-time, installs wrangler locally
npx wrangler login         # one-time, opens Cloudflare OAuth in browser
npm run deploy             # deploys static/ to https://lmu-pitwall.pages.dev

Useful follow-ups:

npm run list               # list past deployments
npm run tail               # live tail logs (HTTP requests, errors)
npm run deploy:preview     # deploy to a preview branch URL

Custom domain (optional)

  1. In the Cloudflare dashboard: Workers & Pages β†’ lmu-pitwall β†’ Custom domains β†’ Set up a custom domain.
  2. Enter pitwall.your-domain.com (or apex your-domain.com).
  3. If the domain's DNS is already on Cloudflare, it's wired automatically. If not, add a CNAME record pointing to lmu-pitwall.pages.dev at your registrar.

Layout

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ● LMU PIT WALL  Β· CKT Β· CAR Β· DRV Β· CLS Β· AIR/TRK/WET Β· sessionclock         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚           VITALS            β”‚      TIRES       β”‚      TIMING TOWER           β”‚
β”‚                             β”‚  β”Œβ”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚  β”‚ FL β”‚  β”‚ FR β”‚  β”‚  β”‚ POSITION β”‚ β”‚   LAP    β”‚  β”‚
β”‚   β”‚                   β”‚    β”‚  β””β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”˜  β”‚  β”‚    03    β”‚ β”‚    47    β”‚  β”‚
β”‚   β”‚        6          β”‚    β”‚  β”Œβ”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”  β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”‚
β”‚   β”‚                   β”‚    β”‚  β”‚ RL β”‚  β”‚ RR β”‚  β”‚  β”‚ CURR  3:31.423        β”‚  β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  β””β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”˜  β”‚  β”‚ LAST  3:30.124        β”‚  β”‚
β”‚   [β–ˆβ–ˆβ–ˆβ–ˆ shift lights]      β”‚                  β”‚  β”‚ BEST  3:28.912        β”‚  β”‚
β”‚                             β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   THR  BRK  CLT             β”‚   CAR STATUS     β”‚  β”‚ S1 / S2 / S3 / Ξ”       β”‚  β”‚
β”‚                             β”‚  FUEL 84.3L      β”‚  β”‚ ...                    β”‚  β”‚
β”‚   [── STEERING ──]          β”‚  WATER 88Β° OIL 96β”‚  β”‚ GAPS Β· FLAGS           β”‚  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Project layout

le-mans-ultimate-telemetry/
β”œβ”€β”€ server.py                 # FastAPI + ctypes SMMP reader + WebSocket stream
β”œβ”€β”€ static/
β”‚   └── index.html            # Monolithic frontend (HTML + CSS + JS)
β”œβ”€β”€ package.json              # npm scripts for wrangler deploy
β”œβ”€β”€ wrangler.toml             # Cloudflare Pages config
β”œβ”€β”€ STRATEGIST_GUIDE.md       # Full strategist manual (English)
β”œβ”€β”€ STRATEGIST_GUIDE.ru.md    # Full strategist manual (Russian)
β”œβ”€β”€ README.md                 # This file (English)
β”œβ”€β”€ README.ru.md              # This file (Russian)
β”œβ”€β”€ LICENSE
└── .gitignore

Tuning

Variable Where Default What it controls
STREAM_HZ (CLI --hz) server.py 50 WebSocket frame rate
tireColor() thresholds index.html JS 60 / 80–100 / 115 Β°C Cold / optimal / hot tire colour bands
fuel low / critical index.html JS 25 / 10 L When the FUEL card turns orange / blinks red
ovh threshold index.html JS water > 110 Β°C or oil > 130 Β°C Activates OVHEAT flag

Troubleshooting

  • "AWAITING TELEMETRY" stays forever β€” the SMMP plugin DLL is not loaded. Verify the path (<LMU>\Bin64\Plugins\) and that LMU has loaded it (Settings β†’ Plugins checkbox).
  • ModuleNotFoundError: No module named 'fastapi' β€” activate your venv, then pip install fastapi "uvicorn[standard]".
  • Strategist gets "Mixed Content" error β€” ngrok HTTPS forwards to plain HTTP backend, this is normal; the frontend uses wss:// so it works.
  • Wrong tire temperatures β€” values are converted from Kelvin in _wheel_to_dict(). Double-check KELVIN constant if your readings look off by ~273Β°.

Credits

  • Shared memory layout based on rF2SharedMemoryMapPlugin by TheIronWolfMod
  • Visual language inspired by FIA WEC / F1 pit-wall engineer dashboards

About

WEC-style real-time telemetry dashboard for Le Mans Ultimate / rFactor 2. Python FastAPI + WebSocket backend reading Shared Memory; monolithic vanilla-JS pit-wall frontend.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages