No description
  • C++ 70%
  • Python 24%
  • C 5.6%
  • Batchfile 0.2%
  • CMake 0.2%
Find a file
Justin Eloff 0540ca39ae
All checks were successful
Build Fuel Server Image / build (push) Successful in 18s
Fix multi-daemon support: increase Channels queue maxsize from 1 to 200 in Server
With maxsize=1, when two daemons sent telemetry simultaneously to a shared
queue, only one daemon's message fit — the other was silently dropped via
queue.Full. This starved one daemon's data from reaching consumers.

The Channels class now accepts a configurable maxsize (default 1 for
single-daemon Client compatibility). The Server uses maxsize=200 to buffer
interleaving frames from multiple daemons without dropping them.
2026-07-11 23:02:00 +02:00
.forgejo/workflows Use core/checkout@main (no v4 tag available) 2026-07-11 18:35:34 +02:00
iro_daemon v0.6 2026-07-11 17:41:37 +02:00
irsdk_1_20 init 2026-07-05 00:35:58 +02:00
tests v0.6 2026-07-11 17:41:37 +02:00
.dockerignore Add Dockerfile and Forgejo Actions workflow for fuel server image 2026-07-11 18:17:02 +02:00
.gitignore Add .idea/ to gitignore and untrack IDE files 2026-07-11 22:33:14 +02:00
Dockerfile Add Dockerfile and Forgejo Actions workflow for fuel server image 2026-07-11 18:17:02 +02:00
fuel_server.py Fix: use unique connection IDs instead of TCP addr tuple to distinguish daemons 2026-07-11 22:32:03 +02:00
iro.py Fix multi-daemon support: increase Channels queue maxsize from 1 to 200 in Server 2026-07-11 23:02:00 +02:00
iro_protocol.py v0.6 2026-07-11 17:41:37 +02:00
PROTOCOL.md v0.2a 2026-07-06 03:56:16 +02:00
README.md Let AI add about a million tests... 2026-07-06 19:03:01 +02:00
server_test.py v0.4 2026-07-06 20:06:04 +02:00
web_test_client.py v0.6 2026-07-11 17:41:37 +02:00

iRO - iRacing Telemetry Daemon & Protocol

iRO is a Windows system-tray daemon that reads live telemetry from iRacing shared memory and streams it over TCP to remote applications. It also exposes a local endpoint for unfiltered dashboard-style access.

How It Works

┌──────────┐     shared memory      ┌──────────────┐     TCP (42001)     ┌──────────────┐
│ iRacing  │ ────────────────────── │  iro_daemon  │ ─────────────────►  │  Your App    │
│  (sim)   │   Local\IRSDKMemMap*   │  (bridge)    │   filtered vars    │  (server)    │
└──────────┘                        │              │   + YAML sections  └──────────────┘
                                    │              │
                                    │  TCP (42002) │     ┌──────────────┐
                                    │ ◄─────────── │     │  Dashboard   │
                                    │  unfiltered  │     │  (browser)   │
                                    │  all vars    │     └──────────────┘
                                    └──────────────┘

Route A — Remote server (port 42001): The daemon connects outbound as a TCP client. Your application listens as a TCP server. You send a filter list of variable names you care about; the daemon sends back only those variables at ~60 Hz as compressed float32 frames. You can also request specific YAML sections from the session info (track, drivers, weather, etc.) instead of the full ~100 KB blob.

Route B — Local clients (port 42002): The daemon listens on loopback as a TCP server. Connect and receive every iRacing variable, unfiltered, plus the full session YAML. The reference dashboard (web_test_client.py) uses this.

Build

Real daemon

cd iro_daemon
build.bat          → x64\Release\iro_daemon.exe

Test daemon (SDK stub)

cd tests
build_test_daemon.bat    → build\iro_daemon_test.exe

The test daemon links against irsdk_stub.cpp instead of the real irsdk_utils.cpp. The stub fakes 27 telemetry variables and session YAML so no iRacing installation is needed.

Run

iro_daemon.exe [server_host] [server_port] [local_port]

# defaults:
iro_daemon.exe                           → 127.0.0.1:42001  :42002
iro_daemon.exe 192.168.1.5 42001 42002   → explicit
  • server_host:server_port — where the daemon connects as a TCP client
  • local_port — where the daemon listens for local dashboard connections

The daemon runs silently in the system tray. Right-click → Exit to stop.

Protocol

All communication uses raw TCP with little-endian binary messages. Five message types:

ID Name Direction Purpose
0x01 MSG_FILTER_LIST Server → Daemon Subscribe to specific telemetry variables
0x02 MSG_TELEMETRY Daemon → Server 60 Hz telemetry frame (~7 + 4N bytes)
0x03 MSG_SESSION_INFO Daemon → Local only Full session YAML (port 42002 only)
0x04 MSG_SESSION_FILTER Server → Daemon Request specific YAML sections
0x05 MSG_SESSION_DATA Daemon → Server Filtered YAML sections

Full spec is in PROTOCOL.md. The reference implementations in server_test.py and web_test_client.py show working Python send/receive for every message type.

Test Suite

python -m tests.run_tests                 # all 50 tests (~45 s)
python -m tests.run_tests --protocol      # protocol-only, no daemon needed

The suite covers:

  • Protocol (24 tests) — every message type roundtrip, little-endian byte order, truncation detection
  • Remote interface (8 tests) — daemon connects to server, accepts filter subscriptions, disconnects/reconnects, retries when server is down
  • Local interface (12 tests) — client receives init messages, telemetry frames, session YAML; multiple concurrent clients; tick monotonicity; value sanity checks
  • Connection lifecycle (6 tests) — clean start/stop, port release, garbage data resilience, rapid disconnect cycles, loopback-only binding

All daemon tests use the C++ SDK stub — no iRacing required.

Reference Scripts

Script Role
server_test.py TCP server on port 42001. Sends filter lists, receives & logs telemetry and session data.
web_test_client.py TCP client on port 42002. Serves a real-time HTML dashboard at http://127.0.0.1:8080 via Server-Sent Events.
python server_test.py --port 42001 --filters Speed,RPM,Gear
python web_test_client.py --daemon-port 42002 --http-port 8080