Built by an isolated-worktree agent. Adds a thread-safe one-off command path to PollScheduler (DTC reads/clears never race the polling thread for the serial link), Controller.read_dtcs()/clear_dtcs(), and a GUI Diagnostics menu + dock (codes grouped stored/pending/permanent, descriptions from the active profile's DtcDatabase, no_start codes bold red, guarded clear with re-read). Reviewed: threading path correct (drain at top of tick, inline when not running, timeout + exception propagation); merges clean with the vehicle profiles (disjoint files). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs
ford-obd
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 codes/PIDs; the triage notes are 6.0-specific.
Created as a stopgap while forscan.org was offline — it covers reading/clearing codes and the basics, not Ford-enhanced diesel PIDs (see Scope below).
Features
- Read stored (mode 03), pending (mode 07), permanent (mode 0A) DTCs
- Decode P/C/B/U codes, with common 6.0 codes described and no-start suspects flagged
- Clear codes (mode 04) — guarded behind
--clear+ a typedCLEARconfirmation, then re-reads to show any code that returns immediately (active fault) - Key live values (coolant, IAT, MAP, module voltage, RPM, load, throttle) + battery voltage
- 6.0 Power Stroke no-start triage checklist (FICM, ICP, cam/crank, batteries, fuel)
Setup
Runs on Windows, macOS, and Linux (Python + pyserial). The only per-OS difference is the CH340 USB driver:
- Windows — install WCH
CH341SER; adapter shows asUSB-SERIAL CH340 (COMx)in Device Manager → Ports. Install Python from https://www.python.org/downloads/ (tick Add Python to PATH), or just double-clickRUN_OBD.bat. - macOS — install WCH
CH34xVCPDriver(Mac App Store or wch.cn). Port appears as/dev/cu.wchusbserial*.pip install pyserial. - Linux —
ch341driver is built into the kernel (no install). Port is/dev/ttyUSB0; add yourself to thedialoutgroup for access (sudo usermod -aG dialout $USER, then re-login).pip install pyserial.
The tool auto-detects the port on all three; pass it explicitly if needed
(COM5, /dev/cu.usbserial-1420, /dev/ttyUSB0).
Usage
python obd_reader.py # auto-detect the COM port
python obd_reader.py COM5 # force a port
python obd_reader.py COM5 9600 # force port + baud (default 38400)
python obd_reader.py COM5 --clear # read, then optionally clear (asks to confirm)
python obd_reader.py COM5 -v # verbose: show raw ELM327 traffic
Crank monitor (dedicated no-start view) — --crank
The one to use for a crank-but-won't-start. Big ICP readout with a wide bar
(the | marks the 500-psi firing threshold), a rolling ASCII trace of the
ICP build-up, peak-hold, FICM/battery/RPM with sag tracking, and a pass/fail
verdict. Start it, then crank.
python obd_reader.py COM5 --crank # crank monitor
python obd_reader.py COM5 --crank --dash-log crank.csv # + record a CSV
ICP [#################################|##----] 539.8 psi
PEAK 540 psi FIRING PRESSURE REACHED
FICM Main 47.5V (min 47.5) [DOC] Batt 12.6V (min 10.7) RPM 200
ICP trace (psi vs time, last 16 samples)
600 |
500 |----------------------------------------------#### <- firing line
| ######
| ########
+--------------------------------------------------
Read it: ICP should climb past 500 psi within 1–2 s of cranking
(FIRING PRESSURE REACHED, green). If it stalls below 500 (red, trace flat
under the line), that's the high-pressure oil bleed-off — STC fitting / oil-rail
O-rings. On exit it prints the peak and a verdict. q quits, r resets.
Graphical app (preview — P1)
A cross-platform desktop GUI (PySide6 + pyqtgraph) is in progress. P1 = PID browser + live overlay plot; see ARCHITECTURE.md for the roadmap (cranking/driving/diagnostics perspectives, record/playback, etc.).
pip install -r requirements-gui.txt
python run_gui.py # tick "Mock" + Connect to explore with no adapter
The whole app runs against simulated data (MockLink) so it can be developed
on any machine and only needs the truck for real captures.
Live dashboard (real-time gauges)
Updates in place as you crank or run the engine — color-coded, with live
min/max so a crank's peak ICP is captured. No extra dependencies (ANSI;
works on any Windows 10+ terminal). q quits, r resets min/max.
python obd_reader.py COM5 --dash # vitals preset (ICP, FICM, IPR, batt, RPM, temps)
python obd_reader.py COM5 --dash crank # cranking preset: ICP / FICM main / batt / RPM (fastest)
python obd_reader.py COM5 --dash full # every PID
python obd_reader.py COM5 --dash crank --dash-log crank.csv # + write a CSV while you watch
No-start use: run --dash crank, then crank. A healthy 6.0 builds
~500+ psi ICP within 1–2 s; if ICP stalls below 500 (red), that confirms
the high-pressure oil bleed-off. FICM Main should hold ~48V. The --dash-log
CSV is your streaming log — paste it back for analysis.
Note: the FICM PIDs (09xx) are [DOC] (not yet confirmed on this truck); if
they read --, they auto-drop after a few frames so the refresh rate stays up.
Or just double-click RUN_OBD.bat on Windows (auto-installs pyserial).
On the truck: plug into the OBD port under the dash, key to RUN (engine off is fine for codes), then run the tool.
Scope / honesty
A generic ELM327 reads standard OBD-II only: codes, generic PIDs, port voltage. It does not read Ford-enhanced diesel PIDs (ICP, FICM main/sync voltage, IPR%) — those need FORScan. For FICM/ICP numbers, measure at the FICM with a meter, or use FORScan when it's available. Default baud is 38400 (measured on the CH340 adapter); try 9600 if you get garbage.
Requirements
pyserial (pip install pyserial). Tested against a QinHeng CH340 ELM327 v1.5 clone.
