Section 1 backend: VIN/Mode-09, readiness monitors, freeze-frame, trip/perf
obdcore additions (all standard SAE J1979, vehicle-agnostic, hardware-free tested): - obdservices.py: decode_vin (Mode 09), decode_readiness (Mode 01 PID 01 I-M monitors + MIL + DTC count, spark/diesel monitor sets), freeze-frame PID set. - link.py: ElmLink.read_vehicle_info (VIN/cal/ECU), read_readiness, read_freeze_frame. - trip.py: TripComputer (MAF-based MPG + trip totals) and PerformanceMeter (0-60 / 1/4-mile with launch detection). - mock.py: speed/MAF/readiness + service stubs for GUI mock mode. - tests/test_services.py: VIN, readiness bit decode, trip math, 0-60/quarter. 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:
@@ -172,6 +172,41 @@ class ElmLink:
|
||||
data = self._bytes(lines)
|
||||
return 0x44 in data or ("OK" in "".join(lines).upper())
|
||||
|
||||
# -- standard OBD services (Mode 09 / 01-01 / 02) --
|
||||
def read_vehicle_info(self, timeout=2.0):
|
||||
"""Mode 09: VIN + calibration IDs + ECU name. Returns a dict."""
|
||||
from . import obdservices as svc
|
||||
vin = svc.decode_vin(self._bytes(self.cmd("0902", timeout=timeout)))
|
||||
cal = svc.decode_ascii_block(self._bytes(self.cmd("0904", timeout=timeout)), 0x04)
|
||||
ecu = svc.decode_ascii_block(self._bytes(self.cmd("090A", timeout=timeout)), 0x0A)
|
||||
return {"vin": vin, "calibration": cal, "ecu_name": ecu}
|
||||
|
||||
def read_readiness(self, timeout=1.0):
|
||||
"""Mode 01 PID 01: MIL, DTC count, and I-M readiness monitors."""
|
||||
from . import obdservices as svc
|
||||
data = self.read_m01("01", 4, timeout=timeout)
|
||||
return svc.decode_readiness(data) if data else None
|
||||
|
||||
def read_freeze_frame(self, timeout=0.6):
|
||||
"""Mode 02: the DTC that set the freeze frame + the standard PID snapshot."""
|
||||
from . import obdservices as svc
|
||||
out = {"dtc": None, "values": []}
|
||||
d = self._bytes(self.cmd("0202", timeout=timeout))
|
||||
if 0x42 in d:
|
||||
r = d[d.index(0x42) + 2:] # after '42 02'
|
||||
if len(r) >= 2 and (r[0] or r[1]):
|
||||
out["dtc"] = decode_dtc(r[0], r[1])
|
||||
for name, pid, nbytes, dec, unit in svc.FREEZE_PIDS:
|
||||
dd = self._bytes(self.cmd(f"02{pid}00", timeout=timeout))
|
||||
if 0x42 in dd:
|
||||
payload = dd[dd.index(0x42) + 3:dd.index(0x42) + 3 + nbytes]
|
||||
if len(payload) == nbytes:
|
||||
try:
|
||||
out["values"].append((name, dec(payload), unit))
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
self.ser.close()
|
||||
|
||||
Reference in New Issue
Block a user