Skip to content

Repository files navigation

Pitwall — On-Device AI Racing Coach

Real-time, fully on-device coaching for solo track-day drivers. Ingests CAN telemetry over USB from the car (via an AiM MXP dash logger), persists to SQLite/DuckDB, and serves a Vue 3 PWA over HTTP. Built to run on a rooted Pixel 10 with Termux — no cloud round-trips in the coaching loop.

The May 23, 2026 Sonoma Raceway field test is the load-bearing milestone.

┌──────────────────────── Pixel 10 (in cabin) ─────────────────────────┐
│   CANable 2.0 (SLCAN @ 1 Mbit/s) ─► Termux ─► Python bridge :8765    │
│                                                │                      │
│                                                ▼                      │
│                                       SQLite / DuckDB                 │
│                                                │                      │
│                                                ▼                      │
│   Chrome PWA  ◄──  SSE / HTTP  ◄── Flask bridge ── LocalLLM :8080     │
│                                       (OpenAI-compat, Gemma 4 E2B)    │
└───────────────────────────────────────────────────────────────────────┘

When no CAN cable is plugged in, the bridge can re-publish a recorded session via POST /session/replay/start — same SSE frames, no live bus.

Quick start

Laptop (development)

# Backend
pip install -e .[can,ops,dev]
python -m pitwall --track data/tracks/sonoma.json --port 8765

# Frontend
cd src/pwa
npm install
npm run dev          # vite on :5173, proxies /api → :8765

Phone (Pixel 10 + Termux)

The deploy/phone/ ladder is the repeatable rooted-Pixel deployment path. See deploy/phone/README.md for the full flow.

deploy/phone/00-check.sh           # adb + Termux + root sanity
deploy/phone/10-termux-packages.sh # pkg install python pyarrow numpy …
deploy/phone/20-stage-repo.sh      # rsync src + data → phone
deploy/phone/30-python-deps.sh     # uv venv + pip install
deploy/phone/40-stage-recording.sh # push a SQLite recording for replay
deploy/phone/50-build-pwa.sh       # vite build → dist/
deploy/phone/60-forward-ports.sh   # adb reverse 8765
SIM=1 SIM_LAP_SECONDS=60 deploy/phone/70-start-bridge.sh   # synthetic
deploy/phone/80-open-pwa.sh        # `am start` Chrome at the PWA URL
deploy/phone/99-stop.sh            # kill bridge
deploy/phone/status.sh             # /health + recent logs

Bridge mode is auto-detected: live CAN on /dev/ttyACM0 if present; otherwise SIM=1 enables the in-process AiM MXP synthetic generator, or POST /session/replay/start republishes a recorded session.

Architecture

Three-tier, all local:

Tier What Where
Bridge Flask + SQLite/DuckDB + pyarrow src/pitwall/
Frontend Vue 3 + Pinia + DuckDB-wasm + Leaflet + Chart.js src/pwa/
LLM LocalLLM Android APK (OpenAI-compat HTTP) sibling APK on phone, http://localhost:8080/v1

ADK paddock agents (/coach/ask, /coach/agents) are opt-in — they require google-adk to be importable. On the phone the dependency is currently not installable (DNS in adb su exec context is unreliable, and manylinux wheels don't match Termux's android wheel tag), so these endpoints honestly return available: false. The bridge falls back to LocalLLM for brief/debrief via litert_coach.py.

See docs/architecture.md for the as-shipped topology and docs/api.md for every endpoint.

Key endpoints

Method Path What
GET /health engine + coach + CAN status
GET /cars every YAML in data/cars/; the loaded one is flagged
GET /cues/stream SSE: live coaching cues
GET /telemetry/stream SSE: live telemetry frames
POST /session/replay/start re-publish a recorded session to telemetry_bus
POST /session/replay/stop stop the replay thread
GET /session/replay/status running / idle + frame index
GET /session/<sid>/laps lap envelope (GPS / distance / stint detection)
GET /session/<sid>/export.parquet parquet export for DuckDB-wasm (4 tables)
POST /coach/brief, /coach/debrief LocalLLM-backed narratives
GET /coach/traces poll ADK agent traces (returns available: false when ADK absent)
GET /leaderboard?track=&limit= best laps grouped by driver/car/track

Repo layout

pitwall/
├── src/pitwall/                  # Flask bridge (Python 3.12)
│   ├── features/
│   │   ├── bp_core.py            # /health, /analyze
│   │   ├── bp_cars.py            # /cars
│   │   ├── bp_leaderboard.py     # /leaderboard
│   │   ├── bp_diagnostics.py     # /diagnostics/*
│   │   ├── session/
│   │   │   ├── bp_session.py     # CRUD + parquet export + /session/<sid>/laps
│   │   │   ├── bp_analysis.py    # lap-time table, sector splits, pedal stats …
│   │   │   └── bp_replay.py      # /session/replay/{start,stop,status}
│   │   ├── coaching/             # bp_coaching.py + ADK orchestrator + litert_coach
│   │   ├── telemetry/            # CAN reader, ADR-015 sink, signal registry
│   │   ├── track/                # markers, weather, elevation, evolution
│   │   └── realtime/             # SSE bus + spectator tokens
│   └── __init__.py               # register_blueprints + create_app
├── src/pwa/                      # Vue 3 PWA (27 pages)
│   ├── src/pages/                # analysis-hub, on-track-hud, pit-stall, …
│   ├── src/entities/             # Pinia stores
│   ├── src/widgets/              # bridge-offline, install-prompt, fullscreen-toggle, coach-voice-button
│   └── public/icons/             # PWA icons (192/512/maskable + apple-touch)
├── data/                         # cars/*.yaml, dbc/, tracks/, registry/, formulas/
├── deploy/phone/                 # 00-99 + _common.sh + status.sh
├── docs/                         # api.md, architecture.md, stack.md, hardware.md, …
└── tests/                        # pytest

Hardware

Field-test rig:

  • Phone: rooted Pixel 10 (codename frankel) with Termux + KernelSU
  • USB-CAN adapter: Jhoinrch CANable 2.0 RH-02 PRO, SLCAN @ 1 Mbit/s over USB-C OTG
  • Dash logger: AiM MXP SmartyCam Enhanced CAN Protocol v3.0 (20 frames, 66 channels)
  • Car: 2003 BMW E46 M3 with MSS54HP DME

See docs/hardware.md for the full wiring + signal map.

Tests

python -m pytest tests/ -q

Documentation

Architecture decisions live under docs/adr/ and are frozen historical records — refer but do not edit when documenting current behaviour.

License

TBD before any public release.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages