DEV Community

Atharv Sanjeev Kumar
Atharv Sanjeev Kumar

Posted on Edited on

I Tried Two Racing Games Before ETS2 Gave Me Real Telemetry

I went into the SigNoz hackathon wanting to build something cool and unique. Three games and a fair number of dead ends later, I had a fleet-observability pipeline running on a truck simulator. Along the way I hit two things about OpenTelemetry and SigNoz that the docs don't mention until you've already built around them.

Where this started

I found this hackathon through the youtube channel of Kunal Kushwaha. I explored the hackathon page and the GitHub page given for ideas, one issue on the project page I really liked: "Monitor anything weird," a Raspberry Pi homelab, a 3D printer, a coffee machine, or an IoT fleet, wired up with real observability instead of a toy dashboard. I wanted to do the IoT fleet one. I didn't have access to a fleet, though. I had curiosity, and when there's a will, there's a way.

I tried Forza Horizon 4 first, since it has a built-in "Data Out" feature that's supposed to stream telemetry over UDP to any address and port you configure. In practice, no packets ever showed up on my listener. I never fully pinned down whether the game wasn't sending anything or whether something on my network was swallowing them before they arrived. I tried the usual suspects: different ports, disabling the firewall temporarily, listening on 0.0.0.0 instead of a specific interface. Nothing got through. Debugging a feed with zero visibility into which end of the pipe was broken stopped being a good use of hackathon time, so I moved on.

Next I tried Assetto Corsa Competizione, since it exposes a proper Windows shared-memory telemetry block, the same kind of interface real racing telemetry tools use. That part worked. I could pull speed, tire pressure, and G-forces straight out of the game's memory in real time. What didn't work was me. ACC's driving model is built for a wheel and pedals, and doing anything more than a few clean laps on keyboard was rough enough that I couldn't reliably generate the sustained, varied driving data I actually needed.

Euro Truck Simulator 2 turned out to be the answer for a boring reason: it's forgiving to drive. Cruise control, no wheel-to-wheel racing, long stretches of highway. It was also technically easier. Instead of reading a Windows-only shared-memory block, Funbit's ETS2 provides a telemetry SDK that helps access the entire truck state as a REST API returning JSON, a much smaller barrier between "game" and "OpenTelemetry SDK" than parsing raw memory structs.

Is fleet monitoring actually a real problem, or just a convenient theme?

Before committing, I wanted to check whether fleet observability was a real operational pain point or just a plausible-sounding pitch. A few things I found:

  • Driver safety monitoring (speeding, harsh braking, fatigue) is repeatedly named as a top fleet management challenge, since most fleets only find out about dangerous driving after an incident, not during one. (Teletrac Navman)

  • Wasted mileage, the gap between planned route distance and what a driver actually drives, directly increases fuel spend, and route-planning vendors have built entire product lines around closing that gap. (FleetGS)

  • Unplanned downtime from deferred maintenance is one of the costliest failure modes in commercial trucking, which is the whole argument for continuous component-wear monitoring instead of scheduled-only maintenance. (OxMaint)

That was enough for me. A truck sim obviously isn't a real fleet, but the shape of the problem (safety events, route efficiency, predictive maintenance) is real, and I could build something that speaks to all three.

What I built

The architecture is a Python daemon polling the ETS2 telemetry API twice a second, pushing OpenTelemetry metrics, traces, and logs to a self-hosted SigNoz instance. Metrics cover continuous gauges: speed, RPM, fuel, fluid levels, component wear. Logs cover anything discrete: crashes, harsh braking, speed-limit violations, low fuel, jackknife risk. Traces are the interesting part. Each delivery job is a distributed trace: attaching a trailer starts a root span, dropping it off ends the span with computed attributes like fuel used and wasted mileage, and any safety event during the job gets injected as a span event on the timeline. A trace for one delivery shows exactly where things went wrong, not just that the job happened.

ETS2 exposes the truck's heading and the trailer's heading as separate values. The angle between them tells you how far the trailer is swinging relative to the cab, a real physical jackknife-risk signal computed from two numbers most people wouldn't think to compare:

def jackknife_sway_angle(truck_heading, trailer_heading):
    # headings are 0.0-1.0 representing 0-360 degrees
    diff = abs(truck_heading - trailer_heading)
    return min(diff, 1 - diff)  # handle wraparound

sway = jackknife_sway_angle(truck.placement.heading, trailer.placement.heading)
if sway > 0.04:  # roughly 15 degrees
    emit_event("fleet.safety.jackknife_risk", sway_angle=sway)
Enter fullscreen mode Exit fullscreen mode

The second is ferry handling. Some ETS2 routes cross water on a ferry or through the Eurotunnel, which teleports the truck's position instantly. My first version of the speeding and mileage logic saw a 50km position jump in half a second and had a small crisis about it: false speeding alerts, garbage mileage numbers. The fix was to detect displacement above a physically impossible threshold and treat it as a transport event instead of a driving event, logging it cleanly rather than trying to explain it away as bad driving.

What I learned about SigNoz

This is the part I think is actually useful to other people building something similar, not just a list of things that went wrong for me specifically.

Metrics have a 60-second aggregation floor. I was exporting metrics every second, expecting a smooth live dashboard, and the panels stayed choppy no matter what I changed on my end. It turns out SigNoz's dashboard panels currently have a fixed 60-second step interval for metrics. That's a known, open limitation, not something misconfigured on my side. The fix was moving my highest-frequency data, a telemetry_tick log emitted every few seconds, into Logs instead of Metrics, since log-based panels aren't bound by that floor. If you need sub-minute granularity in SigNoz today, logs are the more reliable path, not metrics.

There's also no map panel. SigNoz doesn't have a geospatial visualization type yet; it's an open feature request on their GitHub, not something I missed in the docs. Anything involving the truck's actual position has to live outside SigNoz, in a separate view.

The Logs Explorer did more work than the dashboards during actual debugging. When a threshold felt wrong (was 15Β° too sensitive for jackknife risk, or not sensitive enough?), I could filter directly to event.name = "fleet.safety.jackknife_risk" and scroll through the real trigger events with their attributes attached, instead of squinting at a chart and guessing. It's the same workflow SRE teams use to go from "the graph looks off" to "here's the exact record that explains it," just pointed at truck physics instead of request logs.

A state-machine bug took longer to find than it should have. The telemetry API keeps the source and destination city fields populated for a moment after a delivery is actually turned in, which fooled my job-tracking logic into thinking the delivery was still active. The fix was watching job.income instead of the city fields. It drops to zero the instant a delivery is completed, a cleaner signal than any field that sounds like it should mean "job done."

The most useful accident of the whole project happened partway through testing, when my SigNoz instance got wiped, taking every dashboard and alert rule with it, because I'd built them by clicking around in the UI instead of writing anything down. I got lucky. I'd kept the ClickHouse SQL for each panel in a scratch file while iterating, so I rebuilt everything in under five minutes. If I hadn't, that's hours of work gone with zero warning. Anything you build by hand in someone else's UI is disposable until it's checked into your own repo.

What I'd tell myself at the start

Pick the vehicle or game with the most forgiving controls before the one with the most realistic physics. You need volume and variety of clean data more than you need simulation fidelity. And when a platform's docs don't mention a limit, that's not the same as the limit not existing.

Try it yourself

Code, dashboard JSON, and setup steps are in the repo. Built for "Agents of SigNoz" hackathon, Track 3, Build Your Own.

Top comments (0)