Make app vehicle-agnostic: JSON vehicle profiles + menu bar
Vehicle data is now DATA, not code. PIDs/scaling/DTCs/presets live in profiles/*.json; the app loads them at runtime, so it works across vehicles and others can contribute profiles (open source). Core: - obdcore/formula.py: safe AST evaluator for scaling formulas (A/B/... byte vars, Torque/FORScan convention). Only arithmetic/bitwise + min/max/abs/ round/int/float; names/attrs/arbitrary calls rejected at load -> a community profile CANNOT execute code. - obdcore/profile.py: load/save/list profiles; compiles each formula into a decode callable. registry.py now profile-backed (PidRegistry/DtcDatabase take a Profile); hardcoded Ford table removed. - store.py: clear()/snapshot()/export_csv() for capture management. Profiles: - profiles/ford-6.0-powerstroke.json (27 PIDs, verified formulas, DTCs) - profiles/generic-obd2.json (standard SAE Mode-01 base, any vehicle) - profiles/README.md (schema + formula language + contributing) GUI: - Menu bar: File (new/record/export/replay capture, quit), Profile (switch/ load/import/reload/edit-JSON/export, live profile list), View (Graph/Table views, gauges P2, toggle PID dock, normalize, light/dark theme), Help (about/confidence legend/profile info). - PID browser + presets rebuild on profile switch; added Table view; raw-JSON profile editor dialog (validates schema+formulas before saving). Tests: profiles load+compile, formula sandbox rejects hostile input, decoders still match real truck bytes, crank/derived/dead-PID/replay -- all pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# Vehicle Profiles
|
||||
|
||||
Each `*.json` file here is a **vehicle profile** — pure data that makes the
|
||||
ford-obd app vehicle-agnostic. A profile defines a vehicle's PIDs (with safe
|
||||
scaling formulas), DTC meanings, and named presets. Load one in the app via
|
||||
**Profile → Load**, or drop a new file in this folder and it appears in the list.
|
||||
|
||||
**Contributions welcome** — add a profile for your vehicle and open a PR.
|
||||
|
||||
## Current profiles
|
||||
|
||||
| File | Vehicle | Notes |
|
||||
|---|---|---|
|
||||
| `ford-6.0-powerstroke.json` | Ford 6.0L Power Stroke (2003–2007) | Verified Mode-22 PIDs (ICP, FICM, EBP, MAP/BARO, EOT, …) + DTCs |
|
||||
| `generic-obd2.json` | Any OBD-II vehicle (1996+) | Standard SAE Mode-01 PIDs only — a base to fork from |
|
||||
|
||||
## Schema (`schema: 1`)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"schema": 1,
|
||||
"meta": {
|
||||
"name": "Ford 6.0L Power Stroke", // shown in the Profile menu
|
||||
"make": "Ford", "model": "...", "years": "2003-2007",
|
||||
"engine": "6.0L Power Stroke diesel",
|
||||
"author": "you", "version": "1.0.0",
|
||||
"protocol": "auto", // ELM ATSP target, or "auto"
|
||||
"notes": "provenance / confidence policy / caveats"
|
||||
},
|
||||
"presets": { "crank": ["ICP","FICM_M","BATT","RPM"], "...": [] },
|
||||
"pids": [ /* see below */ ],
|
||||
"dtcs": [ {"code":"P0087","desc":"...","system":"fuel","no_start":true,"causes":""} ]
|
||||
}
|
||||
```
|
||||
|
||||
### PID fields
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `key` | short unique id used in presets/derived (e.g. `ICP`) |
|
||||
| `name` | display name |
|
||||
| `mode` | `01` (generic SAE), `22` (manufacturer-enhanced), `atrv` (adapter pin voltage), `derived` (computed from other PIDs) |
|
||||
| `pid` | request id hex — `0C` (mode 01) or `1446` (mode 22) |
|
||||
| `nbytes` | expected data bytes in the response |
|
||||
| `formula` | scaling expression (see below) |
|
||||
| `round` | display rounding: omit = raw, `0` = integer, `2` = 2 dp |
|
||||
| `unit`, `group` | display unit; group = `fuel\|ficm\|air\|engine\|driveline\|power\|misc` |
|
||||
| `vmin`,`vmax` | range (used for gauges + the Normalize overlay) |
|
||||
| `confidence` | `verified` (multi-source / read on a real vehicle), `doc` (sourced, unconfirmed), `tentative` (single-source / disputed) |
|
||||
| `deps` | for `derived`: the PID keys the formula references |
|
||||
| `notes` | freeform; surfaced as a tooltip |
|
||||
|
||||
### Formula language
|
||||
|
||||
Arithmetic over **data-byte variables** `A, B, C, …` (byte 0, 1, 2, …) — the
|
||||
same convention as Torque/FORScan/ScanGauge:
|
||||
|
||||
```
|
||||
(A*256+B)*0.57 # 16-bit * scale (ICP psi)
|
||||
A-40 # 8-bit temp
|
||||
(A>>1)&1 # a status bit
|
||||
A//2 # integer divide (gear)
|
||||
```
|
||||
|
||||
For `derived` PIDs the variables are **other PID keys**: `"MAP - BARO"` with
|
||||
`"deps": ["MAP","BARO"]`.
|
||||
|
||||
Formulas are evaluated by a **safe AST evaluator** (`obdcore/formula.py`):
|
||||
only numbers, the declared variables, arithmetic/bitwise operators, and
|
||||
`min/max/abs/round/int/float` are allowed. Anything else (names, attribute
|
||||
access, arbitrary calls) is rejected at load — so a community profile **cannot
|
||||
execute code**.
|
||||
|
||||
## Caveats worth recording in `notes`
|
||||
|
||||
- Manufacturer-enhanced (`22`) PIDs vary by model year and PCM strategy.
|
||||
- Some signals aren't on the OBD stream at all (e.g. the 6.0 has no EGT or
|
||||
lube-oil-pressure PID — only ICP and EOT). Don't invent them.
|
||||
- Mark single-source numbers `tentative` and say so in `notes`.
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"meta": {
|
||||
"name": "Ford 6.0L Power Stroke",
|
||||
"make": "Ford",
|
||||
"model": "Super Duty / Excursion",
|
||||
"years": "2003-2007",
|
||||
"engine": "6.0L Power Stroke diesel",
|
||||
"author": "ford-obd project",
|
||||
"version": "1.1.0",
|
||||
"protocol": "auto",
|
||||
"notes": "PID addresses + scaling corrected/verified by the ford-60-pid-hunt workflow (2026-06-29) and on-truck reads (2026-06-30). confidence: verified = multi-source or read on a real 6.0; doc = corroborated in sources, not yet read on-vehicle; tentative = single-source / disputed scaling. ICP_DES (desired ICP) has no public Mode-22 DID -> FORScan-only, not included."
|
||||
},
|
||||
"presets": {
|
||||
"crank": ["ICP", "FICM_M", "BATT", "RPM"],
|
||||
"driving": ["BOOST", "VGT", "EOT", "ECT", "EBP", "LOAD", "RPM", "IPR", "BATT"],
|
||||
"vitals": ["ICP", "FICM_M", "FICM_L", "IPR", "BATT", "RPM", "ECT", "EOT", "IAT", "VPCM"]
|
||||
},
|
||||
"pids": [
|
||||
{"key": "ICP", "name": "Injection Control Pressure", "mode": "22", "pid": "1446", "nbytes": 2, "formula": "(A*256+B)*0.57", "round": 1, "unit": "psi", "group": "fuel", "vmin": 0, "vmax": 3500, "confidence": "verified", "notes": "need ~500+ psi to fire"},
|
||||
{"key": "ICP_V", "name": "ICP Sensor Voltage", "mode": "22", "pid": "16AD", "nbytes": 2, "formula": "(A*256+B)*0.000072", "round": 4, "unit": "V", "group": "fuel", "vmin": 0, "vmax": 5, "confidence": "tentative", "notes": "single-source"},
|
||||
{"key": "IPR", "name": "Injection Pressure Regulator", "mode": "22", "pid": "1434", "nbytes": 1, "formula": "A*13.53/35", "round": 1, "unit": "%", "group": "fuel", "vmin": 0, "vmax": 100, "confidence": "tentative", "notes": "KOEO ~14-15%, cranking ~30-40%"},
|
||||
{"key": "INJ_TIMING", "name": "Injection Timing", "mode": "22", "pid": "09CC", "nbytes": 2, "formula": "(A*256+B)*10/64", "round": 1, "unit": "degBTDC", "group": "fuel", "vmin": -10, "vmax": 30, "confidence": "tentative", "notes": "scaling disputed; using *10/64 (ScanGauge), not /10"},
|
||||
{"key": "FUEL_PUMP", "name": "Fuel Pump Duty (HFCM)", "mode": "22", "pid": "1672", "nbytes": 1, "formula": "A*100/128", "round": 1, "unit": "%", "group": "fuel", "vmin": 0, "vmax": 100, "confidence": "tentative", "notes": "sits ~100%, drops on high EOT"},
|
||||
{"key": "MFDES", "name": "Mass Fuel Desired", "mode": "22", "pid": "1411", "nbytes": 2, "formula": "A*256+B", "round": 0, "unit": "raw", "group": "fuel", "vmin": 0, "vmax": 65535, "confidence": "tentative", "notes": "~mg/stroke internal count; no verified GPH formula"},
|
||||
{"key": "FICM_M", "name": "FICM Main Power", "mode": "22", "pid": "09D0", "nbytes": 2, "formula": "(A*256+B)/256", "round": 1, "unit": "V", "group": "ficm", "vmin": 0, "vmax": 55, "confidence": "verified", "notes": "~48V; <45 suspect; reads intermittently while cranking"},
|
||||
{"key": "FICM_L", "name": "FICM Logic Power", "mode": "22", "pid": "09CF", "nbytes": 2, "formula": "(A*256+B)/256", "round": 1, "unit": "V", "group": "ficm", "vmin": 0, "vmax": 16, "confidence": "doc"},
|
||||
{"key": "FICM_V", "name": "FICM Vehicle Power", "mode": "22", "pid": "09CE", "nbytes": 2, "formula": "(A*256+B)/256", "round": 1, "unit": "V", "group": "ficm", "vmin": 0, "vmax": 16, "confidence": "doc"},
|
||||
{"key": "FICM_SYNC", "name": "FICM Sync", "mode": "22", "pid": "09CD", "nbytes": 1, "formula": "(A>>1)&1", "round": 0, "unit": "", "group": "ficm", "vmin": 0, "vmax": 1, "confidence": "doc", "notes": "1=in sync, 0=no sync"},
|
||||
{"key": "MAP", "name": "Manifold Absolute Pressure", "mode": "22", "pid": "1440", "nbytes": 2, "formula": "(A*256+B)*0.03625", "round": 2, "unit": "psia", "group": "air", "vmin": 0, "vmax": 60, "confidence": "verified"},
|
||||
{"key": "BARO", "name": "Barometric Pressure", "mode": "22", "pid": "1442", "nbytes": 2, "formula": "(A*256+B)*0.03625", "round": 2, "unit": "psia", "group": "air", "vmin": 0, "vmax": 20, "confidence": "verified"},
|
||||
{"key": "EBP", "name": "Exhaust Back Pressure", "mode": "22", "pid": "1445", "nbytes": 2, "formula": "(A*256+B)*0.03625", "round": 2, "unit": "psia", "group": "air", "vmin": 0, "vmax": 60, "confidence": "verified", "notes": "minus BARO = gauge"},
|
||||
{"key": "VGT", "name": "VGT Duty Cycle", "mode": "22", "pid": "096D", "nbytes": 2, "formula": "(A*256+B)*100/32767", "round": 1, "unit": "%", "group": "air", "vmin": 0, "vmax": 100, "confidence": "doc", "notes": "turbo vane duty"},
|
||||
{"key": "EOT", "name": "Engine Oil Temperature", "mode": "22", "pid": "1310", "nbytes": 2, "formula": "(A*256+B)/100-40", "round": 1, "unit": "C", "group": "engine", "vmin": -40, "vmax": 160, "confidence": "verified"},
|
||||
{"key": "FAN", "name": "Fan Speed", "mode": "22", "pid": "099F", "nbytes": 2, "formula": "(A*256+B)/4", "round": 0, "unit": "rpm", "group": "engine", "vmin": 0, "vmax": 4000, "confidence": "doc", "notes": "real ceiling ~3500"},
|
||||
{"key": "GEAR", "name": "Current Gear", "mode": "22", "pid": "11B3", "nbytes": 1, "formula": "A//2", "round": 0, "unit": "", "group": "driveline", "vmin": 0, "vmax": 6, "confidence": "verified"},
|
||||
{"key": "TSS", "name": "Trans Input Shaft Speed", "mode": "22", "pid": "11B4", "nbytes": 2, "formula": "(A*256+B)/4", "round": 0, "unit": "rpm", "group": "driveline", "vmin": 0, "vmax": 4000, "confidence": "verified"},
|
||||
{"key": "RPM", "name": "Engine RPM", "mode": "01", "pid": "0C", "nbytes": 2, "formula": "(A*256+B)/4", "round": 0, "unit": "rpm", "group": "engine", "vmin": 0, "vmax": 4000, "confidence": "verified"},
|
||||
{"key": "ECT", "name": "Engine Coolant Temp", "mode": "01", "pid": "05", "nbytes": 1, "formula": "A-40", "round": 0, "unit": "C", "group": "engine", "vmin": -40, "vmax": 160, "confidence": "verified"},
|
||||
{"key": "IAT", "name": "Intake Air Temp", "mode": "01", "pid": "0F", "nbytes": 1, "formula": "A-40", "round": 0, "unit": "C", "group": "air", "vmin": -40, "vmax": 160, "confidence": "verified"},
|
||||
{"key": "LOAD", "name": "Engine Load", "mode": "01", "pid": "04", "nbytes": 1, "formula": "A*100/255", "round": 0, "unit": "%", "group": "engine", "vmin": 0, "vmax": 100, "confidence": "verified"},
|
||||
{"key": "VPCM", "name": "Module Voltage", "mode": "01", "pid": "42", "nbytes": 2, "formula": "(A*256+B)/1000", "round": 2, "unit": "V", "group": "power", "vmin": 0, "vmax": 16, "confidence": "verified"},
|
||||
{"key": "VBAT", "name": "Battery (PCM)", "mode": "22", "pid": "1172", "nbytes": 1, "formula": "A/16", "round": 1, "unit": "V", "group": "power", "vmin": 0, "vmax": 16, "confidence": "tentative", "notes": "PCM-reported B+; distinct from ATRV port voltage"},
|
||||
{"key": "FUEL_LVL", "name": "Fuel Level", "mode": "22", "pid": "16C1", "nbytes": 2, "formula": "(A*256+B)*100/328", "round": 1, "unit": "%", "group": "misc", "vmin": 0, "vmax": 100, "confidence": "tentative", "notes": "UNCALIBRATED -- needs per-truck full/empty cal"},
|
||||
{"key": "BATT", "name": "Battery (OBD port)", "mode": "atrv", "unit": "V", "group": "power", "vmin": 0, "vmax": 16, "confidence": "verified", "notes": "ELM327 ATRV pin voltage"},
|
||||
{"key": "BOOST", "name": "Boost (MGP)", "mode": "derived", "formula": "MAP-BARO", "deps": ["MAP", "BARO"], "round": 2, "unit": "psi", "group": "air", "vmin": -5, "vmax": 40, "confidence": "verified", "notes": "MAP - BARO"}
|
||||
],
|
||||
"dtcs": [
|
||||
{"code": "P0087", "desc": "Fuel rail/system pressure too LOW", "system": "fuel", "no_start": true},
|
||||
{"code": "P0088", "desc": "Fuel rail/system pressure too HIGH", "system": "fuel"},
|
||||
{"code": "P0148", "desc": "Fuel delivery error (low pressure / HPOP / IPR)", "system": "fuel", "no_start": true},
|
||||
{"code": "P0335", "desc": "Crankshaft position (CKP) sensor circuit", "system": "engine", "no_start": true},
|
||||
{"code": "P0340", "desc": "Camshaft position (CMP) sensor circuit", "system": "engine", "no_start": true},
|
||||
{"code": "P0611", "desc": "FICM performance", "system": "ficm", "no_start": true},
|
||||
{"code": "P1316", "desc": "Injector circuit/FICM codes detected", "system": "ficm", "no_start": true},
|
||||
{"code": "P0606", "desc": "PCM processor fault", "system": "power", "no_start": true},
|
||||
{"code": "U0100", "desc": "Lost communication with PCM/ECM", "system": "network", "no_start": true},
|
||||
{"code": "P0670", "desc": "Glow plug control module circuit", "system": "engine"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"meta": {
|
||||
"name": "Generic OBD-II",
|
||||
"make": "Any",
|
||||
"model": "Any OBD-II vehicle (1996+)",
|
||||
"years": "1996+",
|
||||
"engine": "any",
|
||||
"author": "ford-obd project",
|
||||
"version": "1.0.0",
|
||||
"protocol": "auto",
|
||||
"notes": "Standard SAE J1979 Mode-01 PIDs only -- supported by essentially every OBD-II vehicle. Use as a base/starting point for a new vehicle profile, then add manufacturer-enhanced Mode-22 PIDs. Decodes are the SAE-standard formulas."
|
||||
},
|
||||
"presets": {
|
||||
"basic": ["RPM", "SPEED", "ECT", "IAT", "MAP", "THROTTLE", "LOAD", "BATT"]
|
||||
},
|
||||
"pids": [
|
||||
{"key": "RPM", "name": "Engine RPM", "mode": "01", "pid": "0C", "nbytes": 2, "formula": "(A*256+B)/4", "round": 0, "unit": "rpm", "group": "engine", "vmin": 0, "vmax": 8000, "confidence": "verified"},
|
||||
{"key": "SPEED", "name": "Vehicle Speed", "mode": "01", "pid": "0D", "nbytes": 1, "formula": "A", "round": 0, "unit": "km/h", "group": "driveline", "vmin": 0, "vmax": 255, "confidence": "verified"},
|
||||
{"key": "ECT", "name": "Engine Coolant Temp", "mode": "01", "pid": "05", "nbytes": 1, "formula": "A-40", "round": 0, "unit": "C", "group": "engine", "vmin": -40, "vmax": 215, "confidence": "verified"},
|
||||
{"key": "IAT", "name": "Intake Air Temp", "mode": "01", "pid": "0F", "nbytes": 1, "formula": "A-40", "round": 0, "unit": "C", "group": "air", "vmin": -40, "vmax": 215, "confidence": "verified"},
|
||||
{"key": "MAP", "name": "Intake Manifold Pressure", "mode": "01", "pid": "0B", "nbytes": 1, "formula": "A", "round": 0, "unit": "kPa", "group": "air", "vmin": 0, "vmax": 255, "confidence": "verified"},
|
||||
{"key": "MAF", "name": "Mass Air Flow", "mode": "01", "pid": "10", "nbytes": 2, "formula": "(A*256+B)/100", "round": 2, "unit": "g/s", "group": "air", "vmin": 0, "vmax": 655, "confidence": "verified"},
|
||||
{"key": "THROTTLE", "name": "Throttle Position", "mode": "01", "pid": "11", "nbytes": 1, "formula": "A*100/255", "round": 0, "unit": "%", "group": "engine", "vmin": 0, "vmax": 100, "confidence": "verified"},
|
||||
{"key": "LOAD", "name": "Calculated Load", "mode": "01", "pid": "04", "nbytes": 1, "formula": "A*100/255", "round": 0, "unit": "%", "group": "engine", "vmin": 0, "vmax": 100, "confidence": "verified"},
|
||||
{"key": "TIMING", "name": "Timing Advance", "mode": "01", "pid": "0E", "nbytes": 1, "formula": "A/2-64", "round": 1, "unit": "deg", "group": "engine", "vmin": -64, "vmax": 64, "confidence": "verified"},
|
||||
{"key": "VPCM", "name": "Module Voltage", "mode": "01", "pid": "42", "nbytes": 2, "formula": "(A*256+B)/1000", "round": 2, "unit": "V", "group": "power", "vmin": 0, "vmax": 16, "confidence": "verified"},
|
||||
{"key": "BATT", "name": "Battery (OBD port)", "mode": "atrv", "unit": "V", "group": "power", "vmin": 0, "vmax": 16, "confidence": "verified", "notes": "ELM327 ATRV pin voltage"}
|
||||
],
|
||||
"dtcs": []
|
||||
}
|
||||
Reference in New Issue
Block a user