Rename project to OBDash + per-metric colored multi-axis
Rename: the app is vehicle-agnostic, so 'ford-obd' was wrong. Rebranded all code/docs/profile authors to OBDash; Gitea repo renamed justin/ford-obd -> justin/obdash (remote + description updated). Ford the make and the ford-6.0-powerstroke profile are unchanged (that vehicle really is a Ford). Multi-axis upgrade (per request): - MultiAxisPlot now gives each METRIC its own Y axis, each axis colored to match its line; the primary metric owns the LEFT axis, others stack right. - Click a line to promote it to the left axis (sigClicked -> set_primary). - Cleaner teardown (no removeItem warnings); axis label no longer doubles the unit; Normalize round-trips. Validated headless: colored per-metric axes, promote-to-left, gauge view, normalize toggle, profile switch; obdcore + diagnostics tests 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:
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# ford-obd — Architecture & Roadmap
|
# OBDash — Architecture & Roadmap
|
||||||
|
|
||||||
Plan for growing the terminal `obd_reader.py` into a graphical Windows scan
|
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.
|
tool for the 6.0L Power Stroke, on a shared headless core.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ford-obd
|
# OBDash
|
||||||
|
|
||||||
Minimal **ELM327 OBD-II code reader** with a **Ford 6.0L Power Stroke no-start triage**,
|
Minimal **ELM327 OBD-II code reader** with a **Ford 6.0L Power Stroke no-start triage**,
|
||||||
built for a cheap CH340 ELM327 USB adapter. Works on any OBD-II vehicle for generic
|
built for a cheap CH340 ELM327 USB adapter. Works on any OBD-II vehicle for generic
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 2026-06-29 — 6.0 Power Stroke no-start session
|
# 2026-06-29 — 6.0 Power Stroke no-start session
|
||||||
|
|
||||||
In-cab diagnostic session using the `ford-obd` tool on the truck.
|
In-cab diagnostic session using the `OBDash` tool on the truck.
|
||||||
Charger on "12V Hi" was attached for most of the session.
|
Charger on "12V Hi" was attached for most of the session.
|
||||||
|
|
||||||
## Symptoms
|
## Symptoms
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 144 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 144 KiB |
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
"""PySide6 + pyqtgraph GUI for ford-obd (P1 shell).
|
"""PySide6 + pyqtgraph GUI for OBDash (P1 shell).
|
||||||
|
|
||||||
Run: python run_gui.py (or) python -m gui
|
Run: python run_gui.py (or) python -m gui
|
||||||
"""
|
"""
|
||||||
|
|||||||
+6
-6
@@ -1,4 +1,4 @@
|
|||||||
"""ford-obd GUI -- vehicle-agnostic scanner shell.
|
"""OBDash GUI -- vehicle-agnostic scanner shell.
|
||||||
|
|
||||||
Menu bar: File (captures) | Profile (vehicle profiles) | View | Help.
|
Menu bar: File (captures) | Profile (vehicle profiles) | View | Help.
|
||||||
Toolbar: port / baud / mock / connect + per-profile preset buttons.
|
Toolbar: port / baud / mock / connect + per-profile preset buttons.
|
||||||
@@ -98,7 +98,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.theme_act = self._act(viewm, "Light Theme", self._toggle_theme, checkable=True)
|
self.theme_act = self._act(viewm, "Light Theme", self._toggle_theme, checkable=True)
|
||||||
|
|
||||||
helpm = mb.addMenu("&Help")
|
helpm = mb.addMenu("&Help")
|
||||||
self._act(helpm, "About ford-obd", self._about)
|
self._act(helpm, "About OBDash", self._about)
|
||||||
self._act(helpm, "PID Confidence Legend", self._legend)
|
self._act(helpm, "PID Confidence Legend", self._legend)
|
||||||
self._act(helpm, "Active Profile Info", self._profile_info)
|
self._act(helpm, "Active Profile Info", self._profile_info)
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self._refresh_title()
|
self._refresh_title()
|
||||||
|
|
||||||
def _refresh_title(self):
|
def _refresh_title(self):
|
||||||
self.setWindowTitle(f"ford-obd — {self.ctl.profile.name}")
|
self.setWindowTitle(f"OBDash — {self.ctl.profile.name}")
|
||||||
|
|
||||||
def _load_profile(self, path):
|
def _load_profile(self, path):
|
||||||
if self.ctl.connected:
|
if self.ctl.connected:
|
||||||
@@ -643,10 +643,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
# ---------- help ----------
|
# ---------- help ----------
|
||||||
def _about(self):
|
def _about(self):
|
||||||
QtWidgets.QMessageBox.about(self, "About ford-obd",
|
QtWidgets.QMessageBox.about(self, "About OBDash",
|
||||||
"ford-obd — vehicle-agnostic OBD-II scanner\n\n"
|
"OBDash — vehicle-agnostic OBD-II scanner\n\n"
|
||||||
"Open source. Vehicle data lives in JSON profiles you can add/share.\n"
|
"Open source. Vehicle data lives in JSON profiles you can add/share.\n"
|
||||||
"git.jpaul.io/justin/ford-obd")
|
"git.jpaul.io/justin/obdash")
|
||||||
|
|
||||||
def _legend(self):
|
def _legend(self):
|
||||||
QtWidgets.QMessageBox.information(self, "PID confidence",
|
QtWidgets.QMessageBox.information(self, "PID confidence",
|
||||||
|
|||||||
+107
-51
@@ -1,5 +1,5 @@
|
|||||||
"""Custom widgets for the ford-obd GUI: a unit-grouped multi-axis plot, a
|
"""Custom widgets for the OBDash GUI: a multi-axis plot (one colored axis per
|
||||||
simple single-axis plot, and an arc gauge / gauge grid.
|
metric), a single-axis plot, and a car-style gauge / gauge grid.
|
||||||
|
|
||||||
All three plot/gauge containers share a small interface so the main window can
|
All three plot/gauge containers share a small interface so the main window can
|
||||||
swap between them: add_curve(key,name,unit,color) / set_data(key,xs,ys) /
|
swap between them: add_curve(key,name,unit,color) / set_data(key,xs,ys) /
|
||||||
@@ -10,8 +10,6 @@ import math
|
|||||||
from PySide6 import QtCore, QtGui, QtWidgets
|
from PySide6 import QtCore, QtGui, QtWidgets
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
|
|
||||||
MAX_EXTRA_AXES = 4 # base left axis + up to 4 right axes (5 units shown)
|
|
||||||
|
|
||||||
|
|
||||||
class SinglePlot(QtWidgets.QWidget):
|
class SinglePlot(QtWidgets.QWidget):
|
||||||
"""One shared Y axis. Used for the Normalize (% of range) mode."""
|
"""One shared Y axis. Used for the Normalize (% of range) mode."""
|
||||||
@@ -58,9 +56,14 @@ class SinglePlot(QtWidgets.QWidget):
|
|||||||
|
|
||||||
|
|
||||||
class MultiAxisPlot(QtWidgets.QWidget):
|
class MultiAxisPlot(QtWidgets.QWidget):
|
||||||
"""Overlay with one Y axis PER UNIT (psi / V / rpm / C / %), so mixed-scale
|
"""Overlay with one Y axis PER METRIC, each axis colored to match its line,
|
||||||
signals are all readable at their true values. Curves are grouped by unit;
|
so mixed-scale signals are all readable at their true values.
|
||||||
each extra unit gets its own right-hand axis + linked ViewBox."""
|
|
||||||
|
The 'primary' metric owns the LEFT axis; the rest stack on the right.
|
||||||
|
Click a line to promote it to the left axis. Beyond MAX_RIGHT extra metrics,
|
||||||
|
overflow curves share the primary axis (rare; plot fewer to compare)."""
|
||||||
|
|
||||||
|
MAX_RIGHT = 5
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -73,40 +76,29 @@ class MultiAxisPlot(QtWidgets.QWidget):
|
|||||||
self.p.setLabel("bottom", "time", units="s")
|
self.p.setLabel("bottom", "time", units="s")
|
||||||
self.legend = self.p.addLegend(offset=(10, 10))
|
self.legend = self.p.addLegend(offset=(10, 10))
|
||||||
self.base_vb = self.p.vb
|
self.base_vb = self.p.vb
|
||||||
self._units = {} # unit -> {vb, ax, base}
|
self._curves = {} # key -> {color,name,unit,curve,vb,ax}
|
||||||
self._curves = {} # key -> {curve, unit}
|
self._order = [] # insertion order of keys
|
||||||
self._next_col = 3
|
self._primary = None
|
||||||
self.base_vb.sigResized.connect(self._sync)
|
self.base_vb.sigResized.connect(self._sync)
|
||||||
|
|
||||||
def _ensure_unit(self, unit):
|
|
||||||
e = self._units.get(unit)
|
|
||||||
if e is not None:
|
|
||||||
return e
|
|
||||||
if not self._units: # first unit -> base left axis
|
|
||||||
self.p.setLabel("left", unit)
|
|
||||||
e = {"vb": self.base_vb, "ax": self.p.getAxis("left"), "base": True}
|
|
||||||
elif len(self._units) - 1 < MAX_EXTRA_AXES: # add a right axis
|
|
||||||
vb = pg.ViewBox()
|
|
||||||
ax = pg.AxisItem("right")
|
|
||||||
self.p.layout.addItem(ax, 2, self._next_col)
|
|
||||||
self._next_col += 1
|
|
||||||
self.p.scene().addItem(vb)
|
|
||||||
ax.linkToView(vb)
|
|
||||||
vb.setXLink(self.p)
|
|
||||||
ax.setLabel(unit)
|
|
||||||
e = {"vb": vb, "ax": ax, "base": False}
|
|
||||||
self._sync()
|
|
||||||
else: # out of axes -> reuse base
|
|
||||||
e = self._units[next(iter(self._units))]
|
|
||||||
self._units[unit] = e
|
|
||||||
return e
|
|
||||||
|
|
||||||
def add_curve(self, key, name, unit, color):
|
def add_curve(self, key, name, unit, color):
|
||||||
e = self._ensure_unit(unit)
|
if key in self._curves:
|
||||||
|
return
|
||||||
curve = pg.PlotCurveItem(pen=pg.mkPen(color, width=2), name=name)
|
curve = pg.PlotCurveItem(pen=pg.mkPen(color, width=2), name=name)
|
||||||
e["vb"].addItem(curve)
|
curve.setClickable(True, width=8)
|
||||||
self.legend.addItem(curve, name)
|
curve.sigClicked.connect(lambda *_a, k=key: self.set_primary(k))
|
||||||
self._curves[key] = {"curve": curve, "unit": unit}
|
self._curves[key] = {"color": color, "name": name, "unit": unit,
|
||||||
|
"curve": curve, "vb": None, "ax": None}
|
||||||
|
self._order.append(key)
|
||||||
|
if self._primary is None:
|
||||||
|
self._primary = key
|
||||||
|
self._rebuild()
|
||||||
|
|
||||||
|
def set_primary(self, key):
|
||||||
|
"""Move this metric's axis to the LEFT (make it the primary axis)."""
|
||||||
|
if key in self._curves and key != self._primary:
|
||||||
|
self._primary = key
|
||||||
|
self._rebuild()
|
||||||
|
|
||||||
def set_data(self, key, xs, ys):
|
def set_data(self, key, xs, ys):
|
||||||
c = self._curves.get(key)
|
c = self._curves.get(key)
|
||||||
@@ -117,30 +109,94 @@ class MultiAxisPlot(QtWidgets.QWidget):
|
|||||||
c = self._curves.pop(key, None)
|
c = self._curves.pop(key, None)
|
||||||
if not c:
|
if not c:
|
||||||
return
|
return
|
||||||
e = self._units.get(c["unit"])
|
self._order.remove(key)
|
||||||
if e:
|
self._detach(c)
|
||||||
e["vb"].removeItem(c["curve"])
|
if self._primary == key:
|
||||||
try:
|
self._primary = self._order[0] if self._order else None
|
||||||
self.legend.removeItem(c["curve"])
|
self._rebuild()
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
for key in list(self._curves):
|
for c in self._curves.values():
|
||||||
self.remove_curve(key)
|
self._detach(c)
|
||||||
|
self._curves = {}
|
||||||
|
self._order = []
|
||||||
|
self._primary = None
|
||||||
|
self.legend.clear()
|
||||||
|
|
||||||
def set_y_label(self, _text):
|
def set_y_label(self, _text):
|
||||||
pass # multi-axis labels itself per unit
|
pass # labels itself per metric
|
||||||
|
|
||||||
def set_background(self, bg):
|
def set_background(self, bg):
|
||||||
self.glw.setBackground(bg)
|
self.glw.setBackground(bg)
|
||||||
|
|
||||||
|
# -- internals --
|
||||||
|
def _detach(self, c):
|
||||||
|
"""Remove a curve from its current ViewBox and tear down its extra axis
|
||||||
|
(the base left axis is kept). Idempotent -- safe to call repeatedly."""
|
||||||
|
cur = c["curve"]
|
||||||
|
vb = c.get("vb")
|
||||||
|
if vb is not None:
|
||||||
|
try:
|
||||||
|
vb.removeItem(cur)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
ax = c.get("ax")
|
||||||
|
if ax is not None and ax is not self.p.getAxis("left"):
|
||||||
|
try:
|
||||||
|
self.p.layout.removeItem(ax)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
sc = ax.scene()
|
||||||
|
if sc is not None:
|
||||||
|
sc.removeItem(ax)
|
||||||
|
if vb is not None and vb is not self.base_vb:
|
||||||
|
sc = vb.scene()
|
||||||
|
if sc is not None:
|
||||||
|
sc.removeItem(vb)
|
||||||
|
c["vb"] = None
|
||||||
|
c["ax"] = None
|
||||||
|
|
||||||
|
def _rebuild(self):
|
||||||
|
for c in self._curves.values():
|
||||||
|
self._detach(c)
|
||||||
|
self.legend.clear()
|
||||||
|
if self._primary is None:
|
||||||
|
return
|
||||||
|
order = [self._primary] + [k for k in self._order if k != self._primary]
|
||||||
|
col = 3
|
||||||
|
left = self.p.getAxis("left")
|
||||||
|
for idx, key in enumerate(order):
|
||||||
|
c = self._curves[key]
|
||||||
|
cur = c["curve"]
|
||||||
|
pen = pg.mkPen(c["color"])
|
||||||
|
if idx == 0: # primary -> left axis
|
||||||
|
self.base_vb.addItem(cur)
|
||||||
|
left.setLabel(c["name"]) # name already carries the unit
|
||||||
|
left.setPen(pen); left.setTextPen(pen)
|
||||||
|
c["vb"] = self.base_vb; c["ax"] = left
|
||||||
|
elif idx <= self.MAX_RIGHT: # right axis, colored to line
|
||||||
|
vb = pg.ViewBox()
|
||||||
|
ax = pg.AxisItem("right")
|
||||||
|
self.p.layout.addItem(ax, 2, col); col += 1
|
||||||
|
self.p.scene().addItem(vb)
|
||||||
|
ax.linkToView(vb); vb.setXLink(self.p)
|
||||||
|
ax.setLabel(c["name"])
|
||||||
|
ax.setPen(pen); ax.setTextPen(pen)
|
||||||
|
vb.addItem(cur)
|
||||||
|
c["vb"] = vb; c["ax"] = ax
|
||||||
|
else: # overflow -> primary axis
|
||||||
|
self.base_vb.addItem(cur)
|
||||||
|
c["vb"] = self.base_vb; c["ax"] = None
|
||||||
|
self.legend.addItem(cur, c["name"])
|
||||||
|
self._sync()
|
||||||
|
|
||||||
def _sync(self):
|
def _sync(self):
|
||||||
rect = self.base_vb.sceneBoundingRect()
|
rect = self.base_vb.sceneBoundingRect()
|
||||||
for e in self._units.values():
|
for c in self._curves.values():
|
||||||
if not e["base"]:
|
vb = c.get("vb")
|
||||||
e["vb"].setGeometry(rect)
|
if vb is not None and vb is not self.base_vb:
|
||||||
e["vb"].linkedViewChanged(self.base_vb, e["vb"].XAxis)
|
vb.setGeometry(rect)
|
||||||
|
vb.linkedViewChanged(self.base_vb, vb.XAxis)
|
||||||
|
|
||||||
|
|
||||||
class ArcGauge(QtWidgets.QWidget):
|
class ArcGauge(QtWidgets.QWidget):
|
||||||
|
|||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
# Handoff — ford-obd / 6.0 Power Stroke no-start
|
# Handoff — OBDash / 6.0 Power Stroke no-start
|
||||||
|
|
||||||
Pick-up notes for diagnosing the truck in the cab. Repo: `git.jpaul.io/justin/ford-obd` (private).
|
Pick-up notes for diagnosing the truck in the cab. Repo: `git.jpaul.io/justin/obdash` (private).
|
||||||
|
|
||||||
## TL;DR — what to do at the truck
|
## TL;DR — what to do at the truck
|
||||||
1. CH340 ELM327 adapter → OBD port (under dash, driver side).
|
1. CH340 ELM327 adapter → OBD port (under dash, driver side).
|
||||||
@@ -78,10 +78,10 @@ The 6.0 needs, to fire: **good batteries → FICM ~48V → ICP ~500 psi → fuel
|
|||||||
[crank-test-2026-06-30.txt](diagnostics/2026-06-29-no-start/crank-test-2026-06-30.txt).
|
[crank-test-2026-06-30.txt](diagnostics/2026-06-29-no-start/crank-test-2026-06-30.txt).
|
||||||
Next: physical inspection (IPR valve first, then STC fitting, then
|
Next: physical inspection (IPR valve first, then STC fitting, then
|
||||||
oil rail O-rings).
|
oil rail O-rings).
|
||||||
- Pushed to `git.jpaul.io/justin/ford-obd`, branch `main`.
|
- Pushed to `git.jpaul.io/justin/obdash`, branch `main`.
|
||||||
|
|
||||||
## To resume with Claude from the cab
|
## To resume with Claude from the cab
|
||||||
Mention: "6.0 Power Stroke no-start, using the ford-obd tool (git.jpaul.io/justin/ford-obd)".
|
Mention: "6.0 Power Stroke no-start, using the OBDash tool (git.jpaul.io/justin/obdash)".
|
||||||
Then **paste the tool's full output** (codes + live values). Useful to also say: cranks vs.
|
Then **paste the tool's full output** (codes + live values). Useful to also say: cranks vs.
|
||||||
no-crank, hot vs. cold, what changed before it died, and FICM/ICP readings if you metered them.
|
no-crank, hot vs. cold, what changed before it died, and FICM/ICP readings if you metered them.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# Vehicle Profiles
|
# Vehicle Profiles
|
||||||
|
|
||||||
Each `*.json` file here is a **vehicle profile** — pure data that makes the
|
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
|
OBDash 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
|
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.
|
**Profile → Load**, or drop a new file in this folder and it appears in the list.
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"model": "Super Duty / Excursion",
|
"model": "Super Duty / Excursion",
|
||||||
"years": "2003-2007",
|
"years": "2003-2007",
|
||||||
"engine": "6.0L Power Stroke diesel",
|
"engine": "6.0L Power Stroke diesel",
|
||||||
"author": "ford-obd project",
|
"author": "OBDash project",
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"protocol": "auto",
|
"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."
|
"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."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"model": "Mustang SVT Cobra",
|
"model": "Mustang SVT Cobra",
|
||||||
"years": "1996",
|
"years": "1996",
|
||||||
"engine": "4.6L DOHC 32-valve modular V8 (naturally aspirated, EEC-V, MAF-metered)",
|
"engine": "4.6L DOHC 32-valve modular V8 (naturally aspirated, EEC-V, MAF-metered)",
|
||||||
"author": "ford-obd project",
|
"author": "OBDash project",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"protocol": "SAE J1850 PWM",
|
"protocol": "SAE J1850 PWM",
|
||||||
"notes": "First OBD-II model year on early EEC-V; Ford SCP J1850 PWM at 41.6 kbps. MAF-based airflow (no MAP/speed-density). V8 with two banks: dual-bank fuel trims and pre/post-cat narrowband HO2S included. CAN-era PIDs (0142 module voltage, 0146 ambient, 0133 baro, 012F fuel level) not supported and omitted. No verifiable manufacturer-enhanced (mode 22) PIDs for this EEC-V SCP vehicle; generic ELM327 adapters widely reported to fail enhanced sessions on Ford SCP PWM. All reliable data is in the standard SAE J1979 mode-01 set. Validated: all formulas safe-evaluator-clean; fuel-trim vmax normalized to +-100."
|
"notes": "First OBD-II model year on early EEC-V; Ford SCP J1850 PWM at 41.6 kbps. MAF-based airflow (no MAP/speed-density). V8 with two banks: dual-bank fuel trims and pre/post-cat narrowband HO2S included. CAN-era PIDs (0142 module voltage, 0146 ambient, 0133 baro, 012F fuel level) not supported and omitted. No verifiable manufacturer-enhanced (mode 22) PIDs for this EEC-V SCP vehicle; generic ELM327 adapters widely reported to fail enhanced sessions on Ford SCP PWM. All reliable data is in the standard SAE J1979 mode-01 set. Validated: all formulas safe-evaluator-clean; fuel-trim vmax normalized to +-100."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"model": "Mustang GT",
|
"model": "Mustang GT",
|
||||||
"years": "1996",
|
"years": "1996",
|
||||||
"engine": "4.6L SOHC 2-valve modular V8 (EEC-V, MAF-metered)",
|
"engine": "4.6L SOHC 2-valve modular V8 (EEC-V, MAF-metered)",
|
||||||
"author": "ford-obd project",
|
"author": "OBDash project",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"protocol": "SAE J1850 PWM",
|
"protocol": "SAE J1850 PWM",
|
||||||
"notes": "EEC-V PCM on SAE J1850 PWM (41.6 kbaud, OBD-II pins 2 & 10). MAF-metered V8 with dual exhaust: four heated O2 sensors (B1S1/B1S2/B2S1/B2S2) and two fuel-trim banks. Bank 1 = passenger side (cyl #1). Standard SAE J1979 Mode-01 backbone; no verifiable mode-22 enhanced PIDs published for this early EEC-V generation."
|
"notes": "EEC-V PCM on SAE J1850 PWM (41.6 kbaud, OBD-II pins 2 & 10). MAF-metered V8 with dual exhaust: four heated O2 sensors (B1S1/B1S2/B2S1/B2S2) and two fuel-trim banks. Bank 1 = passenger side (cyl #1). Standard SAE J1979 Mode-01 backbone; no verifiable mode-22 enhanced PIDs published for this early EEC-V generation."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"model": "Any OBD-II vehicle (1996+)",
|
"model": "Any OBD-II vehicle (1996+)",
|
||||||
"years": "1996+",
|
"years": "1996+",
|
||||||
"engine": "any",
|
"engine": "any",
|
||||||
"author": "ford-obd project",
|
"author": "OBDash project",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"protocol": "auto",
|
"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."
|
"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."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"model": "Wrangler TJ",
|
"model": "Wrangler TJ",
|
||||||
"years": "1997",
|
"years": "1997",
|
||||||
"engine": "4.0L I6 (242 cu in)",
|
"engine": "4.0L I6 (242 cu in)",
|
||||||
"author": "ford-obd project",
|
"author": "OBDash project",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"protocol": "ISO 9141-2",
|
"protocol": "ISO 9141-2",
|
||||||
"notes": "Chrysler/Jeep OBD-II over ISO 9141-2 K-line (used by Chrysler/Jeep for generic OBD-II through the early 2000s, later replaced by ISO 15765 CAN; Chrysler never used J1850). Speed-density: MAP sensor, no MAF. Single bank (inline-6): fuel trim bank 1 only, two narrowband O2 sensors (B1S1 upstream, B1S2 post-cat). Generic Mode-01 only; no generic service-22 enhanced channel on this JTEC PCM (Chrysler enhanced data lives on the proprietary SCI bus)."
|
"notes": "Chrysler/Jeep OBD-II over ISO 9141-2 K-line (used by Chrysler/Jeep for generic OBD-II through the early 2000s, later replaced by ISO 15765 CAN; Chrysler never used J1850). Speed-density: MAP sensor, no MAF. Single bank (inline-6): fuel trim bank 1 only, two narrowband O2 sensors (B1S1 upstream, B1S2 post-cat). Generic Mode-01 only; no generic service-22 enhanced channel on this JTEC PCM (Chrysler enhanced data lives on the proprietary SCI bus)."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"model": "Mountaineer",
|
"model": "Mountaineer",
|
||||||
"years": "2006",
|
"years": "2006",
|
||||||
"engine": "4.6L SOHC 3-valve Triton V8 (MAF-metered, AWD)",
|
"engine": "4.6L SOHC 3-valve Triton V8 (MAF-metered, AWD)",
|
||||||
"author": "ford-obd project",
|
"author": "OBDash project",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"protocol": "SAE J1850 PWM",
|
"protocol": "SAE J1850 PWM",
|
||||||
"notes": "UN150 platform, badge-twin of the 4th-gen (2006-2010) Ford Explorer. Uses Ford SCP = SAE J1850 PWM (DLC pins 2 & 10, 41.6 kbps) for SAE J1979 diagnostics \u2014 ELM327 protocol 1. The MS-CAN on this platform is an internal body network (FORScan), not the generic emissions bus; ISO 15765 CAN only arrived for MY2008. MAF-metered, no MAP. Two banks, two narrowband HEGO O2 sensors per bank. No verifiable mode-22 enhanced PIDs for this pre-CAN J1850 PCM, so none included. Mode-01 0142 module voltage omitted (CAN-era PID); use BATT (ATRV)."
|
"notes": "UN150 platform, badge-twin of the 4th-gen (2006-2010) Ford Explorer. Uses Ford SCP = SAE J1850 PWM (DLC pins 2 & 10, 41.6 kbps) for SAE J1979 diagnostics \u2014 ELM327 protocol 1. The MS-CAN on this platform is an internal body network (FORScan), not the generic emissions bus; ISO 15765 CAN only arrived for MY2008. MAF-metered, no MAP. Two banks, two narrowband HEGO O2 sensors per bank. No verifiable mode-22 enhanced PIDs for this pre-CAN J1850 PCM, so none included. Mode-01 0142 module voltage omitted (CAN-era PID); use BATT (ATRV)."
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Launcher for the ford-obd GUI.
|
"""Launcher for the OBDash GUI.
|
||||||
|
|
||||||
pip install PySide6 pyqtgraph numpy pyserial
|
pip install PySide6 pyqtgraph numpy pyserial
|
||||||
python run_gui.py
|
python run_gui.py
|
||||||
|
|||||||
Reference in New Issue
Block a user