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 <noreply@anthropic.com>
This commit is contained in:
metatroncubeswdev 2026-10-12 23:08:29 -04:00
parent 2bd937aca5
commit ba79775fbc
6 changed files with 367 additions and 218 deletions

View File

@ -7,6 +7,18 @@ import 'bt_service.dart';
enum EcuType { s300, kpro } 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<int> rxBuf); typedef _Matcher = Uint8List? Function(List<int> rxBuf);
class _Pending { class _Pending {

View File

@ -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). /// Key format: 'ERRxx_bity' where xx = ERR byte index (00-based), y = bit
/// Value: 'Pxxxx — description' /// number (0=LSB). Value: '<code> — description'. Bits the PDF leaves
/// undocumented ("???") use 'ERRxx.y' as the code so the raw bit is still
/// visible on screen.
library; 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<String, String> s300DtcMap = { const Map<String, String> 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<String, String> kproDtcMap = {
// ERR00 // ERR00
'ERR00_bit0': 'P0130 — O2 Sensor (front)', 'ERR00_bit0': 'P0130 — Oxygen Sensor #1 (O2A, code 1)',
'ERR00_bit1': 'P0135 — O2 Sensor Heater (front)', 'ERR00_bit1': 'P0150 — Oxygen Sensor #2 (O2B, code 2)',
'ERR00_bit2': 'P0505 — Idle Air Control Valve', 'ERR00_bit2': 'P0105 — Manifold Absolute Pressure Sensor (MAP, code 3)',
'ERR00_bit3': 'P0108 — MAP Sensor High', 'ERR00_bit3': 'P0335 — Crank Position Sensor (CKP, code 4)',
'ERR00_bit4': 'P0107 — MAP Sensor Low', 'ERR00_bit4': 'P0105 — Manifold Absolute Pressure Sensor (MAP, code 5)',
'ERR00_bit5': 'P0113 — IAT Sensor High', 'ERR00_bit5': 'P0106 — Engine Coolant Sensor (ECT, code 6)',
'ERR00_bit6': 'P0112 — IAT Sensor Low', 'ERR00_bit6': 'P0123 — Throttle Position Sensor (TPS, code 7)',
'ERR00_bit7': 'P0118 — ECT Sensor High', 'ERR00_bit7': 'P1362 — Top Dead Center Sensor (TDC, code 8)',
// ERR01 // ERR01
'ERR01_bit0': 'P0117 — ECT Sensor Low', 'ERR01_bit0': 'P0340 — Cylinder/Cam Sensor (CYL, code 9)',
'ERR01_bit1': 'P0336 — CKP Sensor', 'ERR01_bit1': 'P0113 — Intake Air Temperature Sensor (IAT, code 10)',
'ERR01_bit2': 'P0341 — CMP Sensor', 'ERR01_bit2': 'P0217 — Engine Overheating (OH, code 11)',
'ERR01_bit3': 'P1362 — TDC Sensor (No Signal)', 'ERR01_bit3': 'P0403 — EGR Lift Valve (EGR, code 12)',
'ERR01_bit4': 'P1363 — TDC Sensor (No Signal 2)', 'ERR01_bit4': 'P1106 — Atmospheric Pressure Sensor (BARO, code 13)',
'ERR01_bit5': 'P0335 — CKP Sensor (No Signal)', 'ERR01_bit5': 'P0505 — Idle Air Control Valve (IAC, code 14)',
'ERR01_bit6': 'P1361 — TDC Sensor', 'ERR01_bit6': 'P0350 — Ignition Control Module (IGM, code 15)',
'ERR01_bit7': 'P0300 — Random Misfire', 'ERR01_bit7': 'P0200 — Fuel Injectors (INJ, code 16)',
// ERR02 // ERR02
'ERR02_bit0': 'P0301 — Misfire Cylinder 1', 'ERR02_bit0': 'P0500 — Vehicle Speed Sensor (VSS, code 17)',
'ERR02_bit1': 'P0302 — Misfire Cylinder 2', 'ERR02_bit1': 'ERR02.1 — Undocumented code',
'ERR02_bit2': 'P0303 — Misfire Cylinder 3', 'ERR02_bit2': 'P0700 — Automatic Transmission Lockup Control Valve (code 19)',
'ERR02_bit3': 'P0304 — Misfire Cylinder 4', 'ERR02_bit3': 'P1298 — Electrical Load Detector (ELD, code 20)',
'ERR02_bit4': 'P1259 — VTEC System Malfunction', 'ERR02_bit4': 'P2649 — VTEC Solenoid (VTS, code 21)',
'ERR02_bit5': 'P0420 — Catalyst System Low Efficiency', 'ERR02_bit5': 'P2647 — VTEC Pressure Valve (VTP, code 22)',
'ERR02_bit6': 'P0401 — EGR Insufficient Flow', 'ERR02_bit6': 'P0325 — Knock Sensor (code 23)',
'ERR02_bit7': 'P1456 — EVAP Leak (large) Fuel Tank Side', 'ERR02_bit7': 'P1324 — Knock Sensor (code 24)',
// ERR03 // ERR03
'ERR03_bit0': 'P1457 — EVAP Leak (large) Canister Side', 'ERR03_bit0': 'P0750 — Automatic Transmission A Signal (ATA, code 30)',
'ERR03_bit1': 'P0498 — EVAP Canister Vent Shut Valve', 'ERR03_bit1': 'P0755 — Automatic Transmission B Signal (ATB, code 31)',
'ERR03_bit2': 'P0497 — EVAP Low Purge Flow', 'ERR03_bit2': 'P0135 — Primary Oxygen Sensor Heater (PO2H, code 41)',
'ERR03_bit3': 'P0122 — TPS Sensor Low', 'ERR03_bit3': 'P0135 — Oxygen Sensor Heater',
'ERR03_bit4': 'P0123 — TPS Sensor High', 'ERR03_bit4': 'P0169 — Fuel Supply System (code 43)',
'ERR03_bit5': 'P0562 — Battery Voltage Low', 'ERR03_bit5': 'ERR03.5 — Undocumented code',
'ERR03_bit6': 'P0563 — Battery Voltage High', 'ERR03_bit6': 'ERR03.6 — Undocumented code',
'ERR03_bit7': 'P1607 — PCM Internal Circuit', 'ERR03_bit7': 'ERR03.7 — Undocumented code',
};
/// KPro DTC map (ERR00–ERR17 = frame bytes 62–79).
const Map<String, String> 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
'ERR04_bit0': 'P0136 — O2 Sensor (rear)', 'ERR04_bit0': 'P0505 — Idle Control System Malfunction',
'ERR04_bit1': 'P0141 — O2 Sensor Heater (rear)', 'ERR04_bit1': 'P1519 — Idle Air Control Valve Circuit Malfunction',
'ERR04_bit2': 'P1166 — Air Fuel Ratio (A/F) Sensor Heater', 'ERR04_bit2': 'P0500 — VSS Malfunction',
'ERR04_bit3': 'P1167 — Air Fuel Ratio (A/F) Sensor', 'ERR04_bit3': 'ERR04.3 — VSP',
'ERR04_bit4': 'P2195 — A/F Sensor Signal Stuck Lean', 'ERR04_bit4': 'P1297 — ELD Circuit Low Voltage',
'ERR04_bit5': 'P2196 — A/F Sensor Signal Stuck Rich', 'ERR04_bit5': 'P1298 — ELD Circuit High Voltage',
'ERR04_bit6': 'P2237 — A/F Sensor Pump Cell Current Input Low', 'ERR04_bit6': 'P1253 — VTEC Solenoid Drive Time Abnormality',
'ERR04_bit7': 'P2240 — A/F Sensor Pump Cell Current Input High', 'ERR04_bit7': 'ERR04.7 — Unknown error 47',
// ERR05 // ERR05
'ERR05_bit0': 'P2243 — A/F Sensor Reference Voltage Low', 'ERR05_bit0': 'P1259 — VTEC System Malfunction (Oil Pressure Switch)',
'ERR05_bit1': 'P2247 — A/F Sensor Reference Voltage High', 'ERR05_bit1': 'ERR05.1 — Unknown error 51',
'ERR05_bit2': 'P2251 — A/F Sensor VS Cell Voltage Low', 'ERR05_bit2': 'P0325 — Knock Sensor Circuit Malfunction',
'ERR05_bit3': 'P2254 — A/F Sensor VS Cell Voltage High', 'ERR05_bit3': 'P1324 — Knock Sensor Circuit Failure',
'ERR05_bit4': 'P0031 — O2 Sensor Heater Current Low (bank 1, sensor 1)', 'ERR05_bit4': 'P0563 — VBU Voltage High',
'ERR05_bit5': 'P0032 — O2 Sensor Heater Current High (bank 1, sensor 1)', 'ERR05_bit5': 'ERR05.5 — Unknown error 55',
'ERR05_bit6': 'P0037 — O2 Sensor Heater Current Low (bank 1, sensor 2)', 'ERR05_bit6': 'ERR05.6 — Unknown error 56',
'ERR05_bit7': 'P0038 — O2 Sensor Heater Current High (bank 1, sensor 2)', 'ERR05_bit7': 'ERR05.7 — Unknown error 57',
// ERR06 // ERR06
'ERR06_bit0': 'P0068 — MAP/MAF vs Throttle Position Correlation', 'ERR06_bit0': 'P1162 — Air Fuel Ratio Sensor Circuit Malfunction',
'ERR06_bit1': 'P0101 — MAF Sensor Range/Performance', 'ERR06_bit1': 'ERR06.1 — LAF Sensor AFV1 Short',
'ERR06_bit2': 'P0102 — MAF Sensor Low Input', 'ERR06_bit2': 'ERR06.2 — LAF Sensor AFC1 Short',
'ERR06_bit3': 'P0103 — MAF Sensor High Input', 'ERR06_bit3': 'ERR06.3 — Unknown error 63',
'ERR06_bit4': 'P0171 — System Lean (bank 1)', 'ERR06_bit4':
'ERR06_bit5': 'P0172 — System Rich (bank 1)', 'ERR06.4 — Daughter Board Communication Error (battery voltage low when cranking)',
'ERR06_bit6': 'P0174 — System Lean (bank 2)', 'ERR06_bit5': 'ERR06.5 — Unknown error 65',
'ERR06_bit7': 'P0175 — System Rich (bank 2)', 'ERR06_bit6':
'P0600 — Multiplexer Not Present (for engine swaps disable in misc parameters)',
'ERR06_bit7': 'ERR06.7 — Unknown error 67',
// ERR07 // ERR07
'ERR07_bit0': 'P0325 — Knock Sensor', 'ERR07_bit0': 'P1505 — PCV Negative Pressure',
'ERR07_bit1': 'P0330 — Knock Sensor 2', 'ERR07_bit1': 'ERR07.1 — Unknown error 71',
'ERR07_bit2': 'P0339 — CKP Sensor Intermittent', 'ERR07_bit2': 'ERR07.2 — Unknown error 72',
'ERR07_bit3': 'P0344 — CMP Sensor Intermittent', 'ERR07_bit3': 'ERR07.3 — Idle Valve',
'ERR07_bit4': 'P0365 — CMP Sensor B Circuit', 'ERR07_bit4': 'P0135 — Primary HO2S Circuit Malfunction',
'ERR07_bit5': 'P0390 — CMP Sensor B Circuit (bank 2)', 'ERR07_bit5': 'P1166 — Primary HO2S Heater Circuit Malfunction',
'ERR07_bit6': 'P0498 — EVAP Canister Vent Shut Valve Low', 'ERR07_bit6': 'P1167 — Primary LAF HO2S Heater System Malfunction',
'ERR07_bit7': 'P0499 — EVAP Canister Vent Shut Valve High', 'ERR07_bit7': 'P0134 — Air/Fuel Ratio Sensor No Activity Detected',
// ERR08 // ERR08
'ERR08_bit0': 'P0600 — Serial Communication Link', 'ERR08_bit0': 'P0172 — Fuel System Too Rich',
'ERR08_bit1': 'P0601 — Internal Control Module Memory', 'ERR08_bit1': 'P0171 — Fuel System Too Lean',
'ERR08_bit2': 'P0604 — Internal Control Module RAM', 'ERR08_bit2': 'P1162 — Air/Fuel Ratio (A/F) Sensor Slow Response',
'ERR08_bit3': 'P0605 — Internal Control Module ROM', 'ERR08_bit3': 'ERR08.3 — Unknown error 83',
'ERR08_bit4': 'P0657 — Actuator Supply Voltage A Open', 'ERR08_bit4': 'P0010 — VTC Oil Control Solenoid Valve Malfunction',
'ERR08_bit5': 'P0685 — ECM/PCM Power Relay Control Open', 'ERR08_bit5': 'P0011 — VTC System Malfunction',
'ERR08_bit6': 'P0691 — Fan 1 Control Circuit Low', 'ERR08_bit6': 'P0340 — CMP Sensor No Signal',
'ERR08_bit7': 'P0692 — Fan 1 Control Circuit High', 'ERR08_bit7': 'P0344 — CMP Sensor Intermittent Interruption',
// ERR09 // ERR09
'ERR09_bit0': 'P0693 — Fan 2 Control Circuit Low', 'ERR09_bit0': 'P0341 — Variable Valve Timing (VTC) Phase Gap',
'ERR09_bit1': 'P0694 — Fan 2 Control Circuit High', 'ERR09_bit1': 'ERR09.1 — Unknown error 91',
'ERR09_bit2': 'P1454 — EVAP Pressure Sensor Range/Performance', 'ERR09_bit2': 'ERR09.2 — Undocumented code',
'ERR09_bit3': 'P1455 — EVAP Leak (small)', 'ERR09_bit3': 'P1163 — Air Fuel Ratio Sensor Slow Response',
'ERR09_bit4': 'P1508 — IAC Valve Circuit Low', 'ERR09_bit4': 'P1164 — Air Fuel Ratio Sensor Range/Performance Problem',
'ERR09_bit5': 'P1509 — IAC Valve Circuit High', 'ERR09_bit5': 'ERR09.5 — Undocumented code',
'ERR09_bit6': 'P1519 — Idle Air Control Valve Stuck', 'ERR09_bit6': 'P0137 — Secondary O2 Circuit Low Voltage',
'ERR09_bit7': 'P1706 — A/T Range Switch Out Of Range', 'ERR09_bit7': 'P0138 — Secondary O2 Circuit High Voltage',
// ERR10 // ERR10
'ERR10_bit0': 'P2101 — Throttle Actuator Control Motor Range', 'ERR10_bit0': 'P0139 — Secondary O2 Circuit Slow Response',
'ERR10_bit1': 'P2118 — Throttle Actuator Control Motor Current Range', 'ERR10_bit1': 'ERR10.1 — Unknown error 101',
'ERR10_bit2': 'P2119 — Throttle Actuator Control Throttle Body Range', 'ERR10_bit2': 'P0141 — Secondary O2 Circuit Heater Circuit Malfunction',
'ERR10_bit3': 'P2122 — Throttle/Pedal Position Sensor D Low Input', 'ERR10_bit3': 'ERR10.3 — Unknown error 103',
'ERR10_bit4': 'P2123 — Throttle/Pedal Position Sensor D High Input', 'ERR10_bit4': 'P0420 — Catalyst System Efficiency Below Threshold',
'ERR10_bit5': 'P2127 — Throttle/Pedal Position Sensor E Low Input', 'ERR10_bit5': 'ERR10.5 — Catalyst / Secondary O2 Sensor',
'ERR10_bit6': 'P2128 — Throttle/Pedal Position Sensor E High Input', 'ERR10_bit6': 'ERR10.6 — Unknown error 106',
'ERR10_bit7': 'P2135 — Throttle/Pedal Position Sensor A/B Voltage Correlation', 'ERR10_bit7': 'ERR10.7 — Unknown error 107',
// ERR11 // ERR11
'ERR11_bit0': 'P2138 — Throttle/Pedal Position Sensor D/E Voltage Correlation', 'ERR11_bit0': 'P0301 — Cylinder #1 Misfire',
'ERR11_bit1': 'P2176 — Throttle Actuator Control System Idle Position Not Learned', 'ERR11_bit1': 'ERR11.1 — Unknown error 111',
'ERR11_bit2': 'P2227 — Barometric Pressure Sensor A Circuit Range', 'ERR11_bit2': 'P0302 — Cylinder #2 Misfire',
'ERR11_bit3': 'P2228 — Barometric Pressure Sensor A Circuit Low', 'ERR11_bit3': 'ERR11.3 — Undocumented code',
'ERR11_bit4': 'P2229 — Barometric Pressure Sensor A Circuit High', 'ERR11_bit4': 'P0303 — Cylinder #3 Misfire',
'ERR11_bit5': 'P2610 — ECM/PCM Internal Engine Off Timer', 'ERR11_bit5': 'ERR11.5 — Unknown error 115',
'ERR11_bit6': 'P2647 — VTEC System High (bank 1)', 'ERR11_bit6': 'P0304 — Cylinder #4 Misfire',
'ERR11_bit7': 'P2648 — VTEC System Low (bank 1)', 'ERR11_bit7': 'ERR11.7 — Unknown error 117',
// ERR12 // ERR12
'ERR12_bit0': 'P2649 — VTEC System High (bank 2)', 'ERR12_bit0': 'P0300 — Random Misfire',
'ERR12_bit1': 'P2650 — VTEC System Low (bank 2)', 'ERR12_bit1': 'ERR12.1 — Unknown error 121',
'ERR12_bit2': 'P3400 — VTEC System Stuck Off (bank 1)', 'ERR12_bit2': 'ERR12.2 — EGR Flow',
'ERR12_bit3': 'P3497 — VTEC System Stuck Off (bank 2)', 'ERR12_bit3': 'ERR12.3 — Unknown error 123',
'ERR12_bit4': 'U0073 — Control Module Communication Bus Off', 'ERR12_bit4': 'P0116 — ECT Sensor Range/Performance Problem',
'ERR12_bit5': 'U0100 — Lost Communication With ECM/PCM A', 'ERR12_bit5': 'ERR12.5 — Unknown error 125',
'ERR12_bit6': 'U0155 — Lost Communication With Instrument Panel Cluster', 'ERR12_bit6': 'P0128 — Cooling System Malfunction (Thermostat)',
'ERR12_bit7': 'U0164 — Lost Communication With HVAC Control Module', 'ERR12_bit7': 'ERR12.7 — VTC Solenoid',
// ERR13 // ERR13
'ERR13_bit0': 'P0011 — Camshaft Position A — Timing Over-Advanced (bank 1)', 'ERR13_bit0': 'P1456 — EVAP Control System Leakage (Fuel Tank)',
'ERR13_bit1': 'P0012 — Camshaft Position A — Timing Over-Retarded (bank 1)', 'ERR13_bit1': 'P1457 — EVAP Control System Leakage (Canister System)',
'ERR13_bit2': 'P0021 — Camshaft Position B — Timing Over-Advanced (bank 1)', 'ERR13_bit2': 'P0452 — Fuel Tank Pressure Sensor Low Voltage',
'ERR13_bit3': 'P0022 — Camshaft Position B — Timing Over-Retarded (bank 1)', 'ERR13_bit3': 'P0453 — Fuel Tank Pressure Sensor High Voltage',
'ERR13_bit4': 'P0340 — CMP Sensor A Circuit (bank 1)', 'ERR13_bit4': 'P0451 — Fuel Tank Pressure Sensor Range/Performance',
'ERR13_bit5': 'P0345 — CMP Sensor A Circuit (bank 2)', 'ERR13_bit5': 'ERR13.5 — Undocumented code',
'ERR13_bit6': 'P0366 — CMP Sensor B Circuit Range/Performance (bank 1)', 'ERR13_bit6': 'P0443 — EVAP Purge Valve Circuit',
'ERR13_bit7': 'P0391 — CMP Sensor B Circuit Range/Performance (bank 2)', 'ERR13_bit7': 'ERR13.7 — Unknown error 137',
// ERR14 // ERR14
'ERR14_bit0': 'P0016 — Crankshaft/Camshaft Position Correlation (bank 1, sensor A)', 'ERR14_bit0': 'ERR14.0 — PF2 Low',
'ERR14_bit1': 'P0017 — Crankshaft/Camshaft Position Correlation (bank 1, sensor B)', 'ERR14_bit1': 'ERR14.1 — PF2 High',
'ERR14_bit2': 'P0018 — Crankshaft/Camshaft Position Correlation (bank 2, sensor A)', 'ERR14_bit2': 'ERR14.2 — PF2 Monitor',
'ERR14_bit3': 'P0019 — Crankshaft/Camshaft Position Correlation (bank 2, sensor B)', 'ERR14_bit3': 'ERR14.3 — Undocumented code',
'ERR14_bit4': 'P0008 — Engine Position System (bank 1)', 'ERR14_bit4': 'ERR14.4 — TF2 Low',
'ERR14_bit5': 'P0009 — Engine Position System (bank 2)', 'ERR14_bit5': 'ERR14.5 — TF2 High',
'ERR14_bit6': 'P0014 — Camshaft Position B — Timing Over-Advanced (bank 1)', 'ERR14_bit6': 'ERR14.6 — PF0 Low',
'ERR14_bit7': 'P0024 — Camshaft Position B — Timing Over-Advanced (bank 2)', 'ERR14_bit7': 'ERR14.7 — PF0 High',
// ERR15 // ERR15
'ERR15_bit0': 'P0087 — Fuel Rail/System Pressure Too Low', 'ERR15_bit0': 'ERR15.0 — TF0 Low',
'ERR15_bit1': 'P0088 — Fuel Rail/System Pressure Too High', 'ERR15_bit1': 'ERR15.1 — TF0 High',
'ERR15_bit2': 'P0089 — Fuel Pressure Regulator Performance', 'ERR15_bit2': 'ERR15.2 — 3rd O2 Sensor Voltage Low',
'ERR15_bit3': 'P0090 — Fuel Pressure Regulator Control Circuit', 'ERR15_bit3': 'ERR15.3 — 3rd O2 Sensor Voltage High',
'ERR15_bit4': 'P0091 — Fuel Pressure Regulator Control Circuit Low', 'ERR15_bit4': 'ERR15.4 — 3rd O2 Sensor Response',
'ERR15_bit5': 'P0092 — Fuel Pressure Regulator Control Circuit High', 'ERR15_bit5': 'ERR15.5 — Unknown error 155',
'ERR15_bit6': 'P0201 — Injector Circuit/Open Cylinder 1', 'ERR15_bit6': 'ERR15.6 — 3rd O2 Sensor Heater Current Abnormality',
'ERR15_bit7': 'P0202 — Injector Circuit/Open Cylinder 2', 'ERR15_bit7': 'ERR15.7 — Unknown error 157',
// ERR16 // ERR16
'ERR16_bit0': 'P0203 — Injector Circuit/Open Cylinder 3', 'ERR16_bit0': 'ERR16.0 — NOx Ability Monitor',
'ERR16_bit1': 'P0204 — Injector Circuit/Open Cylinder 4', 'ERR16_bit1': 'ERR16.1 — Unknown error 161',
'ERR16_bit2': 'P0261 — Injector Circuit Low Cylinder 1', 'ERR16_bit2':
'ERR16_bit3': 'P0264 — Injector Circuit Low Cylinder 2', 'P1077 — Intake Manifold Runner Control System Malfunction (low rpm)',
'ERR16_bit4': 'P0267 — Injector Circuit Low Cylinder 3', 'ERR16_bit3':
'ERR16_bit5': 'P0270 — Injector Circuit Low Cylinder 4', 'P1078 — Intake Manifold Runner Control System Malfunction (high rpm)',
'ERR16_bit6': 'P0262 — Injector Circuit High Cylinder 1', 'ERR16_bit4': 'P0661 — Intake Manifold Runner Control Low Voltage',
'ERR16_bit7': 'P0265 — Injector Circuit High Cylinder 2', '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
'ERR17_bit0': 'P0268 — Injector Circuit High Cylinder 3', 'ERR17_bit0': 'P0219 — Engine Over-Rev',
'ERR17_bit1': 'P0271 — Injector Circuit High Cylinder 4', 'ERR17_bit1': 'P0217 — Engine Over-Heating',
'ERR17_bit2': 'P0351 — Ignition Coil A Primary/Secondary Circuit', 'ERR17_bit2': 'ERR17.2 — Undocumented code',
'ERR17_bit3': 'P0352 — Ignition Coil B Primary/Secondary Circuit', 'ERR17_bit3': 'ERR17.3 — Undocumented code',
'ERR17_bit4': 'P0353 — Ignition Coil C Primary/Secondary Circuit', 'ERR17_bit4': 'ERR17.4 — Undocumented code',
'ERR17_bit5': 'P0354 — Ignition Coil D Primary/Secondary Circuit', 'ERR17_bit5': 'ERR17.5 — Undocumented code',
'ERR17_bit6': 'P0627 — Fuel Pump Control Circuit Open', 'ERR17_bit6': 'ERR17.6 — Undocumented code',
'ERR17_bit7': 'P0628 — Fuel Pump Control Circuit Low', 'ERR17_bit7': 'ERR17.7 — Undocumented code',
}; };
/// Returns a list of active DTC descriptions from the given raw error bytes /// Returns a list of active DTC descriptions from the given raw error bytes

View File

@ -149,6 +149,14 @@ class BtNotifier extends StateNotifier<BtState> {
Future<void> connect(BluetoothDevice device) async { Future<void> connect(BluetoothDevice device) async {
if (state.isConnecting) return; // ignore double taps if (state.isConnecting) return; // ignore double taps
final session = ++_session; 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; _device = device;
_userDisconnected = false; _userDisconnected = false;
_resetCounters(); _resetCounters();
@ -369,19 +377,93 @@ class BtNotifier extends StateNotifier<BtState> {
); );
} }
/// DTC reset per hvbt_protocol.pdf: { 0x1B, 0x70, NEG8 }; the adapter echoes /// DTC reset per hvbt_protocol.pdf: { 0x1B, 0x70, NEG8 }; the adapter should
/// the same 3 bytes as acknowledgement. /// echo the same 3 bytes as acknowledgement.
Future<({bool acknowledged, Uint8List raw})> clearDtcs() async { ///
/// 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; 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<int> errOf(Uint8List f) =>
ecuType == EcuType.s300 ? f.sublist(37, 41) : f.sublist(62, 80);
String hex(List<int> 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<int>.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]); final command = Uint8List.fromList([0x1B, 0x70, 0x00]);
command[2] = calculateNeg8(Uint8List.fromList([0x1B, 0x70])); 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<void>.delayed(_dtcWatch);
await sub.cancel();
final codesCleared = clearedMask.fold(0, (n, b) => n + bits(b));
AppLog.i('Clear DTC: ack=${result.acknowledged} ' AppLog.i('Clear DTC: ack=${result.acknowledged} '
'rxBytes=${result.raw.length}'); 'rxBytes=${result.raw.length} '
return result; '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 @override
void dispose() { void dispose() {
_statusTimer?.cancel(); _statusTimer?.cancel();

View File

@ -98,20 +98,29 @@ class _DtcScreenState extends ConsumerState<DtcScreen> {
_lastRaw = result.raw; _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; final String message;
if (result.acknowledged) { if (result.codesBefore == 0) {
message = 'Trouble codes cleared'; 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) { } 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 { } else {
// The scan runs alongside normal polling now, so "not acknowledged" // "View" opens the raw reply hex to check by hand.
// can mean several seconds' worth of regular datalog traffic with no message = 'Unexpected reply (${result.raw.length} bytes) — codes did '
// ack byte pattern found in it — dumping all of that raw would be 'not clear';
// 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';
} }
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(

View File

@ -7,16 +7,29 @@ void main() {
expect(s300DtcMap.length, equals(32)); // 4 bytes × 8 bits expect(s300DtcMap.length, equals(32)); // 4 bytes × 8 bits
}); });
test('P0130 maps to correct description for ERR00 bit0', () { // Expected codes come from the S300 "DTC map" in hvbt_protocol.pdf.
expect(s300DtcMap['ERR00_bit0'], equals('P0130 — O2 Sensor (front)')); test('ERR00 bit0 = P0130 (O2A)', () {
expect(s300DtcMap['ERR00_bit0'], startsWith('P0130 — '));
}); });
test('P0505 maps for ERR00 bit2', () { test('ERR01 bit5 = P0505 (IAC)', () {
expect(s300DtcMap['ERR00_bit2'], equals('P0505 — Idle Air Control Valve')); expect(s300DtcMap['ERR01_bit5'], startsWith('P0505 — '));
}); });
test('P1607 maps for ERR03 bit7', () { test('bench codes: ERR01.6 = P0350, ERR02.3 = P1298, ERR02.4 = P2649', () {
expect(s300DtcMap['ERR03_bit7'], equals('P1607 — PCM Internal Circuit')); 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)); expect(kproDtcMap.length, equals(144));
}); });
test('P0130 maps for ERR00 bit0', () { // Expected codes come from the KPro "DTC map" in hvbt_protocol.pdf.
expect(kproDtcMap['ERR00_bit0'], equals('P0130 — O2 Sensor (front)')); 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', () { test('ERR17 entries present', () {
@ -44,7 +60,7 @@ void main() {
test('decodes ERR00 bit0 = P0130', () { test('decodes ERR00 bit0 = P0130', () {
final result = decodeDtcs([0x01, 0, 0, 0], s300DtcMap); 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', () { test('decodes multiple bits in one byte', () {

View File

@ -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);
});
});
}