6bee9c0d7f
Foundation for the PySide6 + pyqtgraph Windows GUI, shared with the terminal
tool. Pure data/IO -- no Qt, no curses.
obdcore/
link.py ElmLink -- ELM327 serial (Mode-01/22, ATRV, DTC read/clear)
mock.py MockLink -- synthetic crank for tests + GUI dev (no truck)
registry.py PidRegistry (verified Ford 6.0 PIDs + confidence) + DtcDatabase
scheduler.py PollScheduler -- prioritized round-robin polling, dead-PID park,
derived channels; tick() is fake-clock test-drivable
store.py TimeSeriesStore (ring buffers + min/max) + CsvRecorder/replay
Design centers on the ELM327 bandwidth limit (~7-15 reads/sec): the active
view subscribes PIDs at chosen rates; acquisition runs off the UI thread;
the GUI only reads the store. FICM_M (09D0) promoted to verified after the
2026-06-30 on-truck crank read (48.0V, intermittent).
tests/test_obdcore.py: decoders vs real truck bytes, crank ramp + peak,
derived BOOST, dead-PID park/revive, record/replay roundtrip -- all pass.
ARCHITECTURE.md: layers, data model, GUI plan, 6.0 stock-PID limits
(no EGT/oil-PSI), feature backlog, P0-P5 roadmap.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs
135 lines
6.8 KiB
Markdown
135 lines
6.8 KiB
Markdown
# ford-obd — Architecture & Roadmap
|
||
|
||
Plan for growing the terminal `obd_reader.py` into a graphical Windows scan
|
||
tool for the 6.0L Power Stroke, on a shared headless core.
|
||
|
||
## Vision
|
||
|
||
A desktop app with:
|
||
- a **PID browser** (searchable list, live values) on the side,
|
||
- a flexible **graph workspace** — overlay many metrics on one plot, split into
|
||
a grid of single-metric plots, or show gauges (one metric each),
|
||
- purpose-built **perspectives**: Cranking, Driving, Diagnostics (DTCs), Logging,
|
||
- a **Ford DTC database** behind the codes page,
|
||
- **session record + playback** so intermittent faults can be reviewed offline.
|
||
|
||
## The constraint that shapes everything: ELM327 bandwidth
|
||
|
||
The ELM327 is a one-request-at-a-time link: each Mode-22 round-trip is
|
||
~40–150 ms over the CH340, so total throughput is **~7–15 PID reads/sec**,
|
||
shared across the whole UI. Consequences baked into the design:
|
||
|
||
- A **prioritized polling scheduler** owns the link. The active view subscribes
|
||
the PIDs it needs at the rates it needs (cranking = ICP fast, ignore the rest).
|
||
- Acquisition runs **off the UI thread**; the GUI only reads the store.
|
||
- Per-PID rates and dead-PID parking keep the sample rate from collapsing.
|
||
- A faster adapter (**OBDLink SX/MX+**, STN chip — batched PIDs, faster
|
||
protocols) multiplies throughput; the link layer is abstracted so either works.
|
||
|
||
## Layers
|
||
|
||
```
|
||
+------------------- GUI (PySide6 + pyqtgraph) -------------------+
|
||
| PID browser | Graph workspace | Perspectives | DTC page | Log |
|
||
+--------------------------------|-------------------------------+
|
||
reads only | (Qt thread)
|
||
+--------------------------------v-------------------------------+
|
||
| obdcore (headless) |
|
||
| PollScheduler --reads--> ElmLink / MockLink --serial--> PCM |
|
||
| | pushes samples |
|
||
| v |
|
||
| TimeSeriesStore <--- CsvRecorder / replay_csv |
|
||
| PidRegistry (verified PIDs) DtcDatabase |
|
||
+-----------------------------------------------------------------+
|
||
```
|
||
|
||
`obdcore` is pure data/IO — no Qt, no curses — so it's shared by the terminal
|
||
tool, the GUI, and tests.
|
||
|
||
### obdcore modules (built, tested)
|
||
|
||
| Module | Responsibility |
|
||
|---|---|
|
||
| `link.py` `ElmLink` | ELM327 serial: init, protocol negotiate, Mode-01/22 reads, ATRV, DTC read/clear. Returns raw bytes. |
|
||
| `mock.py` `MockLink` | Synthetic crank (ICP ramp, FICM ~48V, batt sag) — same interface; powers tests + GUI dev with no truck. |
|
||
| `registry.py` `PidRegistry` | Verified Ford 6.0 PID table (corrected addresses + scaling + confidence) and subscription presets. `DtcDatabase` seeds the code DB. |
|
||
| `scheduler.py` `PollScheduler` | Prioritized round-robin polling; per-PID Hz; derived channels; dead-PID park/revive. `tick()` is test-drivable with a fake clock. |
|
||
| `store.py` `TimeSeriesStore` | Per-PID ring buffers + min/max; `CsvRecorder` (long format) + `replay_csv` for record/playback. |
|
||
|
||
Tests: `tests/test_obdcore.py` — decoders vs real truck bytes, crank ramp +
|
||
peak capture, derived BOOST, dead-PID parking, record/replay round-trip.
|
||
|
||
## Data model
|
||
|
||
- **Pid**: key, name, mode (`01`/`22`/`atrv`/`derived`), pid hex, nbytes,
|
||
decode fn, unit, group, vmin/vmax, **confidence** (`verified|doc|tentative`),
|
||
deps (for derived), notes.
|
||
- **Channel**: rolling `(t, value)` + session min/max.
|
||
- **Derived/virtual channels**: e.g. `BOOST = MAP − BARO`; later `ICP_error =
|
||
ICP_DES − ICP`, `FICM_sag = FICM_M − FICM_V`.
|
||
|
||
## GUI plan (PySide6 + pyqtgraph)
|
||
|
||
- **Left dock**: `QTreeView` PID browser grouped by system (fuel/ficm/air/…),
|
||
live value + confidence badge, checkbox to add to the focused panel.
|
||
- **Central workspace**: dockable/tabbed panels. Panel types:
|
||
- *Overlay plot* — many PIDs, multiple Y-axes (pyqtgraph `ViewBox` linking).
|
||
- *Split grid* — one plot per PID.
|
||
- *Gauge* — radial/linear single metric with warn/crit bands.
|
||
Drag a PID from the browser onto a panel to add it.
|
||
- **Perspectives** (saved layouts):
|
||
- **Cranking** — ICP big readout + 500-psi firing line + peak-hold + trace
|
||
(port of terminal `--crank`).
|
||
- **Driving** — boost (MGP), EOT, ECT, EBP, load, RPM, IPR, FICM, trans temp.
|
||
- **Diagnostics** — DTC read/clear (guarded) + freeze frame, joined to the
|
||
Ford DTC DB (description, causes, no-start relevance).
|
||
- **Logging/Playback** — record a session; scrub/replay through the graphs.
|
||
- **Settings** — COM port, baud, protocol, per-PID rates, units (psi/°F vs kPa/°C),
|
||
dark/night theme.
|
||
- **Bottom status** — adapter, protocol, port voltage, dropped-response rate.
|
||
|
||
### Honest 6.0 data-stream limits
|
||
|
||
Two commonly-wanted gauges are **not in the stock 6.0 PCM stream** from the OBD
|
||
port and need aftermarket sensors:
|
||
- **Engine oil PRESSURE** — only ICP (injection oil) + EOT (oil temp) exist; lube
|
||
pressure is an idiot-light switch, not a PID.
|
||
- **EGT** — only EBP (exhaust *back pressure*) exists; exhaust *gas temperature*
|
||
is an add-on pyrometer.
|
||
|
||
Plan: present what the PCM exposes; design an **aux-input** path so external
|
||
sensors (e.g. a serial/analog EGT/oil-PSI module) can feed extra channels later.
|
||
|
||
## Additional features (backlog)
|
||
|
||
- Session record + **playback/scrub** (highest value; foundation already in `store`).
|
||
- Bi-directional tests: **KOEO/KOER self-tests, injector buzz, cylinder
|
||
contribution/balance**.
|
||
- **Alarms** + min/max hold per PID (EOT>230°F, ICP<500 cranking).
|
||
- Timeline **annotations** ("started cranking", "stabbed throttle").
|
||
- **Computed channels** (ICP error, FICM sag).
|
||
- **Multi-vehicle profiles** + per-truck DTC history.
|
||
- **Export/report** (CSV/PDF, graph screenshots, one-click "share state").
|
||
- **PID discovery scan** in GUI (the brute-scan, auto-add hits).
|
||
- Units toggle, night theme, big-touch cab mode.
|
||
|
||
## Roadmap
|
||
|
||
- **P0 — core (this commit):** `obdcore` package + tests + this doc. *Next:*
|
||
migrate `obd_reader.py` to import `obdcore` (remove the duplicated ELM/PID
|
||
logic) so terminal + GUI share one source of truth.
|
||
- **P1 — GUI shell:** PySide6 window, connect dialog, PID browser, one overlay
|
||
plot fed by the scheduler/store. Validate against `MockLink` first.
|
||
- **P2 — panels + perspectives:** split plots, gauges, Cranking + Driving views.
|
||
- **P3 — diagnostics:** DTC read/clear page + Ford DTC DB (built by the
|
||
cross-verified workflow, same method as the PID hunt).
|
||
- **P4 — record/playback + alarms + computed channels.**
|
||
- **P5 — packaging:** PyInstaller one-file `.exe` (+ CH340 driver note),
|
||
optional code-signing; OBDLink/STN fast-path support.
|
||
|
||
## Dependencies
|
||
|
||
- Runtime (core): `pyserial`.
|
||
- GUI: `PySide6`, `pyqtgraph`, `numpy`.
|
||
- Dev: `pytest`, `pyinstaller`.
|