Native Linux daemon that drives the RPM LEDs on Thrustmaster racing wheel rims using live telemetry from racing sims running under Wine/Proton. No SimHub, no Windows tools — just a Python daemon talking HID to the wheel.
Tested on the Thrustmaster TS-PC Racer with the Ferrari 488 Challenge Edition wheel rim (15 RPM LEDs). Should work with any Thrustmaster base supported by hid-tmff2 — the LED protocol is a property of the rim, not the base.
| PID | Base |
|---|---|
B66E |
T300RS (PS3 normal) |
B66F |
T300RS (PS3 advanced) |
B66D |
T300RS (PS4) |
B669 |
TX |
B689 |
TS-PC Racer |
B692 |
TS-XW |
B696 |
T248 |
B6FF |
T818 |
The daemon auto-detects any of these by scanning /sys/class/hidraw/ for VID 044F and a known PID.
Racing Sim (Wine/Proton) e.g. AC, AMS2, ACC, ...
| Windows shared memory game-specific format
v
simshmbridge (*bridge.exe) runs in Wine, maps Win SHM -> /dev/shm/
|
simd (systemd user service) auto-detects game, maps to common format
| writes /dev/shm/SIMAPI.DAT
v
tm-led-daemon (Python 3.11+) reads SimData from SIMAPI.DAT
| Maps RPM % -> LED count (0-15)
| Blinks all LEDs at redline
v
Thrustmaster wheel -> RPM LEDs 64-byte HID packets to /dev/hidrawN
Any game that simd supports works automatically — no per-game code in the daemon.
Any game with a simshmbridge bridge works. Currently tested:
- Assetto Corsa
- Automobilista 2
Other games with simshmbridge support (rFactor 2, Le Mans Ultimate, RaceRoom) should work but are untested.
- Python 3.11+
- Thrustmaster wheel base with a rim that has RPM LEDs (see supported bases above)
- hid-tmff2 — kernel driver (DKMS, handles FFB and mode switch)
- simd — cross-sim telemetry mapping daemon
- simapi — shared library used by simd
- simshmbridge — bridge executables for each game (included as submodule)
x86_64-w64-mingw32-gcc— mingw-w64 cross compiler (to build bridge.exefiles)
git clone --recursive https://github.com/<user>/thrustmaster-led-linux.git
cd thrustmaster-led-linux/tm-led-daemon
./setup.shThe setup script builds the simshmbridge bridges, generates a simd config, installs the udev rule and systemd service. You still need to:
- Install simd and simapi (Arch AUR:
yay -S simapi-git simd-git) - Set Steam launch options for each game (the script prints the exact commands)
- Configure in-game settings if needed (e.g. AMS2 shared memory settings)
See tm-led-daemon/README.md for detailed setup instructions.
Edit tm-led-daemon/config.toml:
[device]
game = "simapi" # "simapi" (recommended) or "ac"
[led]
brightness = 20 # 0-100
shift_begin = 0.70 # RPM % where LEDs start lighting
shift_point = 0.95 # RPM % where LEDs start blinking
blink_rate_hz = 8 # blink speed at redline
poll_rate_hz = 60 # LED update rateThe daemon starts and stops automatically with the wheel:
- Plug/attach: udev detects VID
044Fwith a known PID, startstm-led-daemon.service(systemd user unit) - Unplug/detach: daemon detects
/dev/hidrawNdisappeared, exits cleanly - No restart loop: service has
Restart=no— udev starts it fresh on next plug
The correct hidraw device is auto-detected by scanning /sys/class/hidraw/ for the matching VID:PID.
To run manually:
python3 tm-led-daemon/daemon.py # normal operation
python3 tm-led-daemon/daemon.py --test # test LEDs (no game needed)
python3 tm-led-daemon/daemon.py --demo # simulate RPM sweep (no game needed)thrustmaster-led-linux/
├── tm-led-daemon/ # Python daemon — drives wheel LEDs from telemetry
│ ├── daemon.py # main entry point
│ ├── led.py # hidraw LED control, auto-detect by VID:PID
│ ├── telemetry.py # telemetry readers (SimAPI, HTTP relay)
│ └── config.toml # runtime configuration
├── TMLedRelay/ # CSP Lua app — AC-only alternative telemetry source
│ └── TMLedRelay.lua # reads telemetry in-game, POSTs to daemon
├── documentation/
│ ├── REPORT.md # FFB replug bug: root cause and fix
│ └── DRIVER_ANALYSIS.md # Windows driver reverse-engineering (Ghidra)
└── simshmbridge/ # git submodule: bridges game shared memory to /dev/shm/
The wheel rim LEDs are controlled via 64-byte HID output reports to /dev/hidrawN:
| Command | Byte 3 | Data | Description |
|---|---|---|---|
| LED update | 0x02 |
2 bytes (LE bitmask) | Each bit = one LED, 15 total |
| Brightness | 0x10 |
1 byte (0-100) | LED brightness level |
Full packet: {0x60, 0x00, 0x41, command, data..., 0x00...} (64 bytes)
Protocol source: Prodigal.Knight's SimHub plugin
- FFB replug bug report — root cause analysis of the
open_mode=0fix for FFB breaking after replug/usbip - Windows driver analysis — Ghidra reverse-engineering of the full Thrustmaster Windows driver stack (FFB, LEDs, display, motor tuning, firmware update)
GPLv3 — see LICENSE.