From ba79775fbc66204dd464daa6cdf5fa8d31da12d1 Mon Sep 17 00:00:00 2001 From: metatroncubeswdev Date: Mon, 12 Oct 2026 23:08:29 -0400 Subject: [PATCH] Fix DTC tables and Clear DTC result; auto-set ECU type from adapter name - Rewrite S300 and KPro DTC tables from hvbt_protocol.pdf (the previous tables did not match the PDF; e.g. ERR01.6 is P0350 IGM, not P1361). - Clear DTC: the adapter never echoes the documented ack, but the ECU does clear (bench: 3 of 4 codes cleared, P0350 re-set at once as an active fault). Judge success from the live ERR bytes; wait only 0.6 s for the ack so polling resumes, and report "N of M cleared". - Set the ECU protocol from the adapter name (S300/KPro) on connect; a stale KPro setting made an S300 adapter look unresponsive. Co-Authored-By: Claude Opus 5.5 --- lib/core/bluetooth/bt_poller.dart | 12 + lib/core/protocol/dtc_map.dart | 390 ++++++++++++++-------------- lib/core/providers/bt_provider.dart | 96 ++++++- lib/ui/screens/dtc/dtc_screen.dart | 31 ++- test/dtc_map_test.dart | 34 ++- test/ecu_type_from_name_test.dart | 22 ++ 6 files changed, 367 insertions(+), 218 deletions(-) create mode 100644 test/ecu_type_from_name_test.dart diff --git a/lib/core/bluetooth/bt_poller.dart b/lib/core/bluetooth/bt_poller.dart index 223ad3e..cceb33f 100644 --- a/lib/core/bluetooth/bt_poller.dart +++ b/lib/core/bluetooth/bt_poller.dart @@ -7,6 +7,18 @@ import 'bt_service.dart'; enum EcuType { s300, kpro } +/// ECU type implied by the adapter's Bluetooth name, or null if the name +/// doesn't say. HV adapters advertise it, e.g. "S300[94]-SPP", +/// "HVS300-SPP[94]"; a KPro adapter is expected to contain "KPRO"/"K-PRO". +EcuType? ecuTypeFromDeviceName(String? name) { + if (name == null) return null; + final n = name.toUpperCase().replaceAll('-', ''); + final s300 = n.contains('S300'); + final kpro = n.contains('KPRO'); + if (s300 == kpro) return null; // neither, or ambiguous + return s300 ? EcuType.s300 : EcuType.kpro; +} + typedef _Matcher = Uint8List? Function(List rxBuf); class _Pending { diff --git a/lib/core/protocol/dtc_map.dart b/lib/core/protocol/dtc_map.dart index 0288dff..e4175d6 100644 --- a/lib/core/protocol/dtc_map.dart +++ b/lib/core/protocol/dtc_map.dart @@ -1,233 +1,241 @@ -/// DTC maps for S300 and KPro ECU protocols. +/// DTC maps for S300 and KPro ECU protocols, transcribed from the "DTC map" +/// sections of hvbt_protocol.pdf. /// -/// Key format: 'ERRxx_bity' where xx = byte index (00-based), y = bit number (0=LSB). -/// Value: 'Pxxxx — description' +/// Key format: 'ERRxx_bity' where xx = ERR byte index (00-based), y = bit +/// number (0=LSB). Value: ' — description'. Bits the PDF leaves +/// undocumented ("???") use 'ERRxx.y' as the code so the raw bit is still +/// visible on screen. library; -/// S300 DTC map (ERR00–ERR03, 4 bytes × 8 bits = 32 possible codes) +/// S300 DTC map (ERR00–ERR03 = frame bytes 37–40). Descriptions include the +/// Honda blink code and sensor abbreviation from the PDF. const Map s300DtcMap = { - // ERR00 (byte 0x31) - 'ERR00_bit0': 'P0130 — O2 Sensor (front)', - 'ERR00_bit1': 'P0135 — O2 Sensor Heater (front)', - 'ERR00_bit2': 'P0505 — Idle Air Control Valve', - 'ERR00_bit3': 'P0108 — MAP Sensor High', - 'ERR00_bit4': 'P0107 — MAP Sensor Low', - 'ERR00_bit5': 'P0113 — IAT Sensor High', - 'ERR00_bit6': 'P0112 — IAT Sensor Low', - 'ERR00_bit7': 'P0118 — ECT Sensor High', - - // ERR01 (byte 0x32) - 'ERR01_bit0': 'P0117 — ECT Sensor Low', - 'ERR01_bit1': 'P0336 — CKP Sensor', - 'ERR01_bit2': 'P0341 — CMP Sensor', - 'ERR01_bit3': 'P1362 — TDC Sensor (No Signal)', - 'ERR01_bit4': 'P1363 — TDC Sensor (No Signal 2)', - 'ERR01_bit5': 'P0335 — CKP Sensor (No Signal)', - 'ERR01_bit6': 'P1361 — TDC Sensor', - 'ERR01_bit7': 'P0300 — Random Misfire', - - // ERR02 (byte 0x33) - 'ERR02_bit0': 'P0301 — Misfire Cylinder 1', - 'ERR02_bit1': 'P0302 — Misfire Cylinder 2', - 'ERR02_bit2': 'P0303 — Misfire Cylinder 3', - 'ERR02_bit3': 'P0304 — Misfire Cylinder 4', - 'ERR02_bit4': 'P1259 — VTEC System Malfunction', - 'ERR02_bit5': 'P0420 — Catalyst System Low Efficiency', - 'ERR02_bit6': 'P0401 — EGR Insufficient Flow', - 'ERR02_bit7': 'P1456 — EVAP Leak (large) Fuel Tank Side', - - // ERR03 (byte 0x34) - 'ERR03_bit0': 'P1457 — EVAP Leak (large) Canister Side', - 'ERR03_bit1': 'P0498 — EVAP Canister Vent Shut Valve', - 'ERR03_bit2': 'P0497 — EVAP Low Purge Flow', - 'ERR03_bit3': 'P0122 — TPS Sensor Low', - 'ERR03_bit4': 'P0123 — TPS Sensor High', - 'ERR03_bit5': 'P0562 — Battery Voltage Low', - 'ERR03_bit6': 'P0563 — Battery Voltage High', - 'ERR03_bit7': 'P1607 — PCM Internal Circuit', -}; - -/// KPro DTC map (ERR00–ERR17, 18 bytes × 8 bits = 144 possible codes) -const Map kproDtcMap = { // ERR00 - 'ERR00_bit0': 'P0130 — O2 Sensor (front)', - 'ERR00_bit1': 'P0135 — O2 Sensor Heater (front)', - 'ERR00_bit2': 'P0505 — Idle Air Control Valve', - 'ERR00_bit3': 'P0108 — MAP Sensor High', - 'ERR00_bit4': 'P0107 — MAP Sensor Low', - 'ERR00_bit5': 'P0113 — IAT Sensor High', - 'ERR00_bit6': 'P0112 — IAT Sensor Low', - 'ERR00_bit7': 'P0118 — ECT Sensor High', + 'ERR00_bit0': 'P0130 — Oxygen Sensor #1 (O2A, code 1)', + 'ERR00_bit1': 'P0150 — Oxygen Sensor #2 (O2B, code 2)', + 'ERR00_bit2': 'P0105 — Manifold Absolute Pressure Sensor (MAP, code 3)', + 'ERR00_bit3': 'P0335 — Crank Position Sensor (CKP, code 4)', + 'ERR00_bit4': 'P0105 — Manifold Absolute Pressure Sensor (MAP, code 5)', + 'ERR00_bit5': 'P0106 — Engine Coolant Sensor (ECT, code 6)', + 'ERR00_bit6': 'P0123 — Throttle Position Sensor (TPS, code 7)', + 'ERR00_bit7': 'P1362 — Top Dead Center Sensor (TDC, code 8)', // ERR01 - 'ERR01_bit0': 'P0117 — ECT Sensor Low', - 'ERR01_bit1': 'P0336 — CKP Sensor', - 'ERR01_bit2': 'P0341 — CMP Sensor', - 'ERR01_bit3': 'P1362 — TDC Sensor (No Signal)', - 'ERR01_bit4': 'P1363 — TDC Sensor (No Signal 2)', - 'ERR01_bit5': 'P0335 — CKP Sensor (No Signal)', - 'ERR01_bit6': 'P1361 — TDC Sensor', - 'ERR01_bit7': 'P0300 — Random Misfire', + 'ERR01_bit0': 'P0340 — Cylinder/Cam Sensor (CYL, code 9)', + 'ERR01_bit1': 'P0113 — Intake Air Temperature Sensor (IAT, code 10)', + 'ERR01_bit2': 'P0217 — Engine Overheating (OH, code 11)', + 'ERR01_bit3': 'P0403 — EGR Lift Valve (EGR, code 12)', + 'ERR01_bit4': 'P1106 — Atmospheric Pressure Sensor (BARO, code 13)', + 'ERR01_bit5': 'P0505 — Idle Air Control Valve (IAC, code 14)', + 'ERR01_bit6': 'P0350 — Ignition Control Module (IGM, code 15)', + 'ERR01_bit7': 'P0200 — Fuel Injectors (INJ, code 16)', // ERR02 - 'ERR02_bit0': 'P0301 — Misfire Cylinder 1', - 'ERR02_bit1': 'P0302 — Misfire Cylinder 2', - 'ERR02_bit2': 'P0303 — Misfire Cylinder 3', - 'ERR02_bit3': 'P0304 — Misfire Cylinder 4', - 'ERR02_bit4': 'P1259 — VTEC System Malfunction', - 'ERR02_bit5': 'P0420 — Catalyst System Low Efficiency', - 'ERR02_bit6': 'P0401 — EGR Insufficient Flow', - 'ERR02_bit7': 'P1456 — EVAP Leak (large) Fuel Tank Side', + 'ERR02_bit0': 'P0500 — Vehicle Speed Sensor (VSS, code 17)', + 'ERR02_bit1': 'ERR02.1 — Undocumented code', + 'ERR02_bit2': 'P0700 — Automatic Transmission Lockup Control Valve (code 19)', + 'ERR02_bit3': 'P1298 — Electrical Load Detector (ELD, code 20)', + 'ERR02_bit4': 'P2649 — VTEC Solenoid (VTS, code 21)', + 'ERR02_bit5': 'P2647 — VTEC Pressure Valve (VTP, code 22)', + 'ERR02_bit6': 'P0325 — Knock Sensor (code 23)', + 'ERR02_bit7': 'P1324 — Knock Sensor (code 24)', // ERR03 - 'ERR03_bit0': 'P1457 — EVAP Leak (large) Canister Side', - 'ERR03_bit1': 'P0498 — EVAP Canister Vent Shut Valve', - 'ERR03_bit2': 'P0497 — EVAP Low Purge Flow', - 'ERR03_bit3': 'P0122 — TPS Sensor Low', - 'ERR03_bit4': 'P0123 — TPS Sensor High', - 'ERR03_bit5': 'P0562 — Battery Voltage Low', - 'ERR03_bit6': 'P0563 — Battery Voltage High', - 'ERR03_bit7': 'P1607 — PCM Internal Circuit', + 'ERR03_bit0': 'P0750 — Automatic Transmission A Signal (ATA, code 30)', + 'ERR03_bit1': 'P0755 — Automatic Transmission B Signal (ATB, code 31)', + 'ERR03_bit2': 'P0135 — Primary Oxygen Sensor Heater (PO2H, code 41)', + 'ERR03_bit3': 'P0135 — Oxygen Sensor Heater', + 'ERR03_bit4': 'P0169 — Fuel Supply System (code 43)', + 'ERR03_bit5': 'ERR03.5 — Undocumented code', + 'ERR03_bit6': 'ERR03.6 — Undocumented code', + 'ERR03_bit7': 'ERR03.7 — Undocumented code', +}; + +/// KPro DTC map (ERR00–ERR17 = frame bytes 62–79). +const Map kproDtcMap = { + // ERR00 + 'ERR00_bit0': 'ERR00.0 — Unknown error 0', + 'ERR00_bit1': 'P1607 — Internal Circuit Malfunction (check calibration type)', + 'ERR00_bit2': 'P0131 — O2 Sensor Voltage Low', + 'ERR00_bit3': 'P0132 — O2 Sensor Voltage High', + 'ERR00_bit4': 'P0107 — MAP Sensor Low Voltage', + 'ERR00_bit5': 'P0108 — MAP Sensor High Voltage', + 'ERR00_bit6': 'P0335 — CRK Sensor No Signal', + 'ERR00_bit7': 'P0336 — CRK Sensor Intermittent Interruption', + + // ERR01 + 'ERR01_bit0': 'P1128 — MAP Sensor Signal Lower Than Expected', + 'ERR01_bit1': 'P1129 — MAP Sensor Signal Higher Than Expected', + 'ERR01_bit2': 'P0117 — ECT Sensor Low Voltage', + 'ERR01_bit3': 'P0118 — ECT Sensor High Voltage', + 'ERR01_bit4': 'P0122 — TPS Low Voltage', + 'ERR01_bit5': 'P0123 — TPS High Voltage', + 'ERR01_bit6': 'P1121 — TPS Signal Lower Than Expected', + 'ERR01_bit7': 'P1122 — TPS Signal Higher Than Expected', + + // ERR02 + 'ERR02_bit0': 'P0176 — Ethanol Sensor Input Error', + 'ERR02_bit1': 'ERR02.1 — No description available', + 'ERR02_bit2': 'ERR02.2 — No description available', + 'ERR02_bit3': 'ERR02.3 — No description available', + 'ERR02_bit4': 'P1362 — TDC Sensor No Signal', + 'ERR02_bit5': 'P1361 — TDC Sensor Intermittent Signal Interruption', + 'ERR02_bit6': 'P0112 — IAT Sensor Low Voltage', + 'ERR02_bit7': 'P0113 — IAT Sensor High Voltage', + + // ERR03 + 'ERR03_bit0': 'P1213 — IMA Sensor Voltage Low', + 'ERR03_bit1': 'P1214 — IMA Sensor Voltage High', + 'ERR03_bit2': 'ERR03.2 — EGR Lift Sensor Voltage Error', + 'ERR03_bit3': 'ERR03.3 — EGR F/B Error', + 'ERR03_bit4': 'P1107 — Barometric Pressure Low Voltage', + 'ERR03_bit5': 'P1108 — Barometric Pressure High Voltage', + 'ERR03_bit6': 'P1106 — Barometric Pressure Sensor Range/Performance', + 'ERR03_bit7': 'ERR03.7 — Unknown error 37', // ERR04 - 'ERR04_bit0': 'P0136 — O2 Sensor (rear)', - 'ERR04_bit1': 'P0141 — O2 Sensor Heater (rear)', - 'ERR04_bit2': 'P1166 — Air Fuel Ratio (A/F) Sensor Heater', - 'ERR04_bit3': 'P1167 — Air Fuel Ratio (A/F) Sensor', - 'ERR04_bit4': 'P2195 — A/F Sensor Signal Stuck Lean', - 'ERR04_bit5': 'P2196 — A/F Sensor Signal Stuck Rich', - 'ERR04_bit6': 'P2237 — A/F Sensor Pump Cell Current Input Low', - 'ERR04_bit7': 'P2240 — A/F Sensor Pump Cell Current Input High', + 'ERR04_bit0': 'P0505 — Idle Control System Malfunction', + 'ERR04_bit1': 'P1519 — Idle Air Control Valve Circuit Malfunction', + 'ERR04_bit2': 'P0500 — VSS Malfunction', + 'ERR04_bit3': 'ERR04.3 — VSP', + 'ERR04_bit4': 'P1297 — ELD Circuit Low Voltage', + 'ERR04_bit5': 'P1298 — ELD Circuit High Voltage', + 'ERR04_bit6': 'P1253 — VTEC Solenoid Drive Time Abnormality', + 'ERR04_bit7': 'ERR04.7 — Unknown error 47', // ERR05 - 'ERR05_bit0': 'P2243 — A/F Sensor Reference Voltage Low', - 'ERR05_bit1': 'P2247 — A/F Sensor Reference Voltage High', - 'ERR05_bit2': 'P2251 — A/F Sensor VS Cell Voltage Low', - 'ERR05_bit3': 'P2254 — A/F Sensor VS Cell Voltage High', - 'ERR05_bit4': 'P0031 — O2 Sensor Heater Current Low (bank 1, sensor 1)', - 'ERR05_bit5': 'P0032 — O2 Sensor Heater Current High (bank 1, sensor 1)', - 'ERR05_bit6': 'P0037 — O2 Sensor Heater Current Low (bank 1, sensor 2)', - 'ERR05_bit7': 'P0038 — O2 Sensor Heater Current High (bank 1, sensor 2)', + 'ERR05_bit0': 'P1259 — VTEC System Malfunction (Oil Pressure Switch)', + 'ERR05_bit1': 'ERR05.1 — Unknown error 51', + 'ERR05_bit2': 'P0325 — Knock Sensor Circuit Malfunction', + 'ERR05_bit3': 'P1324 — Knock Sensor Circuit Failure', + 'ERR05_bit4': 'P0563 — VBU Voltage High', + 'ERR05_bit5': 'ERR05.5 — Unknown error 55', + 'ERR05_bit6': 'ERR05.6 — Unknown error 56', + 'ERR05_bit7': 'ERR05.7 — Unknown error 57', // ERR06 - 'ERR06_bit0': 'P0068 — MAP/MAF vs Throttle Position Correlation', - 'ERR06_bit1': 'P0101 — MAF Sensor Range/Performance', - 'ERR06_bit2': 'P0102 — MAF Sensor Low Input', - 'ERR06_bit3': 'P0103 — MAF Sensor High Input', - 'ERR06_bit4': 'P0171 — System Lean (bank 1)', - 'ERR06_bit5': 'P0172 — System Rich (bank 1)', - 'ERR06_bit6': 'P0174 — System Lean (bank 2)', - 'ERR06_bit7': 'P0175 — System Rich (bank 2)', + 'ERR06_bit0': 'P1162 — Air Fuel Ratio Sensor Circuit Malfunction', + 'ERR06_bit1': 'ERR06.1 — LAF Sensor AFV1 Short', + 'ERR06_bit2': 'ERR06.2 — LAF Sensor AFC1 Short', + 'ERR06_bit3': 'ERR06.3 — Unknown error 63', + 'ERR06_bit4': + 'ERR06.4 — Daughter Board Communication Error (battery voltage low when cranking)', + 'ERR06_bit5': 'ERR06.5 — Unknown error 65', + 'ERR06_bit6': + 'P0600 — Multiplexer Not Present (for engine swaps disable in misc parameters)', + 'ERR06_bit7': 'ERR06.7 — Unknown error 67', // ERR07 - 'ERR07_bit0': 'P0325 — Knock Sensor', - 'ERR07_bit1': 'P0330 — Knock Sensor 2', - 'ERR07_bit2': 'P0339 — CKP Sensor Intermittent', - 'ERR07_bit3': 'P0344 — CMP Sensor Intermittent', - 'ERR07_bit4': 'P0365 — CMP Sensor B Circuit', - 'ERR07_bit5': 'P0390 — CMP Sensor B Circuit (bank 2)', - 'ERR07_bit6': 'P0498 — EVAP Canister Vent Shut Valve Low', - 'ERR07_bit7': 'P0499 — EVAP Canister Vent Shut Valve High', + 'ERR07_bit0': 'P1505 — PCV Negative Pressure', + 'ERR07_bit1': 'ERR07.1 — Unknown error 71', + 'ERR07_bit2': 'ERR07.2 — Unknown error 72', + 'ERR07_bit3': 'ERR07.3 — Idle Valve', + 'ERR07_bit4': 'P0135 — Primary HO2S Circuit Malfunction', + 'ERR07_bit5': 'P1166 — Primary HO2S Heater Circuit Malfunction', + 'ERR07_bit6': 'P1167 — Primary LAF HO2S Heater System Malfunction', + 'ERR07_bit7': 'P0134 — Air/Fuel Ratio Sensor No Activity Detected', // ERR08 - 'ERR08_bit0': 'P0600 — Serial Communication Link', - 'ERR08_bit1': 'P0601 — Internal Control Module Memory', - 'ERR08_bit2': 'P0604 — Internal Control Module RAM', - 'ERR08_bit3': 'P0605 — Internal Control Module ROM', - 'ERR08_bit4': 'P0657 — Actuator Supply Voltage A Open', - 'ERR08_bit5': 'P0685 — ECM/PCM Power Relay Control Open', - 'ERR08_bit6': 'P0691 — Fan 1 Control Circuit Low', - 'ERR08_bit7': 'P0692 — Fan 1 Control Circuit High', + 'ERR08_bit0': 'P0172 — Fuel System Too Rich', + 'ERR08_bit1': 'P0171 — Fuel System Too Lean', + 'ERR08_bit2': 'P1162 — Air/Fuel Ratio (A/F) Sensor Slow Response', + 'ERR08_bit3': 'ERR08.3 — Unknown error 83', + 'ERR08_bit4': 'P0010 — VTC Oil Control Solenoid Valve Malfunction', + 'ERR08_bit5': 'P0011 — VTC System Malfunction', + 'ERR08_bit6': 'P0340 — CMP Sensor No Signal', + 'ERR08_bit7': 'P0344 — CMP Sensor Intermittent Interruption', // ERR09 - 'ERR09_bit0': 'P0693 — Fan 2 Control Circuit Low', - 'ERR09_bit1': 'P0694 — Fan 2 Control Circuit High', - 'ERR09_bit2': 'P1454 — EVAP Pressure Sensor Range/Performance', - 'ERR09_bit3': 'P1455 — EVAP Leak (small)', - 'ERR09_bit4': 'P1508 — IAC Valve Circuit Low', - 'ERR09_bit5': 'P1509 — IAC Valve Circuit High', - 'ERR09_bit6': 'P1519 — Idle Air Control Valve Stuck', - 'ERR09_bit7': 'P1706 — A/T Range Switch Out Of Range', + 'ERR09_bit0': 'P0341 — Variable Valve Timing (VTC) Phase Gap', + 'ERR09_bit1': 'ERR09.1 — Unknown error 91', + 'ERR09_bit2': 'ERR09.2 — Undocumented code', + 'ERR09_bit3': 'P1163 — Air Fuel Ratio Sensor Slow Response', + 'ERR09_bit4': 'P1164 — Air Fuel Ratio Sensor Range/Performance Problem', + 'ERR09_bit5': 'ERR09.5 — Undocumented code', + 'ERR09_bit6': 'P0137 — Secondary O2 Circuit Low Voltage', + 'ERR09_bit7': 'P0138 — Secondary O2 Circuit High Voltage', // ERR10 - 'ERR10_bit0': 'P2101 — Throttle Actuator Control Motor Range', - 'ERR10_bit1': 'P2118 — Throttle Actuator Control Motor Current Range', - 'ERR10_bit2': 'P2119 — Throttle Actuator Control Throttle Body Range', - 'ERR10_bit3': 'P2122 — Throttle/Pedal Position Sensor D Low Input', - 'ERR10_bit4': 'P2123 — Throttle/Pedal Position Sensor D High Input', - 'ERR10_bit5': 'P2127 — Throttle/Pedal Position Sensor E Low Input', - 'ERR10_bit6': 'P2128 — Throttle/Pedal Position Sensor E High Input', - 'ERR10_bit7': 'P2135 — Throttle/Pedal Position Sensor A/B Voltage Correlation', + 'ERR10_bit0': 'P0139 — Secondary O2 Circuit Slow Response', + 'ERR10_bit1': 'ERR10.1 — Unknown error 101', + 'ERR10_bit2': 'P0141 — Secondary O2 Circuit Heater Circuit Malfunction', + 'ERR10_bit3': 'ERR10.3 — Unknown error 103', + 'ERR10_bit4': 'P0420 — Catalyst System Efficiency Below Threshold', + 'ERR10_bit5': 'ERR10.5 — Catalyst / Secondary O2 Sensor', + 'ERR10_bit6': 'ERR10.6 — Unknown error 106', + 'ERR10_bit7': 'ERR10.7 — Unknown error 107', // ERR11 - 'ERR11_bit0': 'P2138 — Throttle/Pedal Position Sensor D/E Voltage Correlation', - 'ERR11_bit1': 'P2176 — Throttle Actuator Control System Idle Position Not Learned', - 'ERR11_bit2': 'P2227 — Barometric Pressure Sensor A Circuit Range', - 'ERR11_bit3': 'P2228 — Barometric Pressure Sensor A Circuit Low', - 'ERR11_bit4': 'P2229 — Barometric Pressure Sensor A Circuit High', - 'ERR11_bit5': 'P2610 — ECM/PCM Internal Engine Off Timer', - 'ERR11_bit6': 'P2647 — VTEC System High (bank 1)', - 'ERR11_bit7': 'P2648 — VTEC System Low (bank 1)', + 'ERR11_bit0': 'P0301 — Cylinder #1 Misfire', + 'ERR11_bit1': 'ERR11.1 — Unknown error 111', + 'ERR11_bit2': 'P0302 — Cylinder #2 Misfire', + 'ERR11_bit3': 'ERR11.3 — Undocumented code', + 'ERR11_bit4': 'P0303 — Cylinder #3 Misfire', + 'ERR11_bit5': 'ERR11.5 — Unknown error 115', + 'ERR11_bit6': 'P0304 — Cylinder #4 Misfire', + 'ERR11_bit7': 'ERR11.7 — Unknown error 117', // ERR12 - 'ERR12_bit0': 'P2649 — VTEC System High (bank 2)', - 'ERR12_bit1': 'P2650 — VTEC System Low (bank 2)', - 'ERR12_bit2': 'P3400 — VTEC System Stuck Off (bank 1)', - 'ERR12_bit3': 'P3497 — VTEC System Stuck Off (bank 2)', - 'ERR12_bit4': 'U0073 — Control Module Communication Bus Off', - 'ERR12_bit5': 'U0100 — Lost Communication With ECM/PCM A', - 'ERR12_bit6': 'U0155 — Lost Communication With Instrument Panel Cluster', - 'ERR12_bit7': 'U0164 — Lost Communication With HVAC Control Module', + 'ERR12_bit0': 'P0300 — Random Misfire', + 'ERR12_bit1': 'ERR12.1 — Unknown error 121', + 'ERR12_bit2': 'ERR12.2 — EGR Flow', + 'ERR12_bit3': 'ERR12.3 — Unknown error 123', + 'ERR12_bit4': 'P0116 — ECT Sensor Range/Performance Problem', + 'ERR12_bit5': 'ERR12.5 — Unknown error 125', + 'ERR12_bit6': 'P0128 — Cooling System Malfunction (Thermostat)', + 'ERR12_bit7': 'ERR12.7 — VTC Solenoid', // ERR13 - 'ERR13_bit0': 'P0011 — Camshaft Position A — Timing Over-Advanced (bank 1)', - 'ERR13_bit1': 'P0012 — Camshaft Position A — Timing Over-Retarded (bank 1)', - 'ERR13_bit2': 'P0021 — Camshaft Position B — Timing Over-Advanced (bank 1)', - 'ERR13_bit3': 'P0022 — Camshaft Position B — Timing Over-Retarded (bank 1)', - 'ERR13_bit4': 'P0340 — CMP Sensor A Circuit (bank 1)', - 'ERR13_bit5': 'P0345 — CMP Sensor A Circuit (bank 2)', - 'ERR13_bit6': 'P0366 — CMP Sensor B Circuit Range/Performance (bank 1)', - 'ERR13_bit7': 'P0391 — CMP Sensor B Circuit Range/Performance (bank 2)', + 'ERR13_bit0': 'P1456 — EVAP Control System Leakage (Fuel Tank)', + 'ERR13_bit1': 'P1457 — EVAP Control System Leakage (Canister System)', + 'ERR13_bit2': 'P0452 — Fuel Tank Pressure Sensor Low Voltage', + 'ERR13_bit3': 'P0453 — Fuel Tank Pressure Sensor High Voltage', + 'ERR13_bit4': 'P0451 — Fuel Tank Pressure Sensor Range/Performance', + 'ERR13_bit5': 'ERR13.5 — Undocumented code', + 'ERR13_bit6': 'P0443 — EVAP Purge Valve Circuit', + 'ERR13_bit7': 'ERR13.7 — Unknown error 137', // ERR14 - 'ERR14_bit0': 'P0016 — Crankshaft/Camshaft Position Correlation (bank 1, sensor A)', - 'ERR14_bit1': 'P0017 — Crankshaft/Camshaft Position Correlation (bank 1, sensor B)', - 'ERR14_bit2': 'P0018 — Crankshaft/Camshaft Position Correlation (bank 2, sensor A)', - 'ERR14_bit3': 'P0019 — Crankshaft/Camshaft Position Correlation (bank 2, sensor B)', - 'ERR14_bit4': 'P0008 — Engine Position System (bank 1)', - 'ERR14_bit5': 'P0009 — Engine Position System (bank 2)', - 'ERR14_bit6': 'P0014 — Camshaft Position B — Timing Over-Advanced (bank 1)', - 'ERR14_bit7': 'P0024 — Camshaft Position B — Timing Over-Advanced (bank 2)', + 'ERR14_bit0': 'ERR14.0 — PF2 Low', + 'ERR14_bit1': 'ERR14.1 — PF2 High', + 'ERR14_bit2': 'ERR14.2 — PF2 Monitor', + 'ERR14_bit3': 'ERR14.3 — Undocumented code', + 'ERR14_bit4': 'ERR14.4 — TF2 Low', + 'ERR14_bit5': 'ERR14.5 — TF2 High', + 'ERR14_bit6': 'ERR14.6 — PF0 Low', + 'ERR14_bit7': 'ERR14.7 — PF0 High', // ERR15 - 'ERR15_bit0': 'P0087 — Fuel Rail/System Pressure Too Low', - 'ERR15_bit1': 'P0088 — Fuel Rail/System Pressure Too High', - 'ERR15_bit2': 'P0089 — Fuel Pressure Regulator Performance', - 'ERR15_bit3': 'P0090 — Fuel Pressure Regulator Control Circuit', - 'ERR15_bit4': 'P0091 — Fuel Pressure Regulator Control Circuit Low', - 'ERR15_bit5': 'P0092 — Fuel Pressure Regulator Control Circuit High', - 'ERR15_bit6': 'P0201 — Injector Circuit/Open Cylinder 1', - 'ERR15_bit7': 'P0202 — Injector Circuit/Open Cylinder 2', + 'ERR15_bit0': 'ERR15.0 — TF0 Low', + 'ERR15_bit1': 'ERR15.1 — TF0 High', + 'ERR15_bit2': 'ERR15.2 — 3rd O2 Sensor Voltage Low', + 'ERR15_bit3': 'ERR15.3 — 3rd O2 Sensor Voltage High', + 'ERR15_bit4': 'ERR15.4 — 3rd O2 Sensor Response', + 'ERR15_bit5': 'ERR15.5 — Unknown error 155', + 'ERR15_bit6': 'ERR15.6 — 3rd O2 Sensor Heater Current Abnormality', + 'ERR15_bit7': 'ERR15.7 — Unknown error 157', // ERR16 - 'ERR16_bit0': 'P0203 — Injector Circuit/Open Cylinder 3', - 'ERR16_bit1': 'P0204 — Injector Circuit/Open Cylinder 4', - 'ERR16_bit2': 'P0261 — Injector Circuit Low Cylinder 1', - 'ERR16_bit3': 'P0264 — Injector Circuit Low Cylinder 2', - 'ERR16_bit4': 'P0267 — Injector Circuit Low Cylinder 3', - 'ERR16_bit5': 'P0270 — Injector Circuit Low Cylinder 4', - 'ERR16_bit6': 'P0262 — Injector Circuit High Cylinder 1', - 'ERR16_bit7': 'P0265 — Injector Circuit High Cylinder 2', + 'ERR16_bit0': 'ERR16.0 — NOx Ability Monitor', + 'ERR16_bit1': 'ERR16.1 — Unknown error 161', + 'ERR16_bit2': + 'P1077 — Intake Manifold Runner Control System Malfunction (low rpm)', + 'ERR16_bit3': + 'P1078 — Intake Manifold Runner Control System Malfunction (high rpm)', + 'ERR16_bit4': 'P0661 — Intake Manifold Runner Control Low Voltage', + 'ERR16_bit5': 'P0662 — Intake Manifold Runner Control High Voltage', + 'ERR16_bit6': 'ERR16.6 — P1 Switch Abnormality', + 'ERR16_bit7': 'ERR16.7 — Unknown error 167', // ERR17 - 'ERR17_bit0': 'P0268 — Injector Circuit High Cylinder 3', - 'ERR17_bit1': 'P0271 — Injector Circuit High Cylinder 4', - 'ERR17_bit2': 'P0351 — Ignition Coil A Primary/Secondary Circuit', - 'ERR17_bit3': 'P0352 — Ignition Coil B Primary/Secondary Circuit', - 'ERR17_bit4': 'P0353 — Ignition Coil C Primary/Secondary Circuit', - 'ERR17_bit5': 'P0354 — Ignition Coil D Primary/Secondary Circuit', - 'ERR17_bit6': 'P0627 — Fuel Pump Control Circuit Open', - 'ERR17_bit7': 'P0628 — Fuel Pump Control Circuit Low', + 'ERR17_bit0': 'P0219 — Engine Over-Rev', + 'ERR17_bit1': 'P0217 — Engine Over-Heating', + 'ERR17_bit2': 'ERR17.2 — Undocumented code', + 'ERR17_bit3': 'ERR17.3 — Undocumented code', + 'ERR17_bit4': 'ERR17.4 — Undocumented code', + 'ERR17_bit5': 'ERR17.5 — Undocumented code', + 'ERR17_bit6': 'ERR17.6 — Undocumented code', + 'ERR17_bit7': 'ERR17.7 — Undocumented code', }; /// Returns a list of active DTC descriptions from the given raw error bytes diff --git a/lib/core/providers/bt_provider.dart b/lib/core/providers/bt_provider.dart index f808d71..fae74fe 100644 --- a/lib/core/providers/bt_provider.dart +++ b/lib/core/providers/bt_provider.dart @@ -149,6 +149,14 @@ class BtNotifier extends StateNotifier { Future connect(BluetoothDevice device) async { if (state.isConnecting) return; // ignore double taps final session = ++_session; + // The adapter's name says which ECU it is wired to; a stale saved + // setting for the other ECU makes every request go unanswered. + final inferred = ecuTypeFromDeviceName(device.name); + if (inferred != null && inferred != _ref.read(settingsProvider).ecuType) { + AppLog.i('ECU protocol set to ${inferred.name} from adapter name ' + '"${device.name}"'); + _ref.read(settingsProvider.notifier).setEcuType(inferred); + } _device = device; _userDisconnected = false; _resetCounters(); @@ -369,19 +377,93 @@ class BtNotifier extends StateNotifier { ); } - /// DTC reset per hvbt_protocol.pdf: { 0x1B, 0x70, NEG8 }; the adapter echoes - /// the same 3 bytes as acknowledgement. - Future<({bool acknowledged, Uint8List raw})> clearDtcs() async { + /// DTC reset per hvbt_protocol.pdf: { 0x1B, 0x70, NEG8 }; the adapter should + /// echo the same 3 bytes as acknowledgement. + /// + /// The adapter never sends that ack (confirmed on the bench), so success is + /// judged from the data: after sending, polling resumes and the ERR bytes + /// of the live frames are watched for [_dtcWatch]. If any code that was + /// stored before disappears, the ECU cleared. Codes for faults that are + /// still present come straight back (often before the first frame), just + /// as with the USB tool, so "all zero" is not required. The ack wait is + /// kept short because the adapter stops reading the ECU after ~1 s without + /// datalog requests (Bluetooth.pdf §2.4). + /// + /// [codesBefore]/[codesCleared] count stored code bits before the command + /// and how many of them were seen cleared. + Future< + ({ + bool acknowledged, + int codesBefore, + int codesCleared, + Uint8List raw, + })> clearDtcs() async { final p = _poller; - if (p == null) return (acknowledged: false, raw: Uint8List(0)); + if (p == null) { + return ( + acknowledged: false, + codesBefore: 0, + codesCleared: 0, + raw: Uint8List(0), + ); + } + final ecuType = _ref.read(settingsProvider).ecuType; + // ERR00.. bytes: S300 37-40, KPro 62-79 (hvbt_protocol.pdf). + List errOf(Uint8List f) => + ecuType == EcuType.s300 ? f.sublist(37, 41) : f.sublist(62, 80); + String hex(List b) => + b.map((v) => v.toRadixString(16).padLeft(2, '0')).join(' '); + int bits(int v) => v.toRadixString(2).replaceAll('0', '').length; + + final beforeFrame = state.lastFrame; + final before = beforeFrame == null ? null : errOf(beforeFrame); + final codesBefore = before == null ? 0 : before.fold(0, (n, b) => n + bits(b)); + AppLog.i('Clear DTC: ERR bytes before = ${before == null ? "?" : hex(before)}'); + + // Bits stored before that have been seen clear at least once. + final clearedMask = List.filled(before?.length ?? 0, 0); + String? lastErr = before == null ? null : hex(before); + final watchStart = DateTime.now(); + final sub = frameStream.listen((f) { + final err = errOf(f); + final h = hex(err); + if (h != lastErr) { + final ms = DateTime.now().difference(watchStart).inMilliseconds; + AppLog.i('Clear DTC: +$ms ms ERR bytes = $h'); + lastErr = h; + } + if (before != null) { + for (var i = 0; i < before.length; i++) { + clearedMask[i] |= before[i] & ~err[i]; + } + } + }); + final command = Uint8List.fromList([0x1B, 0x70, 0x00]); command[2] = calculateNeg8(Uint8List.fromList([0x1B, 0x70])); - final result = await p.sendCommandAwaitReply(command, command); + final result = await p.sendCommandAwaitReply( + command, + command, + timeout: const Duration(milliseconds: 600), + ); + await Future.delayed(_dtcWatch); + await sub.cancel(); + + final codesCleared = clearedMask.fold(0, (n, b) => n + bits(b)); AppLog.i('Clear DTC: ack=${result.acknowledged} ' - 'rxBytes=${result.raw.length}'); - return result; + 'rxBytes=${result.raw.length} ' + 'codesCleared=$codesCleared/$codesBefore ' + 'ERR bytes after = ${lastErr ?? "?"}'); + return ( + acknowledged: result.acknowledged, + codesBefore: codesBefore, + codesCleared: codesCleared, + raw: result.raw, + ); } + static const _dtcWatch = Duration(seconds: 5); + @override void dispose() { _statusTimer?.cancel(); diff --git a/lib/ui/screens/dtc/dtc_screen.dart b/lib/ui/screens/dtc/dtc_screen.dart index f4b670f..a1de0db 100644 --- a/lib/ui/screens/dtc/dtc_screen.dart +++ b/lib/ui/screens/dtc/dtc_screen.dart @@ -98,20 +98,29 @@ class _DtcScreenState extends ConsumerState { _lastRaw = result.raw; }); + // Success is judged from the live data: the adapter does not send the + // ack. Codes for faults that are still present return right away. final String message; - if (result.acknowledged) { - message = 'Trouble codes cleared'; + if (result.codesBefore == 0) { + message = 'No stored trouble codes to clear'; + } else if (result.codesCleared > 0) { + final cleared = result.codesCleared; + final back = result.codesBefore - cleared; + message = back == 0 + ? 'Trouble codes cleared' + : '$cleared of ${result.codesBefore} codes cleared — $back came ' + 'straight back (active fault, fix the cause first)'; + } else if (result.acknowledged) { + message = 'ECU confirmed the reset, but every code came straight back ' + '(active faults)'; } else if (result.raw.isEmpty) { - message = 'No response from ECU — codes may not have cleared'; + // Can't tell apart from "every fault re-set before the next frame". + message = 'No codes cleared — the ECU ignored the reset, or every ' + 'fault is still active'; } else { - // The scan runs alongside normal polling now, so "not acknowledged" - // can mean several seconds' worth of regular datalog traffic with no - // ack byte pattern found in it — dumping all of that raw would be - // unusable in a snackbar, so just report how much was seen. "View" - // opens the full hex to check by hand for anything our narrow 3-byte - // pattern match might have missed. - message = - 'No ack seen — ${result.raw.length} bytes of other traffic during the window'; + // "View" opens the raw reply hex to check by hand. + message = 'Unexpected reply (${result.raw.length} bytes) — codes did ' + 'not clear'; } ScaffoldMessenger.of(context).showSnackBar( diff --git a/test/dtc_map_test.dart b/test/dtc_map_test.dart index c2b1b0c..8ae88e8 100644 --- a/test/dtc_map_test.dart +++ b/test/dtc_map_test.dart @@ -7,16 +7,29 @@ void main() { expect(s300DtcMap.length, equals(32)); // 4 bytes × 8 bits }); - test('P0130 maps to correct description for ERR00 bit0', () { - expect(s300DtcMap['ERR00_bit0'], equals('P0130 — O2 Sensor (front)')); + // Expected codes come from the S300 "DTC map" in hvbt_protocol.pdf. + test('ERR00 bit0 = P0130 (O2A)', () { + expect(s300DtcMap['ERR00_bit0'], startsWith('P0130 — ')); }); - test('P0505 maps for ERR00 bit2', () { - expect(s300DtcMap['ERR00_bit2'], equals('P0505 — Idle Air Control Valve')); + test('ERR01 bit5 = P0505 (IAC)', () { + expect(s300DtcMap['ERR01_bit5'], startsWith('P0505 — ')); }); - test('P1607 maps for ERR03 bit7', () { - expect(s300DtcMap['ERR03_bit7'], equals('P1607 — PCM Internal Circuit')); + test('bench codes: ERR01.6 = P0350, ERR02.3 = P1298, ERR02.4 = P2649', () { + expect(s300DtcMap['ERR01_bit6'], startsWith('P0350 — ')); + expect(s300DtcMap['ERR02_bit3'], startsWith('P1298 — ')); + expect(s300DtcMap['ERR02_bit4'], startsWith('P2649 — ')); + }); + + test('undocumented bits show the raw ERR position', () { + expect(s300DtcMap['ERR03_bit7'], startsWith('ERR03.7 — ')); + }); + + test('every entry has a code and a description', () { + for (final v in [...s300DtcMap.values, ...kproDtcMap.values]) { + expect(v.split(' — ').length, equals(2), reason: v); + } }); }); @@ -26,8 +39,11 @@ void main() { expect(kproDtcMap.length, equals(144)); }); - test('P0130 maps for ERR00 bit0', () { - expect(kproDtcMap['ERR00_bit0'], equals('P0130 — O2 Sensor (front)')); + // Expected codes come from the KPro "DTC map" in hvbt_protocol.pdf. + test('ERR00 bit1 = P1607, ERR05 bit0 = P1259, ERR11 bit6 = P0304', () { + expect(kproDtcMap['ERR00_bit1'], startsWith('P1607 — ')); + expect(kproDtcMap['ERR05_bit0'], startsWith('P1259 — ')); + expect(kproDtcMap['ERR11_bit6'], startsWith('P0304 — ')); }); test('ERR17 entries present', () { @@ -44,7 +60,7 @@ void main() { test('decodes ERR00 bit0 = P0130', () { final result = decodeDtcs([0x01, 0, 0, 0], s300DtcMap); - expect(result, contains('P0130 — O2 Sensor (front)')); + expect(result, contains(s300DtcMap['ERR00_bit0'])); }); test('decodes multiple bits in one byte', () { diff --git a/test/ecu_type_from_name_test.dart b/test/ecu_type_from_name_test.dart new file mode 100644 index 0000000..c388b8a --- /dev/null +++ b/test/ecu_type_from_name_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:hvbt_dash/core/bluetooth/bt_poller.dart'; + +void main() { + group('ecuTypeFromDeviceName', () { + test('real S300 adapter names', () { + expect(ecuTypeFromDeviceName('S300[94]-SPP'), EcuType.s300); + expect(ecuTypeFromDeviceName('HVS300-SPP[94]'), EcuType.s300); + }); + + test('KPro names', () { + expect(ecuTypeFromDeviceName('HV-KPro-SPP'), EcuType.kpro); + expect(ecuTypeFromDeviceName('K-PRO 1234'), EcuType.kpro); + }); + + test('unknown or missing names leave the setting alone', () { + expect(ecuTypeFromDeviceName(null), isNull); + expect(ecuTypeFromDeviceName('HC-05'), isNull); + expect(ecuTypeFromDeviceName('S300 / KPro'), isNull); + }); + }); +}