A real-time CAN bus telemetry server: replays a CAN log at (a multiple of) real speed, decodes signals against a DBC as they "arrive," and streams them over a WebSocket to a live-updating browser dashboard. Built as a companion to can-diagnostic-assistant -- that project answers natural-language questions about a completed log after the fact; this one is about watching a vehicle's signals live as they happen, the way a real telemetry dashboard would during a competition or field test.
No LLM involved here -- this is a backend/systems project: async I/O, WebSockets, real-time data pipelines, and a from-scratch frontend.
Team Anveshak (and most rover/EV competitions) need to watch live signals during a run -- motor temps, battery state, fault codes -- without staring at raw hex in a terminal. This is a from-scratch version of that kind of base-station telemetry view, built against a synthetic demo dataset so it's fully runnable without any proprietary hardware logs.
CAN log (.log, candump format) + DBC file
│
▼
log_source.py -- parses raw (timestamp, frame_id, data) tuples
▼
replayer.py -- async generator: re-plays frames respecting original
│ inter-frame timing (scaled by a speed multiplier),
│ with wall-clock drift correction for long replays
▼
decoder.py -- decodes each frame against the DBC as it's replayed
▼
server.py -- FastAPI WebSocket endpoint streams decoded JSON events
│ to the browser in real time
▼
static/index.html -- Chart.js dashboard: live-updating signal panels,
a rolling time window, and a fault banner that
lights up the instant a DTC event streams in
A REST endpoint (/api/signals) exposes the DBC's signal layout so the
frontend doesn't hardcode signal names -- swap in a different DBC and the
metadata endpoint reflects it automatically (the chart panels are
currently hand-grouped in index.html for visual layout, but the available
signal list itself is fully dynamic).
pip install -r requirements.txtuvicorn src.server:app --reloadThen open http://127.0.0.1:8000 in a browser, pick a replay speed, and click "Start Replay."
Uses the same synthetic fault scenario as can-diagnostic-assistant: ~60
seconds of driving where sustained high throttle causes the motor controller
to overheat, trips a P0C31_CONTROLLER_OVERTEMP DTC at t≈41.6s, and the
drivetrain enters a derate state while cooling. Watch the Motor & Controller
Temp panel climb steadily, then the fault banner fire right as it crosses
the thermal limit.
Set two environment variables before starting the server:
export CAN_DBC_PATH=/path/to/your.dbc
export CAN_LOG_PATH=/path/to/your.log
uvicorn src.server:app --reloadLog format must be candump -L style text lines:
(1719500000.000000) can0 100#240C35034F550100
Each browser connection currently gets its own independent replay starting from the beginning of the log -- opening two tabs starts two separate playbacks rather than both watching one shared live feed. That's a deliberate simplification for a single-vehicle demo/portfolio project. A production telemetry system serving an actual live CAN bus to multiple viewers would more likely run one replay/capture loop and broadcast its events to all connected WebSocket clients via a shared queue or pub/sub -- worth mentioning if asked about productionizing this further.
pytest tests/Covers the decoder and replayer layer, including a timing test that asserts the replay actually respects the speed multiplier against wall-clock time (catches regressions like accidentally reverting to a blocking, no-sleep replay).
The replayer schedules each frame against an absolute wall-clock target rather
than sleeping for the gap since the previous frame, so timing error does not
pile up over a long replay. scripts/bench_replay.py measures how late each
frame is emitted relative to the log's original timing:
python scripts/bench_replay.py --speed 1Results on the 60-second demo log (4,805 frames, Linux, Python 3.13):
| Speed | Throughput | p50 jitter | p95 jitter | p99 jitter | Drift at end |
|---|---|---|---|---|---|
| 1x | 80 frames/s | 0.76 ms | 1.23 ms | 1.48 ms | 0.97 ms |
| 10x | 800 frames/s | 0.62 ms | 1.08 ms | 1.15 ms | 0.81 ms |
The end-of-replay offset stays close to the median, so drift is not accumulating: frame 4,805 is as on-time as frame 100.
dbc/ DBC file defining the CAN bus signal layout
data/ Synthetic log generator + generated demo log
src/
log_source.py Raw candump-log line parsing
decoder.py DBC-based frame decoding
replayer.py Async, real-time-paced log replay
server.py FastAPI app: WebSocket stream + REST metadata endpoint
static/
index.html Chart.js live dashboard frontend
scripts/
bench_replay.py Replay timing benchmark (jitter and drift)
tests/ pytest suite for decoder + replayer