@kb1rma/libcomm14cux-ts 0.2.0-beta.6 → 0.2.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -140,6 +140,8 @@ Writing to ECU memory, running the fuel pump or driving the idle air control mot
140
140
 
141
141
  The protocol and decoding logic are derived from [libcomm14cux](https://github.com/colinbourassa/libcomm14cux) © Colin Bourassa, licensed under the GNU GPL v3.
142
142
 
143
+ The factory fault code numbers and the general misfire flag (fault code 25) come from Colin Bourassa's [14cux-firmware](https://github.com/colinbourassa/14cux-firmware) disassembly.
144
+
143
145
  This project is therefore licensed under the **GNU General Public License v3.0 only** (`GPL-3.0-only`). Any application that includes this library must also be distributed under GPL-3.0-compatible terms, with its source code available.
144
146
 
145
147
  Parts of this project were written with the help of an AI assistant. The original libcomm14cux and RoverGauge projects do not accept AI-generated contributions, so nothing from this repository will be submitted to them.
@@ -1,9 +1,23 @@
1
- declare const FAULT_BITS: readonly [readonly [0, 0, "romChecksumFailure"], readonly [0, 1, "lambdaSensorOdd"], readonly [0, 2, "lambdaSensorEven"], readonly [0, 3, "misfireGeneral"], readonly [0, 4, "misfireOddBank"], readonly [0, 5, "misfireEvenBank"], readonly [0, 6, "airflowMeter"], readonly [0, 7, "tuneResistorOutOfRange"], readonly [1, 0, "injectorOddBank"], readonly [1, 2, "injectorEvenBank"], readonly [1, 3, "coolantTempSensor"], readonly [1, 4, "throttlePot"], readonly [1, 5, "throttlePotHiMafLo"], readonly [1, 6, "throttlePotLoMafHi"], readonly [1, 7, "purgeValveLeak"], readonly [2, 1, "mixtureTooLean"], readonly [2, 3, "intakeAirLeak"], readonly [3, 0, "lowFuelPressure"], readonly [3, 4, "idleValveStepperMotor"], readonly [3, 6, "roadSpeedSensor"], readonly [3, 7, "neutralSwitch"], readonly [4, 4, "lowFuelPressureOrAirLeak"], readonly [4, 5, "fuelTempSensor"], readonly [5, 6, "batteryDisconnected"], readonly [5, 7, "ramChecksumFailure"]];
1
+ declare const FAULT_BITS: readonly [readonly [0, 0, "romChecksumFailure", 29], readonly [0, 1, "lambdaSensorOdd", 44], readonly [0, 2, "lambdaSensorEven", 45], readonly [0, 3, "misfireGeneral", 25], readonly [0, 4, "misfireOddBank", 40], readonly [0, 5, "misfireEvenBank", 50], readonly [0, 6, "airflowMeter", 12], readonly [0, 7, "tuneResistorOutOfRange", 21], readonly [1, 0, "injectorOddBank", 34], readonly [1, 2, "injectorEvenBank", 36], readonly [1, 3, "coolantTempSensor", 14], readonly [1, 4, "throttlePot", 17], readonly [1, 5, "throttlePotHiMafLo", 18], readonly [1, 6, "throttlePotLoMafHi", 19], readonly [1, 7, "purgeValveLeak", 88], readonly [2, 1, "mixtureTooLean", 26], readonly [2, 3, "intakeAirLeak", 28], readonly [3, 0, "lowFuelPressure", 23], readonly [3, 4, "idleValveStepperMotor", 48], readonly [3, 6, "roadSpeedSensor", 68], readonly [3, 7, "neutralSwitch", 69], readonly [4, 4, "lowFuelPressureOrAirLeak", 59], readonly [4, 5, "fuelTempSensor", 15], readonly [5, 6, "batteryDisconnected", 2], readonly [5, 7, "ramChecksumFailure", 3]];
2
2
  /**
3
3
  * Name of a fault flag in {@link FaultCodes}, taken from the field names of
4
4
  * the C `c14cux_faultcodes` struct. `misfireGeneral` has no C field.
5
5
  */
6
6
  export type FaultCodeName = (typeof FAULT_BITS)[number][2];
7
+ /**
8
+ * Factory fault code number for each flag in {@link FaultCodes}, as a
9
+ * technician would read it ("fault 59"). Taken from the same table as the
10
+ * decoder, so the two cannot drift apart.
11
+ *
12
+ * Some firmware builds never set some of these codes; the number identifies the
13
+ * flag, not whether a given ECU can raise it.
14
+ *
15
+ * The numbers follow bit position in the firmware's priority table and match
16
+ * the published Land Rover fault code lists. Those lists name cylinder banks
17
+ * A and B rather than odd and even, so which bank is "odd" follows the C field
18
+ * names.
19
+ */
20
+ export declare const FAULT_CODE_NUMBERS: Readonly<Record<FaultCodeName, number>>;
7
21
  /**
8
22
  * Fault code flags (`c14cux_faultcodes`); `true` means the fault is set.
9
23
  *
@@ -1 +1 @@
1
- {"version":3,"file":"faultCodes.d.ts","sourceRoot":"","sources":["../../src/decoders/faultCodes.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,UAAU,y6BA6BN,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAExD,yDAAyD;AACzD,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAQ9D"}
1
+ {"version":3,"file":"faultCodes.d.ts","sourceRoot":"","sources":["../../src/decoders/faultCodes.ts"],"names":[],"mappings":"AAaA,QAAA,MAAM,UAAU,2gCA6BN,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAKpE,CAAC;AAEJ;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAExD,yDAAyD;AACzD,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAQ9D"}
@@ -2,39 +2,58 @@
2
2
  // Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
3
3
  // Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
4
4
  // Ported to TypeScript and modified for comm14cux-ts, 2026.
5
- // [byte offset from 0x0049, bit (0 = LSB), name]. Spare bits are omitted.
5
+ // [byte offset from 0x0049, bit (0 = LSB), name, factory fault code number].
6
+ // Spare bits are omitted.
6
7
  // Names follow the field names of `c14cux_faultcodes`, except `misfireGeneral`,
7
- // which has no C field.
8
+ // which has no C field. Code numbers are the bytes of the `.faultCodes` table
9
+ // in faults.asm of the 14CUX firmware disassembly
10
+ // (https://github.com/colinbourassa/14cux-firmware): each byte of the fault
11
+ // block lists its eight bits there in priority order, LSB first, as BCD (so
12
+ // $59 is code 59).
8
13
  const FAULT_BITS = [
9
- [0, 0, 'romChecksumFailure'],
10
- [0, 1, 'lambdaSensorOdd'],
11
- [0, 2, 'lambdaSensorEven'],
14
+ [0, 0, 'romChecksumFailure', 29],
15
+ [0, 1, 'lambdaSensorOdd', 44],
16
+ [0, 2, 'lambdaSensorEven', 45],
12
17
  // Fault code 25. libcomm14cux treats this bit as spare, but the firmware
13
18
  // disassembly (ignitionInt.asm, faults.asm) sets it on a misfire in builds
14
19
  // before R3360. R3360 and later never set it.
15
- [0, 3, 'misfireGeneral'],
16
- [0, 4, 'misfireOddBank'],
17
- [0, 5, 'misfireEvenBank'],
18
- [0, 6, 'airflowMeter'],
19
- [0, 7, 'tuneResistorOutOfRange'],
20
- [1, 0, 'injectorOddBank'],
21
- [1, 2, 'injectorEvenBank'],
22
- [1, 3, 'coolantTempSensor'],
23
- [1, 4, 'throttlePot'],
24
- [1, 5, 'throttlePotHiMafLo'],
25
- [1, 6, 'throttlePotLoMafHi'],
26
- [1, 7, 'purgeValveLeak'],
27
- [2, 1, 'mixtureTooLean'],
28
- [2, 3, 'intakeAirLeak'],
29
- [3, 0, 'lowFuelPressure'],
30
- [3, 4, 'idleValveStepperMotor'],
31
- [3, 6, 'roadSpeedSensor'],
32
- [3, 7, 'neutralSwitch'],
33
- [4, 4, 'lowFuelPressureOrAirLeak'],
34
- [4, 5, 'fuelTempSensor'],
35
- [5, 6, 'batteryDisconnected'],
36
- [5, 7, 'ramChecksumFailure'],
20
+ [0, 3, 'misfireGeneral', 25],
21
+ [0, 4, 'misfireOddBank', 40],
22
+ [0, 5, 'misfireEvenBank', 50],
23
+ [0, 6, 'airflowMeter', 12],
24
+ [0, 7, 'tuneResistorOutOfRange', 21],
25
+ [1, 0, 'injectorOddBank', 34],
26
+ [1, 2, 'injectorEvenBank', 36],
27
+ [1, 3, 'coolantTempSensor', 14],
28
+ [1, 4, 'throttlePot', 17],
29
+ [1, 5, 'throttlePotHiMafLo', 18],
30
+ [1, 6, 'throttlePotLoMafHi', 19],
31
+ [1, 7, 'purgeValveLeak', 88],
32
+ [2, 1, 'mixtureTooLean', 26],
33
+ [2, 3, 'intakeAirLeak', 28],
34
+ [3, 0, 'lowFuelPressure', 23],
35
+ [3, 4, 'idleValveStepperMotor', 48],
36
+ [3, 6, 'roadSpeedSensor', 68],
37
+ [3, 7, 'neutralSwitch', 69],
38
+ [4, 4, 'lowFuelPressureOrAirLeak', 59],
39
+ [4, 5, 'fuelTempSensor', 15],
40
+ [5, 6, 'batteryDisconnected', 2],
41
+ [5, 7, 'ramChecksumFailure', 3],
37
42
  ];
43
+ /**
44
+ * Factory fault code number for each flag in {@link FaultCodes}, as a
45
+ * technician would read it ("fault 59"). Taken from the same table as the
46
+ * decoder, so the two cannot drift apart.
47
+ *
48
+ * Some firmware builds never set some of these codes; the number identifies the
49
+ * flag, not whether a given ECU can raise it.
50
+ *
51
+ * The numbers follow bit position in the firmware's priority table and match
52
+ * the published Land Rover fault code lists. Those lists name cylinder banks
53
+ * A and B rather than odd and even, so which bank is "odd" follows the C field
54
+ * names.
55
+ */
56
+ export const FAULT_CODE_NUMBERS = Object.freeze(Object.fromEntries(FAULT_BITS.map(([, , name, code]) => [name, code])));
38
57
  /** Number of bytes in the fault code block at 0x0049. */
39
58
  export const FAULT_CODE_BLOCK_SIZE = 6;
40
59
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"faultCodes.js","sourceRoot":"","sources":["../../src/decoders/faultCodes.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,4EAA4E;AAC5E,+DAA+D;AAC/D,4DAA4D;AAE5D,0EAA0E;AAC1E,gFAAgF;AAChF,wBAAwB;AACxB,MAAM,UAAU,GAAG;IACjB,CAAC,CAAC,EAAE,CAAC,EAAE,oBAAoB,CAAC;IAC5B,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACzB,CAAC,CAAC,EAAE,CAAC,EAAE,kBAAkB,CAAC;IAC1B,yEAAyE;IACzE,2EAA2E;IAC3E,8CAA8C;IAC9C,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACzB,CAAC,CAAC,EAAE,CAAC,EAAE,cAAc,CAAC;IACtB,CAAC,CAAC,EAAE,CAAC,EAAE,wBAAwB,CAAC;IAChC,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACzB,CAAC,CAAC,EAAE,CAAC,EAAE,kBAAkB,CAAC;IAC1B,CAAC,CAAC,EAAE,CAAC,EAAE,mBAAmB,CAAC;IAC3B,CAAC,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC;IACrB,CAAC,CAAC,EAAE,CAAC,EAAE,oBAAoB,CAAC;IAC5B,CAAC,CAAC,EAAE,CAAC,EAAE,oBAAoB,CAAC;IAC5B,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC;IACvB,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACzB,CAAC,CAAC,EAAE,CAAC,EAAE,uBAAuB,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACzB,CAAC,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC;IACvB,CAAC,CAAC,EAAE,CAAC,EAAE,0BAA0B,CAAC;IAClC,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,qBAAqB,CAAC;IAC7B,CAAC,CAAC,EAAE,CAAC,EAAE,oBAAoB,CAAC;CACpB,CAAC;AAiBX,yDAAyD;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,KAAK,GAAG,EAAgB,CAAC;IAE/B,KAAK,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["// SPDX-License-Identifier: GPL-3.0-only\n// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)\n// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.\n// Ported to TypeScript and modified for comm14cux-ts, 2026.\n\n// [byte offset from 0x0049, bit (0 = LSB), name]. Spare bits are omitted.\n// Names follow the field names of `c14cux_faultcodes`, except `misfireGeneral`,\n// which has no C field.\nconst FAULT_BITS = [\n [0, 0, 'romChecksumFailure'],\n [0, 1, 'lambdaSensorOdd'],\n [0, 2, 'lambdaSensorEven'],\n // Fault code 25. libcomm14cux treats this bit as spare, but the firmware\n // disassembly (ignitionInt.asm, faults.asm) sets it on a misfire in builds\n // before R3360. R3360 and later never set it.\n [0, 3, 'misfireGeneral'],\n [0, 4, 'misfireOddBank'],\n [0, 5, 'misfireEvenBank'],\n [0, 6, 'airflowMeter'],\n [0, 7, 'tuneResistorOutOfRange'],\n [1, 0, 'injectorOddBank'],\n [1, 2, 'injectorEvenBank'],\n [1, 3, 'coolantTempSensor'],\n [1, 4, 'throttlePot'],\n [1, 5, 'throttlePotHiMafLo'],\n [1, 6, 'throttlePotLoMafHi'],\n [1, 7, 'purgeValveLeak'],\n [2, 1, 'mixtureTooLean'],\n [2, 3, 'intakeAirLeak'],\n [3, 0, 'lowFuelPressure'],\n [3, 4, 'idleValveStepperMotor'],\n [3, 6, 'roadSpeedSensor'],\n [3, 7, 'neutralSwitch'],\n [4, 4, 'lowFuelPressureOrAirLeak'],\n [4, 5, 'fuelTempSensor'],\n [5, 6, 'batteryDisconnected'],\n [5, 7, 'ramChecksumFailure'],\n] as const;\n\n/**\n * Name of a fault flag in {@link FaultCodes}, taken from the field names of\n * the C `c14cux_faultcodes` struct. `misfireGeneral` has no C field.\n */\nexport type FaultCodeName = (typeof FAULT_BITS)[number][2];\n\n/**\n * Fault code flags (`c14cux_faultcodes`); `true` means the fault is set.\n *\n * `misfireGeneral` (fault code 25, bit 3 of 0x0049) is not in the C struct.\n * The firmware disassembly shows that only builds before R3360 set it, so it\n * is not expected to be set on later firmware.\n */\nexport type FaultCodes = Record<FaultCodeName, boolean>;\n\n/** Number of bytes in the fault code block at 0x0049. */\nexport const FAULT_CODE_BLOCK_SIZE = 6;\n\n/**\n * Decodes the fault code block into named flags. Spare bits are ignored.\n *\n * @param bytes - The six bytes read from 0x0049.\n * @returns A flag for each fault; `true` means the fault is set.\n */\nexport function decodeFaultCodes(bytes: Uint8Array): FaultCodes {\n const codes = {} as FaultCodes;\n\n for (const [byteIndex, bit, name] of FAULT_BITS) {\n codes[name] = (((bytes[byteIndex] ?? 0) >> bit) & 1) === 1;\n }\n\n return codes;\n}\n"]}
1
+ {"version":3,"file":"faultCodes.js","sourceRoot":"","sources":["../../src/decoders/faultCodes.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: GPL-3.0-only\n// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)\n// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.\n// Ported to TypeScript and modified for comm14cux-ts, 2026.\n\n// [byte offset from 0x0049, bit (0 = LSB), name, factory fault code number].\n// Spare bits are omitted.\n// Names follow the field names of `c14cux_faultcodes`, except `misfireGeneral`,\n// which has no C field. Code numbers are the bytes of the `.faultCodes` table\n// in faults.asm of the 14CUX firmware disassembly\n// (https://github.com/colinbourassa/14cux-firmware): each byte of the fault\n// block lists its eight bits there in priority order, LSB first, as BCD (so\n// $59 is code 59).\nconst FAULT_BITS = [\n [0, 0, 'romChecksumFailure', 29],\n [0, 1, 'lambdaSensorOdd', 44],\n [0, 2, 'lambdaSensorEven', 45],\n // Fault code 25. libcomm14cux treats this bit as spare, but the firmware\n // disassembly (ignitionInt.asm, faults.asm) sets it on a misfire in builds\n // before R3360. R3360 and later never set it.\n [0, 3, 'misfireGeneral', 25],\n [0, 4, 'misfireOddBank', 40],\n [0, 5, 'misfireEvenBank', 50],\n [0, 6, 'airflowMeter', 12],\n [0, 7, 'tuneResistorOutOfRange', 21],\n [1, 0, 'injectorOddBank', 34],\n [1, 2, 'injectorEvenBank', 36],\n [1, 3, 'coolantTempSensor', 14],\n [1, 4, 'throttlePot', 17],\n [1, 5, 'throttlePotHiMafLo', 18],\n [1, 6, 'throttlePotLoMafHi', 19],\n [1, 7, 'purgeValveLeak', 88],\n [2, 1, 'mixtureTooLean', 26],\n [2, 3, 'intakeAirLeak', 28],\n [3, 0, 'lowFuelPressure', 23],\n [3, 4, 'idleValveStepperMotor', 48],\n [3, 6, 'roadSpeedSensor', 68],\n [3, 7, 'neutralSwitch', 69],\n [4, 4, 'lowFuelPressureOrAirLeak', 59],\n [4, 5, 'fuelTempSensor', 15],\n [5, 6, 'batteryDisconnected', 2],\n [5, 7, 'ramChecksumFailure', 3],\n] as const;\n\n/**\n * Name of a fault flag in {@link FaultCodes}, taken from the field names of\n * the C `c14cux_faultcodes` struct. `misfireGeneral` has no C field.\n */\nexport type FaultCodeName = (typeof FAULT_BITS)[number][2];\n\n/**\n * Factory fault code number for each flag in {@link FaultCodes}, as a\n * technician would read it (\"fault 59\"). Taken from the same table as the\n * decoder, so the two cannot drift apart.\n *\n * Some firmware builds never set some of these codes; the number identifies the\n * flag, not whether a given ECU can raise it.\n *\n * The numbers follow bit position in the firmware's priority table and match\n * the published Land Rover fault code lists. Those lists name cylinder banks\n * A and B rather than odd and even, so which bank is \"odd\" follows the C field\n * names.\n */\nexport const FAULT_CODE_NUMBERS: Readonly<Record<FaultCodeName, number>> =\n Object.freeze(\n Object.fromEntries(\n FAULT_BITS.map(([, , name, code]) => [name, code]),\n ) as Record<FaultCodeName, number>,\n );\n\n/**\n * Fault code flags (`c14cux_faultcodes`); `true` means the fault is set.\n *\n * `misfireGeneral` (fault code 25, bit 3 of 0x0049) is not in the C struct.\n * The firmware disassembly shows that only builds before R3360 set it, so it\n * is not expected to be set on later firmware.\n */\nexport type FaultCodes = Record<FaultCodeName, boolean>;\n\n/** Number of bytes in the fault code block at 0x0049. */\nexport const FAULT_CODE_BLOCK_SIZE = 6;\n\n/**\n * Decodes the fault code block into named flags. Spare bits are ignored.\n *\n * @param bytes - The six bytes read from 0x0049.\n * @returns A flag for each fault; `true` means the fault is set.\n */\nexport function decodeFaultCodes(bytes: Uint8Array): FaultCodes {\n const codes = {} as FaultCodes;\n\n for (const [byteIndex, bit, name] of FAULT_BITS) {\n codes[name] = (((bytes[byteIndex] ?? 0) >> bit) & 1) === 1;\n }\n\n return codes;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './errors.js';
4
4
  export type { Transport } from './transport/types.js';
5
5
  export { SimulatedTransport } from './transport/simulated.js';
6
6
  export { WebSerialTransport, type WebSerialTransportOptions, } from './transport/webSerial.js';
7
+ export { FAULT_CODE_NUMBERS } from './decoders/faultCodes.js';
7
8
  export type { FaultCodes, FaultCodeName } from './decoders/faultCodes.js';
8
9
  export type { FuelMapIndex } from './decoders/fuelMap.js';
9
10
  export type { TuneRevision } from './decoders/tuneRevision.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAC9D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAC9D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -5,4 +5,5 @@ export * from './constants.js';
5
5
  export * from './errors.js';
6
6
  export { SimulatedTransport } from './transport/simulated.js';
7
7
  export { WebSerialTransport, } from './transport/webSerial.js';
8
+ export { FAULT_CODE_NUMBERS } from './decoders/faultCodes.js';
8
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,+CAA+C;AAE/C,OAAO,EAAE,GAAG,EAAiC,MAAM,UAAU,CAAC;AAC9D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EACL,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC","sourcesContent":["// SPDX-License-Identifier: GPL-3.0-only\n// Copyright (C) 2026 comm14cux-ts contributors\n\nexport { Ecu, type EcuOptions, type FuelMap } from './ecu.js';\nexport * from './constants.js';\nexport * from './errors.js';\nexport type { Transport } from './transport/types.js';\nexport { SimulatedTransport } from './transport/simulated.js';\nexport {\n WebSerialTransport,\n type WebSerialTransportOptions,\n} from './transport/webSerial.js';\nexport type { FaultCodes, FaultCodeName } from './decoders/faultCodes.js';\nexport type { FuelMapIndex } from './decoders/fuelMap.js';\nexport type { TuneRevision } from './decoders/tuneRevision.js';\nexport type { Version } from './version.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,+CAA+C;AAE/C,OAAO,EAAE,GAAG,EAAiC,MAAM,UAAU,CAAC;AAC9D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EACL,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["// SPDX-License-Identifier: GPL-3.0-only\n// Copyright (C) 2026 comm14cux-ts contributors\n\nexport { Ecu, type EcuOptions, type FuelMap } from './ecu.js';\nexport * from './constants.js';\nexport * from './errors.js';\nexport type { Transport } from './transport/types.js';\nexport { SimulatedTransport } from './transport/simulated.js';\nexport {\n WebSerialTransport,\n type WebSerialTransportOptions,\n} from './transport/webSerial.js';\nexport { FAULT_CODE_NUMBERS } from './decoders/faultCodes.js';\nexport type { FaultCodes, FaultCodeName } from './decoders/faultCodes.js';\nexport type { FuelMapIndex } from './decoders/fuelMap.js';\nexport type { TuneRevision } from './decoders/tuneRevision.js';\nexport type { Version } from './version.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kb1rma/libcomm14cux-ts",
3
- "version": "0.2.0-beta.6",
3
+ "version": "0.2.0-beta.7",
4
4
  "description": "TypeScript library for communicating with the Lucas 14CUX engine ECU over a serial port using the Web Serial API",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
@@ -3,38 +3,43 @@
3
3
  // Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
4
4
  // Ported to TypeScript and modified for comm14cux-ts, 2026.
5
5
 
6
- // [byte offset from 0x0049, bit (0 = LSB), name]. Spare bits are omitted.
6
+ // [byte offset from 0x0049, bit (0 = LSB), name, factory fault code number].
7
+ // Spare bits are omitted.
7
8
  // Names follow the field names of `c14cux_faultcodes`, except `misfireGeneral`,
8
- // which has no C field.
9
+ // which has no C field. Code numbers are the bytes of the `.faultCodes` table
10
+ // in faults.asm of the 14CUX firmware disassembly
11
+ // (https://github.com/colinbourassa/14cux-firmware): each byte of the fault
12
+ // block lists its eight bits there in priority order, LSB first, as BCD (so
13
+ // $59 is code 59).
9
14
  const FAULT_BITS = [
10
- [0, 0, 'romChecksumFailure'],
11
- [0, 1, 'lambdaSensorOdd'],
12
- [0, 2, 'lambdaSensorEven'],
15
+ [0, 0, 'romChecksumFailure', 29],
16
+ [0, 1, 'lambdaSensorOdd', 44],
17
+ [0, 2, 'lambdaSensorEven', 45],
13
18
  // Fault code 25. libcomm14cux treats this bit as spare, but the firmware
14
19
  // disassembly (ignitionInt.asm, faults.asm) sets it on a misfire in builds
15
20
  // before R3360. R3360 and later never set it.
16
- [0, 3, 'misfireGeneral'],
17
- [0, 4, 'misfireOddBank'],
18
- [0, 5, 'misfireEvenBank'],
19
- [0, 6, 'airflowMeter'],
20
- [0, 7, 'tuneResistorOutOfRange'],
21
- [1, 0, 'injectorOddBank'],
22
- [1, 2, 'injectorEvenBank'],
23
- [1, 3, 'coolantTempSensor'],
24
- [1, 4, 'throttlePot'],
25
- [1, 5, 'throttlePotHiMafLo'],
26
- [1, 6, 'throttlePotLoMafHi'],
27
- [1, 7, 'purgeValveLeak'],
28
- [2, 1, 'mixtureTooLean'],
29
- [2, 3, 'intakeAirLeak'],
30
- [3, 0, 'lowFuelPressure'],
31
- [3, 4, 'idleValveStepperMotor'],
32
- [3, 6, 'roadSpeedSensor'],
33
- [3, 7, 'neutralSwitch'],
34
- [4, 4, 'lowFuelPressureOrAirLeak'],
35
- [4, 5, 'fuelTempSensor'],
36
- [5, 6, 'batteryDisconnected'],
37
- [5, 7, 'ramChecksumFailure'],
21
+ [0, 3, 'misfireGeneral', 25],
22
+ [0, 4, 'misfireOddBank', 40],
23
+ [0, 5, 'misfireEvenBank', 50],
24
+ [0, 6, 'airflowMeter', 12],
25
+ [0, 7, 'tuneResistorOutOfRange', 21],
26
+ [1, 0, 'injectorOddBank', 34],
27
+ [1, 2, 'injectorEvenBank', 36],
28
+ [1, 3, 'coolantTempSensor', 14],
29
+ [1, 4, 'throttlePot', 17],
30
+ [1, 5, 'throttlePotHiMafLo', 18],
31
+ [1, 6, 'throttlePotLoMafHi', 19],
32
+ [1, 7, 'purgeValveLeak', 88],
33
+ [2, 1, 'mixtureTooLean', 26],
34
+ [2, 3, 'intakeAirLeak', 28],
35
+ [3, 0, 'lowFuelPressure', 23],
36
+ [3, 4, 'idleValveStepperMotor', 48],
37
+ [3, 6, 'roadSpeedSensor', 68],
38
+ [3, 7, 'neutralSwitch', 69],
39
+ [4, 4, 'lowFuelPressureOrAirLeak', 59],
40
+ [4, 5, 'fuelTempSensor', 15],
41
+ [5, 6, 'batteryDisconnected', 2],
42
+ [5, 7, 'ramChecksumFailure', 3],
38
43
  ] as const;
39
44
 
40
45
  /**
@@ -43,6 +48,26 @@ const FAULT_BITS = [
43
48
  */
44
49
  export type FaultCodeName = (typeof FAULT_BITS)[number][2];
45
50
 
51
+ /**
52
+ * Factory fault code number for each flag in {@link FaultCodes}, as a
53
+ * technician would read it ("fault 59"). Taken from the same table as the
54
+ * decoder, so the two cannot drift apart.
55
+ *
56
+ * Some firmware builds never set some of these codes; the number identifies the
57
+ * flag, not whether a given ECU can raise it.
58
+ *
59
+ * The numbers follow bit position in the firmware's priority table and match
60
+ * the published Land Rover fault code lists. Those lists name cylinder banks
61
+ * A and B rather than odd and even, so which bank is "odd" follows the C field
62
+ * names.
63
+ */
64
+ export const FAULT_CODE_NUMBERS: Readonly<Record<FaultCodeName, number>> =
65
+ Object.freeze(
66
+ Object.fromEntries(
67
+ FAULT_BITS.map(([, , name, code]) => [name, code]),
68
+ ) as Record<FaultCodeName, number>,
69
+ );
70
+
46
71
  /**
47
72
  * Fault code flags (`c14cux_faultcodes`); `true` means the fault is set.
48
73
  *
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export {
10
10
  WebSerialTransport,
11
11
  type WebSerialTransportOptions,
12
12
  } from './transport/webSerial.js';
13
+ export { FAULT_CODE_NUMBERS } from './decoders/faultCodes.js';
13
14
  export type { FaultCodes, FaultCodeName } from './decoders/faultCodes.js';
14
15
  export type { FuelMapIndex } from './decoders/fuelMap.js';
15
16
  export type { TuneRevision } from './decoders/tuneRevision.js';