Correct Ford 6.0 Mode-22 PID table from workflow research

The old 12xx PIDs (1209/1228/120B/...) were wrong addresses -- that's why
they returned 'no response' on the truck, NOT a bus/gateway problem. The real
Ford-enhanced DIDs are in the 09xx/14xx/16xx families. Confirmed by the truck's
own brute-scan: 1446=ICP, 1445=EBP, 1440=MAP, 1442=BARO, 1310=EOT, 11B3=gear,
11B4=TSS all decode to sane on-vehicle values.

- Rewrite FORD_60_PIDS with corrected addresses + [VERIFIED]/[DOC]/[TENTATIVE] tags
- FICM voltages -> 09D0/09CF/09CE/09CD (09D0 Main = the ~48V no-start metric)
- ICP=1446 *0.57, IPR=1434, ICP_V=16AD; EOT scaling fixed to /100-40
- watch --ford now streams 09D0/09CF/1446/1434 (FICM main V + ICP during crank)
- Add diagnostics/2026-06-29-no-start/pid-research.md (full workflow findings)

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:
2026-06-29 22:29:46 -04:00
parent 2fe3f1100e
commit 36b233f02c
2 changed files with 191 additions and 38 deletions
+61 -38
View File
@@ -11,19 +11,19 @@ Scope (what a generic ELM327 CAN do, engine off / KOEO):
* Read key KOEO live PIDs (coolant, IAT, MAP, module voltage, RPM...)
* Battery voltage at the OBD port (ATRV)
Experimental: with --ford it also tries a handful of community-sourced
Ford-enhanced Mode-22 PIDs (ICP, IPR%, FICM main/sync/logic voltage, EBP,
EOT). The PID numbers and scaling are TENTATIVE and need to be verified
against a known-good reading (meter or FORScan). Raw bytes are always
printed so you can sanity-check. Wrong PID = "no response", nothing
written to the truck.
Ford enhanced (--ford): reads Ford-enhanced Mode-22 PIDs (ICP, IPR%, FICM
main/logic/vehicle voltage + sync, MAP/BARO/EBP, EOT, gear). Addresses were
corrected 2026-06-29 (see diagnostics/2026-06-29-no-start/pid-research.md):
14xx/13xx/11Bx are VERIFIED on this truck; the 09xx FICM family is documented
but not yet read here -- re-probe to confirm. Raw bytes are always printed.
ICP_DES (desired ICP) has no public Mode-22 DID -> FORScan-only.
Usage (Windows):
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
python obd_reader.py --ford # + try Ford 6.0 Mode-22 PIDs
python obd_reader.py --pid 1430 # probe one Mode-22 PID, show raw
python obd_reader.py --ford # + read Ford 6.0 Mode-22 PIDs
python obd_reader.py --pid 1446 # probe one Mode-22 PID (1446=ICP), show raw
python obd_reader.py --clear # erase stored + pending DTCs
Requires: pip install pyserial
@@ -142,32 +142,54 @@ def _u16(b):
return (b[0] << 8) + b[1]
# Confidence tags in the notes column:
# [VERIFIED] multi-source AND confirmed by this truck's own brute-scan
# (the 14xx/13xx/11Bx PIDs returned sane values on-vehicle)
# [DOC] corroborated in >=2 independent sources but NOT yet read on
# this truck (the 09xx FICM family was outside the scan window)
# [TENTATIVE] single-source or disputed scaling -- sanity-check before trust
#
# These addresses were corrected 2026-06-29 by the ford-60-pid-hunt workflow.
# The OLD 12xx numbers (1209/1228/120B/...) were WRONG -- not real Mode-22
# DIDs -- which is why they all returned "no response" on the truck. See
# diagnostics/2026-06-29-no-start/pid-research.md for sources and the full
# verification. (ICP_DES / desired ICP has no public Mode-22 DID -> FORScan-only.)
FORD_60_PIDS = [
# --- Injection Control Pressure (need ~500+ psi to fire) ---
("1209", "ICP", "psi", lambda b: _u16(b),
"Injection Control Pressure (need ~500+ psi to fire)"),
("121A", "ICP_DES", "psi", lambda b: _u16(b),
"ICP desired (commanded)"),
("1430", "ICP_V", "V", lambda b: _u16(b) / 1000.0,
"ICP sensor raw voltage"),
("1446", "ICP", "psi", lambda b: round(_u16(b) * 0.57, 1),
"[VERIFIED] Injection Control Pressure -- need ~500+ psi to fire"),
("16AD", "ICP_V", "V", lambda b: round(_u16(b) * 0.000072, 4),
"[TENTATIVE] ICP sensor raw voltage (single-source)"),
# --- Injection Pressure Regulator duty ---
("120B", "IPR", "%", lambda b: round(b[0] * 100 / 255, 1),
"IPR duty (high = trying hard to make ICP)"),
("1434", "IPR", "%", lambda b: round(b[0] * 13.53 / 35, 1),
"[TENTATIVE] IPR duty -- KOEO ~14-15%, cranking ~30-40%, idle ~25-30%"),
# --- FICM voltages -- THE 6.0 no-start metric (~48V cranking) ---
("1228", "FICM_MPWR", "V", lambda b: _u16(b) / 1000.0,
"FICM Main Power -- want ~48V cranking, <45V = suspect"),
("1229", "FICM_SYNC", "V", lambda b: _u16(b) / 1000.0,
"FICM Sync Power"),
("122A", "FICM_LPWR", "V", lambda b: _u16(b) / 1000.0,
"FICM Logic Power (~12V)"),
("09D0", "FICM_MPWR", "V", lambda b: round(_u16(b) / 256.0, 1),
"[DOC] FICM Main Power -- want ~48V cranking, <45V = suspect"),
("09CF", "FICM_LPWR", "V", lambda b: round(_u16(b) / 256.0, 1),
"[DOC] FICM Logic Power (~12V)"),
("09CE", "FICM_VPWR", "V", lambda b: round(_u16(b) / 256.0, 1),
"[DOC] FICM Vehicle Power (battery, ~12-14V)"),
("09CD", "FICM_SYNC", "", lambda b: (b[0] >> 1) & 1,
"[DOC] FICM Sync -- 1=in sync, 0=no sync (bit 1 of A)"),
# --- Exhaust back pressure / oil temp (handy, not no-start critical) ---
("121C", "EBP", "psi", lambda b: _u16(b) / 100.0,
"Exhaust Back Pressure"),
("1310", "EOT", "C", lambda b: b[0] - 40,
"Engine Oil Temperature"),
# --- Pressures (all raw*0.03625 -> psi ABSOLUTE) ---
("1440", "MAP", "psi", lambda b: round(_u16(b) * 0.03625, 2),
"[VERIFIED] Manifold Absolute Pressure (psi abs; MGP boost = MAP-BARO)"),
("1442", "BARO", "psi", lambda b: round(_u16(b) * 0.03625, 2),
"[VERIFIED] Barometric Pressure (psi abs)"),
("1445", "EBP", "psi", lambda b: round(_u16(b) * 0.03625, 2),
"[VERIFIED] Exhaust Back Pressure (psi abs; minus BARO = gauge)"),
# --- Oil temp / driveline ---
("1310", "EOT", "C", lambda b: round(_u16(b) / 100.0 - 40, 1),
"[VERIFIED] Engine Oil Temperature"),
("11B3", "GEAR", "", lambda b: b[0] // 2,
"[VERIFIED] Current gear (5R110W TorqShift)"),
("11B4", "TSS", "rpm", lambda b: _u16(b) / 4,
"[VERIFIED] Trans input/turbine shaft speed"),
]
@@ -408,10 +430,11 @@ def read_ford_pids(elm, pids=None):
if pids is None:
pids = FORD_60_PIDS
print("\n" + "-" * 64)
print(" FORD 6.0 ENHANCED (Mode 22) -- TENTATIVE / UNVERIFIED")
print(" FORD 6.0 ENHANCED (Mode 22)")
print("-" * 64)
print(" PID numbers and scaling are community-sourced and NOT yet")
print(" verified on this truck. Raw bytes shown for sanity-checking.")
print(" [VERIFIED] = confirmed on this truck's scan; [DOC] = corroborated")
print(" in sources but not yet read here (re-probe to confirm);")
print(" [TENTATIVE] = single-source/disputed scaling. Raw bytes shown.")
print(" 'no response' = wrong PID or this PCM doesn't expose it.")
print()
any_response = False
@@ -464,17 +487,17 @@ def watch_loop(elm, seconds=20, with_ford=False):
"""Stream battery + module voltage as fast as we can for N seconds.
Designed for cranking: start it, then turn the key.
with_ford=True also streams Mode-22 FICM/ICP PIDs -- only enable once
those PIDs are verified for this PCM, otherwise each 'no response'
timeout starves the sample rate."""
with_ford=True also streams Mode-22 FICM/ICP PIDs. 09D0 (FICM Main) is
the no-start metric -- watch it sag during cranking (<45V = failing FICM).
The 09xx PIDs are [DOC]-grade (not yet read on this truck) -- if they
'no response', the timeout will starve the sample rate, so drop --ford."""
stream_pids = []
if with_ford:
stream_pids = [
("1228", "FICM_M", "V", lambda b: (b[0] << 8 | b[1]) / 1000.0),
("1229", "FICM_S", "V", lambda b: (b[0] << 8 | b[1]) / 1000.0),
("122A", "FICM_L", "V", lambda b: (b[0] << 8 | b[1]) / 1000.0),
("1209", "ICP", "psi", lambda b: (b[0] << 8 | b[1])),
("120B", "IPR", "%", lambda b: round(b[0] * 100 / 255, 1)),
("09D0", "FICM_M", "V", lambda b: round((b[0] << 8 | b[1]) / 256.0, 1)),
("09CF", "FICM_L", "V", lambda b: round((b[0] << 8 | b[1]) / 256.0, 1)),
("1446", "ICP", "psi", lambda b: round((b[0] << 8 | b[1]) * 0.57, 1)),
("1434", "IPR", "%", lambda b: round(b[0] * 13.53 / 35, 1)),
]
print("\n" + "=" * 64)
print(f" WATCH MODE ({seconds}s) -- Ctrl-C to stop early")