@kb1rma/libcomm14cux-ts 0.0.0
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/LICENSE +674 -0
- package/README.md +154 -0
- package/dist/bytes.d.ts +9 -0
- package/dist/bytes.d.ts.map +1 -0
- package/dist/bytes.js +13 -0
- package/dist/bytes.js.map +1 -0
- package/dist/constants.d.ts +174 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +122 -0
- package/dist/constants.js.map +1 -0
- package/dist/decoders/airflow.d.ts +19 -0
- package/dist/decoders/airflow.d.ts.map +1 -0
- package/dist/decoders/airflow.js +36 -0
- package/dist/decoders/airflow.js.map +1 -0
- package/dist/decoders/engineSpeed.d.ts +20 -0
- package/dist/decoders/engineSpeed.d.ts.map +1 -0
- package/dist/decoders/engineSpeed.js +33 -0
- package/dist/decoders/engineSpeed.js.map +1 -0
- package/dist/decoders/faultCodes.d.ts +19 -0
- package/dist/decoders/faultCodes.d.ts.map +1 -0
- package/dist/decoders/faultCodes.js +48 -0
- package/dist/decoders/faultCodes.js.map +1 -0
- package/dist/decoders/flags.d.ts +45 -0
- package/dist/decoders/flags.d.ts.map +1 -0
- package/dist/decoders/flags.js +54 -0
- package/dist/decoders/flags.js.map +1 -0
- package/dist/decoders/fuelMap.d.ts +89 -0
- package/dist/decoders/fuelMap.d.ts.map +1 -0
- package/dist/decoders/fuelMap.js +109 -0
- package/dist/decoders/fuelMap.js.map +1 -0
- package/dist/decoders/gear.d.ts +10 -0
- package/dist/decoders/gear.d.ts.map +1 -0
- package/dist/decoders/gear.js +23 -0
- package/dist/decoders/gear.js.map +1 -0
- package/dist/decoders/idleBypass.d.ts +8 -0
- package/dist/decoders/idleBypass.d.ts.map +1 -0
- package/dist/decoders/idleBypass.js +15 -0
- package/dist/decoders/idleBypass.js.map +1 -0
- package/dist/decoders/lambdaTrim.d.ts +15 -0
- package/dist/decoders/lambdaTrim.d.ts.map +1 -0
- package/dist/decoders/lambdaTrim.js +23 -0
- package/dist/decoders/lambdaTrim.js.map +1 -0
- package/dist/decoders/mainVoltage.d.ts +14 -0
- package/dist/decoders/mainVoltage.d.ts.map +1 -0
- package/dist/decoders/mainVoltage.js +26 -0
- package/dist/decoders/mainVoltage.js.map +1 -0
- package/dist/decoders/roadSpeed.d.ts +8 -0
- package/dist/decoders/roadSpeed.d.ts.map +1 -0
- package/dist/decoders/roadSpeed.js +14 -0
- package/dist/decoders/roadSpeed.js.map +1 -0
- package/dist/decoders/romRevision.d.ts +18 -0
- package/dist/decoders/romRevision.d.ts.map +1 -0
- package/dist/decoders/romRevision.js +26 -0
- package/dist/decoders/romRevision.js.map +1 -0
- package/dist/decoders/temperature.d.ts +12 -0
- package/dist/decoders/temperature.d.ts.map +1 -0
- package/dist/decoders/temperature.js +39 -0
- package/dist/decoders/temperature.js.map +1 -0
- package/dist/decoders/throttle.d.ts +18 -0
- package/dist/decoders/throttle.d.ts.map +1 -0
- package/dist/decoders/throttle.js +33 -0
- package/dist/decoders/throttle.js.map +1 -0
- package/dist/decoders/tuneRevision.d.ts +25 -0
- package/dist/decoders/tuneRevision.d.ts.map +1 -0
- package/dist/decoders/tuneRevision.js +22 -0
- package/dist/decoders/tuneRevision.js.map +1 -0
- package/dist/ecu.d.ts +473 -0
- package/dist/ecu.d.ts.map +1 -0
- package/dist/ecu.js +700 -0
- package/dist/ecu.js.map +1 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +33 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/protocol.d.ts +57 -0
- package/dist/protocol/protocol.d.ts.map +1 -0
- package/dist/protocol/protocol.js +137 -0
- package/dist/protocol/protocol.js.map +1 -0
- package/dist/protocol/readCount.d.ts +21 -0
- package/dist/protocol/readCount.d.ts.map +1 -0
- package/dist/protocol/readCount.js +30 -0
- package/dist/protocol/readCount.js.map +1 -0
- package/dist/queue.d.ts +15 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +21 -0
- package/dist/queue.js.map +1 -0
- package/dist/transport/simulated.d.ts +65 -0
- package/dist/transport/simulated.d.ts.map +1 -0
- package/dist/transport/simulated.js +190 -0
- package/dist/transport/simulated.js.map +1 -0
- package/dist/transport/types.d.ts +9 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +4 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/transport/webSerial.d.ts +49 -0
- package/dist/transport/webSerial.d.ts.map +1 -0
- package/dist/transport/webSerial.js +129 -0
- package/dist/transport/webSerial.js.map +1 -0
- package/dist/version.d.ts +18 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +58 -0
- package/src/bytes.ts +13 -0
- package/src/constants.ts +175 -0
- package/src/decoders/airflow.ts +41 -0
- package/src/decoders/engineSpeed.ts +36 -0
- package/src/decoders/faultCodes.ts +61 -0
- package/src/decoders/flags.ts +64 -0
- package/src/decoders/fuelMap.ts +167 -0
- package/src/decoders/gear.ts +26 -0
- package/src/decoders/idleBypass.ts +16 -0
- package/src/decoders/lambdaTrim.ts +24 -0
- package/src/decoders/mainVoltage.ts +37 -0
- package/src/decoders/roadSpeed.ts +14 -0
- package/src/decoders/romRevision.ts +30 -0
- package/src/decoders/temperature.ts +42 -0
- package/src/decoders/throttle.ts +37 -0
- package/src/decoders/tuneRevision.ts +42 -0
- package/src/ecu.ts +948 -0
- package/src/errors.ts +38 -0
- package/src/index.ts +16 -0
- package/src/protocol/protocol.ts +167 -0
- package/src/protocol/readCount.ts +45 -0
- package/src/queue.ts +24 -0
- package/src/test-support/ecu.ts +126 -0
- package/src/transport/simulated.ts +220 -0
- package/src/transport/types.ts +11 -0
- package/src/transport/webSerial.ts +163 -0
- package/src/version.ts +21 -0
package/src/constants.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
/** Baud rate of the 14CUX serial port. */
|
|
7
|
+
export const BAUD = 7812;
|
|
8
|
+
|
|
9
|
+
/** Baud rate used by ECUs running modified double-speed firmware. */
|
|
10
|
+
export const BAUD_DOUBLE_SPEED = 15625;
|
|
11
|
+
|
|
12
|
+
/** Number of engine speed ranges in the fuel maps. */
|
|
13
|
+
export const FUEL_MAP_COLUMNS = 16;
|
|
14
|
+
|
|
15
|
+
/** Number of engine load ranges in the fuel maps. */
|
|
16
|
+
export const FUEL_MAP_ROWS = 8;
|
|
17
|
+
|
|
18
|
+
/** Byte quantities that may be requested in a single read (`c14cux_readcount`). */
|
|
19
|
+
export const ReadCount = {
|
|
20
|
+
Count0: 0x0010,
|
|
21
|
+
Count1: 0x0050,
|
|
22
|
+
Count2: 0x0064,
|
|
23
|
+
Count3: 0x0190,
|
|
24
|
+
Count4: 0x0200,
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
/** Length codes sent to the ECU for the fixed read quantities. */
|
|
28
|
+
export const ReadCountValue = {
|
|
29
|
+
Count1: 0x10,
|
|
30
|
+
Count2: 0x11,
|
|
31
|
+
Count3: 0x12,
|
|
32
|
+
Count4: 0x13,
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
/** Sizes in bytes of fixed-size structures within the ROM. */
|
|
36
|
+
export const DataSize = {
|
|
37
|
+
ROM: 0x4000,
|
|
38
|
+
FuelMap: 0x80,
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
/** Important memory offsets in the ECU's address space (`c14cux_memory_offset`). */
|
|
42
|
+
export const MemoryOffset = {
|
|
43
|
+
ROMAddress: 0xc000,
|
|
44
|
+
RPMTable: 0xc800,
|
|
45
|
+
Port1: 0x0002,
|
|
46
|
+
LongTermLambdaFuelingTrimOdd: 0x0042,
|
|
47
|
+
LongTermLambdaFuelingTrimEven: 0x0046,
|
|
48
|
+
ShortTermLambdaFuelingTrimOdd: 0x0065,
|
|
49
|
+
ShortTermLambdaFuelingTrimEven: 0x0067,
|
|
50
|
+
FaultCodes: 0x0049,
|
|
51
|
+
ThrottleMinimumPosition: 0x0051,
|
|
52
|
+
MainVoltage: 0x0055,
|
|
53
|
+
MassAirflowDirect: 0x0057,
|
|
54
|
+
ThrottlePosition: 0x005f,
|
|
55
|
+
CoolantTemp: 0x006a,
|
|
56
|
+
IdleBypassPosition: 0x006d,
|
|
57
|
+
EngineSpeedInstantaneous: 0x007a,
|
|
58
|
+
EngineSpeedFiltered: 0x007c,
|
|
59
|
+
InjectorPulseWidth: 0x0082,
|
|
60
|
+
Bits008A: 0x008a,
|
|
61
|
+
PurgeValveState: 0x0096,
|
|
62
|
+
Bits00DD: 0x00dd,
|
|
63
|
+
TransmissionGear: 0x2000,
|
|
64
|
+
RoadSpeed: 0x2003,
|
|
65
|
+
FuelTemp: 0x2006,
|
|
66
|
+
RowScaler: 0x200a,
|
|
67
|
+
RPMLimit: 0x200c,
|
|
68
|
+
IdleMode: 0x2047,
|
|
69
|
+
MassAirflowLinear: 0x204d,
|
|
70
|
+
TargetIdleSpeed: 0x2051,
|
|
71
|
+
RevBMainVoltageFactorA: 0xc79b,
|
|
72
|
+
RevBMainVoltageFactorB: 0xc79c,
|
|
73
|
+
RevBMainVoltageFactorC: 0xc79d,
|
|
74
|
+
RevCMainVoltageFactorA: 0xc7c3,
|
|
75
|
+
RevCMainVoltageFactorB: 0xc7c4,
|
|
76
|
+
RevCMainVoltageFactorC: 0xc7c5,
|
|
77
|
+
MAFRowScaler: 0xc1c7,
|
|
78
|
+
Map0RowScalerInitValue: 0xc1c9,
|
|
79
|
+
FuelMap0: 0xc000,
|
|
80
|
+
NewFuelMap1: 0xc267,
|
|
81
|
+
NewFuelMap2: 0xc379,
|
|
82
|
+
NewFuelMap3: 0xc48b,
|
|
83
|
+
NewFuelMap4: 0xc59d,
|
|
84
|
+
NewFuelMap5: 0xc6af,
|
|
85
|
+
OldFuelMap1: 0xc23f,
|
|
86
|
+
OldFuelMap2: 0xc351,
|
|
87
|
+
OldFuelMap3: 0xc463,
|
|
88
|
+
OldFuelMap4: 0xc575,
|
|
89
|
+
OldFuelMap5: 0xc687,
|
|
90
|
+
TuneRevision: 0xffe9,
|
|
91
|
+
ChecksumFixer: 0xffeb,
|
|
92
|
+
TuneIdent: 0xffec,
|
|
93
|
+
CurrentFuelMapId: 0x202c,
|
|
94
|
+
FuelMapRowIndex: 0x005b,
|
|
95
|
+
FuelMapColumnIndex: 0x005c,
|
|
96
|
+
IdleAirControlStepCount: 0x0075,
|
|
97
|
+
FuelPumpTimer: 0x00af,
|
|
98
|
+
} as const;
|
|
99
|
+
|
|
100
|
+
/** Offset from the start of a fuel map to its row scaler. */
|
|
101
|
+
export const FUEL_MAP_ROW_SCALER_OFFSET = 0x10a;
|
|
102
|
+
|
|
103
|
+
/** Fixed main-voltage coefficients for very old ECUs (1990 and earlier). */
|
|
104
|
+
export const RevAMainVoltageFactor = {
|
|
105
|
+
A: 0x64,
|
|
106
|
+
B: 0xbd,
|
|
107
|
+
C: 0x6180,
|
|
108
|
+
} as const;
|
|
109
|
+
|
|
110
|
+
/** Transmission gear selections. */
|
|
111
|
+
export const Gear = {
|
|
112
|
+
NoReading: 0,
|
|
113
|
+
ParkOrNeutral: 1,
|
|
114
|
+
DriveOrReverse: 2,
|
|
115
|
+
ManualGearbox: 3,
|
|
116
|
+
} as const;
|
|
117
|
+
/**
|
|
118
|
+
* Union of the {@link Gear} values.
|
|
119
|
+
*/
|
|
120
|
+
export type Gear = (typeof Gear)[keyof typeof Gear];
|
|
121
|
+
|
|
122
|
+
/** The two engine banks. */
|
|
123
|
+
export const Bank = { Odd: 0, Even: 1 } as const;
|
|
124
|
+
/**
|
|
125
|
+
* Union of the {@link Bank} values.
|
|
126
|
+
*/
|
|
127
|
+
export type Bank = (typeof Bank)[keyof typeof Bank];
|
|
128
|
+
|
|
129
|
+
/** Types of lambda trim for fueling. */
|
|
130
|
+
export const LambdaTrimType = { ShortTerm: 0, LongTerm: 1 } as const;
|
|
131
|
+
/**
|
|
132
|
+
* Union of the {@link LambdaTrimType} values.
|
|
133
|
+
*/
|
|
134
|
+
export type LambdaTrimType =
|
|
135
|
+
(typeof LambdaTrimType)[keyof typeof LambdaTrimType];
|
|
136
|
+
|
|
137
|
+
/** Fueling feedback modes. */
|
|
138
|
+
export const FeedbackMode = { ClosedLoop: 0, OpenLoop: 1 } as const;
|
|
139
|
+
/**
|
|
140
|
+
* Union of the {@link FeedbackMode} values.
|
|
141
|
+
*/
|
|
142
|
+
export type FeedbackMode = (typeof FeedbackMode)[keyof typeof FeedbackMode];
|
|
143
|
+
|
|
144
|
+
/** The two means of reading a value from the MAF. */
|
|
145
|
+
export const AirflowType = { Direct: 0, Linearized: 1 } as const;
|
|
146
|
+
/**
|
|
147
|
+
* Union of the {@link AirflowType} values.
|
|
148
|
+
*/
|
|
149
|
+
export type AirflowType = (typeof AirflowType)[keyof typeof AirflowType];
|
|
150
|
+
|
|
151
|
+
/** The two methods of interpreting a throttle position. */
|
|
152
|
+
export const ThrottlePosType = { Absolute: 0, Corrected: 1 } as const;
|
|
153
|
+
/**
|
|
154
|
+
* Union of the {@link ThrottlePosType} values.
|
|
155
|
+
*/
|
|
156
|
+
export type ThrottlePosType =
|
|
157
|
+
(typeof ThrottlePosType)[keyof typeof ThrottlePosType];
|
|
158
|
+
|
|
159
|
+
/** Revisions of the ROM data layout (`c14cux_data_offset_rev`). */
|
|
160
|
+
export const DataOffsetRev = { Unset: 0, RevA: 1, RevB: 2, RevC: 3 } as const;
|
|
161
|
+
/**
|
|
162
|
+
* Union of the {@link DataOffsetRev} values.
|
|
163
|
+
*/
|
|
164
|
+
export type DataOffsetRev = (typeof DataOffsetRev)[keyof typeof DataOffsetRev];
|
|
165
|
+
|
|
166
|
+
/** States of the carbon canister purge valve. */
|
|
167
|
+
export const PurgeValveState = { Closed: 0, Toggling: 1, Open: 2 } as const;
|
|
168
|
+
/**
|
|
169
|
+
* Union of the {@link PurgeValveState} values.
|
|
170
|
+
*/
|
|
171
|
+
export type PurgeValveState =
|
|
172
|
+
(typeof PurgeValveState)[keyof typeof PurgeValveState];
|
|
173
|
+
|
|
174
|
+
/** Default per-read silence timeout, matching libcomm14cux (100 ms). */
|
|
175
|
+
export const DEFAULT_READ_TIMEOUT_MS = 100;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
import { InvalidReadingError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
const DIRECT_MAX = 1023;
|
|
9
|
+
const LINEAR_MAX = 17290;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Decodes the "direct" mass airflow reading, which changes linearly with
|
|
13
|
+
* sensor voltage but logarithmically with airflow.
|
|
14
|
+
*
|
|
15
|
+
* @param raw - The 10-bit reading, 0 to 1023.
|
|
16
|
+
* @returns Airflow as a fraction from 0 to 1 of the highest measurement.
|
|
17
|
+
* @throws {@link InvalidReadingError} if `raw` is above 1023.
|
|
18
|
+
*/
|
|
19
|
+
export function decodeMafDirect(raw: number): number {
|
|
20
|
+
if (raw > DIRECT_MAX) {
|
|
21
|
+
throw new InvalidReadingError(`MAF direct reading out of range: ${raw}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return raw / DIRECT_MAX;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Decodes the "linearized" mass airflow reading, which changes linearly with
|
|
29
|
+
* airflow.
|
|
30
|
+
*
|
|
31
|
+
* @param raw - The reading, 0 to 17290.
|
|
32
|
+
* @returns Airflow as a fraction from 0 to 1 of the highest measurement.
|
|
33
|
+
* @throws {@link InvalidReadingError} if `raw` is above 17290.
|
|
34
|
+
*/
|
|
35
|
+
export function decodeMafLinear(raw: number): number {
|
|
36
|
+
if (raw > LINEAR_MAX) {
|
|
37
|
+
throw new InvalidReadingError(`MAF linear reading out of range: ${raw}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return raw / LINEAR_MAX;
|
|
41
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
import { InvalidReadingError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Converts a crank pulse width to engine speed (`7500000 / pulseWidth`,
|
|
10
|
+
* truncated).
|
|
11
|
+
*
|
|
12
|
+
* @param pulseWidth - Pulse width in ECU timer ticks. Must not be 0.
|
|
13
|
+
* @returns Engine speed in revolutions per minute.
|
|
14
|
+
* @throws {@link InvalidReadingError} if `pulseWidth` is 0. libcomm14cux
|
|
15
|
+
* divides by zero here, which is undefined behaviour in C.
|
|
16
|
+
*/
|
|
17
|
+
export function pulseWidthToRpm(pulseWidth: number): number {
|
|
18
|
+
if (pulseWidth === 0) {
|
|
19
|
+
// libcomm14cux divides by zero here (undefined behaviour in C).
|
|
20
|
+
throw new InvalidReadingError('Pulse width of zero');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return Math.trunc(7500000 / pulseWidth);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Decodes the engine speed from the filtered pulse width. 0xFFFF is the ECU's
|
|
28
|
+
* initial value with the ignition on and the engine stopped, and means 0 RPM.
|
|
29
|
+
*
|
|
30
|
+
* @param pulseWidth - The filtered pulse width, 0 to 0xFFFF.
|
|
31
|
+
* @returns Engine speed in revolutions per minute.
|
|
32
|
+
* @throws {@link InvalidReadingError} if `pulseWidth` is 0.
|
|
33
|
+
*/
|
|
34
|
+
export function decodeEngineRpm(pulseWidth: number): number {
|
|
35
|
+
return pulseWidth === 0xffff ? 0 : pulseWidthToRpm(pulseWidth);
|
|
36
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
// [byte offset from 0x0049, bit (0 = LSB), name]. Spare bits are omitted.
|
|
7
|
+
// Names follow the field names of `c14cux_faultcodes`.
|
|
8
|
+
const FAULT_BITS = [
|
|
9
|
+
[0, 0, 'romChecksumFailure'],
|
|
10
|
+
[0, 1, 'lambdaSensorOdd'],
|
|
11
|
+
[0, 2, 'lambdaSensorEven'],
|
|
12
|
+
[0, 4, 'misfireOddBank'],
|
|
13
|
+
[0, 5, 'misfireEvenBank'],
|
|
14
|
+
[0, 6, 'airflowMeter'],
|
|
15
|
+
[0, 7, 'tuneResistorOutOfRange'],
|
|
16
|
+
[1, 0, 'injectorOddBank'],
|
|
17
|
+
[1, 2, 'injectorEvenBank'],
|
|
18
|
+
[1, 3, 'coolantTempSensor'],
|
|
19
|
+
[1, 4, 'throttlePot'],
|
|
20
|
+
[1, 5, 'throttlePotHiMafLo'],
|
|
21
|
+
[1, 6, 'throttlePotLoMafHi'],
|
|
22
|
+
[1, 7, 'purgeValveLeak'],
|
|
23
|
+
[2, 1, 'mixtureTooLean'],
|
|
24
|
+
[2, 3, 'intakeAirLeak'],
|
|
25
|
+
[3, 0, 'lowFuelPressure'],
|
|
26
|
+
[3, 4, 'idleValveStepperMotor'],
|
|
27
|
+
[3, 6, 'roadSpeedSensor'],
|
|
28
|
+
[3, 7, 'neutralSwitch'],
|
|
29
|
+
[4, 4, 'lowFuelPressureOrAirLeak'],
|
|
30
|
+
[4, 5, 'fuelTempSensor'],
|
|
31
|
+
[5, 6, 'batteryDisconnected'],
|
|
32
|
+
[5, 7, 'ramChecksumFailure'],
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Name of a fault flag in {@link FaultCodes}, taken from the field names of
|
|
37
|
+
* the C `c14cux_faultcodes` struct.
|
|
38
|
+
*/
|
|
39
|
+
export type FaultCodeName = (typeof FAULT_BITS)[number][2];
|
|
40
|
+
|
|
41
|
+
/** Fault code flags (`c14cux_faultcodes`); `true` means the fault is set. */
|
|
42
|
+
export type FaultCodes = Record<FaultCodeName, boolean>;
|
|
43
|
+
|
|
44
|
+
/** Number of bytes in the fault code block at 0x0049. */
|
|
45
|
+
export const FAULT_CODE_BLOCK_SIZE = 6;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Decodes the fault code block into named flags. Spare bits are ignored.
|
|
49
|
+
*
|
|
50
|
+
* @param bytes - The six bytes read from 0x0049.
|
|
51
|
+
* @returns A flag for each fault; `true` means the fault is set.
|
|
52
|
+
*/
|
|
53
|
+
export function decodeFaultCodes(bytes: Uint8Array): FaultCodes {
|
|
54
|
+
const codes = {} as FaultCodes;
|
|
55
|
+
|
|
56
|
+
for (const [byteIndex, bit, name] of FAULT_BITS) {
|
|
57
|
+
codes[name] = (((bytes[byteIndex] ?? 0) >> bit) & 1) === 1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return codes;
|
|
61
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
import { PurgeValveState } from '../constants.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Decodes the fuel pump relay line. The relay is closed when bit 6 of port 1 is clear.
|
|
10
|
+
*
|
|
11
|
+
* @param port1 - The port 1 byte read from 0x0002.
|
|
12
|
+
* @returns `true` when the relay is closed (pump running).
|
|
13
|
+
*/
|
|
14
|
+
export const decodeFuelPumpRelay = (port1: number): boolean =>
|
|
15
|
+
(port1 & 0x40) === 0;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Decodes the malfunction indicator lamp. It is lit when bit 0 of port 1 is clear.
|
|
19
|
+
*
|
|
20
|
+
* @param port1 - The port 1 byte read from 0x0002.
|
|
21
|
+
* @returns `true` when the MIL is lit.
|
|
22
|
+
*/
|
|
23
|
+
export const decodeMilOn = (port1: number): boolean => (port1 & 0x01) === 0;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decodes idle mode, which is active when bit 0 of 0x2047 is set.
|
|
27
|
+
*
|
|
28
|
+
* @param byte - The byte read from 0x2047.
|
|
29
|
+
* @returns `true` when the ECU is driving an idle speed.
|
|
30
|
+
*/
|
|
31
|
+
export const decodeIdleMode = (byte: number): boolean => (byte & 0x01) === 0x01;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Decodes the heated screen state. It is on when bit 2 of 0x00DD is clear.
|
|
35
|
+
*
|
|
36
|
+
* @param bits - The flag byte read from its address.
|
|
37
|
+
* @returns `true` when the screen heater is on.
|
|
38
|
+
*/
|
|
39
|
+
export const decodeScreenHeater = (bits: number): boolean =>
|
|
40
|
+
(bits & 0x04) === 0;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Decodes the A/C compressor input. It is on when bit 3 of 0x008A is clear.
|
|
44
|
+
*
|
|
45
|
+
* @param bits - The flag byte read from its address.
|
|
46
|
+
* @returns `true` when the compressor is on.
|
|
47
|
+
*/
|
|
48
|
+
export const decodeAcCompressor = (bits: number): boolean =>
|
|
49
|
+
(bits & 0x08) === 0;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Decodes the purge valve state from the ECU's 16-bit purge timer, using the
|
|
53
|
+
* thresholds the ECU itself uses.
|
|
54
|
+
*
|
|
55
|
+
* @param timer - The value read from 0x0096.
|
|
56
|
+
* @returns Closed below 4000, toggling below 29000, otherwise open.
|
|
57
|
+
*/
|
|
58
|
+
export function decodePurgeValveState(timer: number): PurgeValveState {
|
|
59
|
+
if (timer < 4000) {
|
|
60
|
+
return PurgeValveState.Closed;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return timer < 29000 ? PurgeValveState.Toggling : PurgeValveState.Open;
|
|
64
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
DataOffsetRev,
|
|
8
|
+
FUEL_MAP_COLUMNS,
|
|
9
|
+
FUEL_MAP_ROWS,
|
|
10
|
+
FUEL_MAP_ROW_SCALER_OFFSET,
|
|
11
|
+
MemoryOffset,
|
|
12
|
+
} from '../constants.js';
|
|
13
|
+
import { InvalidReadingError } from '../errors.js';
|
|
14
|
+
import { pulseWidthToRpm } from './engineSpeed.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Where a fuel map lives in ECU memory.
|
|
18
|
+
*/
|
|
19
|
+
export interface FuelMapLocation {
|
|
20
|
+
/** Address of the 128-byte map; the 2-byte adjustment factor follows it. */
|
|
21
|
+
offset: number;
|
|
22
|
+
/** Address of the row scaler byte. */
|
|
23
|
+
scalerOffset: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const OLD_MAPS = [
|
|
27
|
+
MemoryOffset.OldFuelMap1,
|
|
28
|
+
MemoryOffset.OldFuelMap2,
|
|
29
|
+
MemoryOffset.OldFuelMap3,
|
|
30
|
+
MemoryOffset.OldFuelMap4,
|
|
31
|
+
MemoryOffset.OldFuelMap5,
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const NEW_MAPS = [
|
|
35
|
+
MemoryOffset.NewFuelMap1,
|
|
36
|
+
MemoryOffset.NewFuelMap2,
|
|
37
|
+
MemoryOffset.NewFuelMap3,
|
|
38
|
+
MemoryOffset.NewFuelMap4,
|
|
39
|
+
MemoryOffset.NewFuelMap5,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A {@link DataOffsetRev} that has been determined (not `Unset`).
|
|
44
|
+
*/
|
|
45
|
+
export type KnownDataOffsetRev = Exclude<DataOffsetRev, 0>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Rejects a fuel map id outside 0 to 5.
|
|
49
|
+
*
|
|
50
|
+
* @param id - The fuel map id to check.
|
|
51
|
+
* @throws {@link RangeError} if `id` is not an integer from 0 to 5.
|
|
52
|
+
*/
|
|
53
|
+
export function assertFuelMapId(id: number): void {
|
|
54
|
+
if (!Number.isInteger(id) || id < 0 || id > 5) {
|
|
55
|
+
throw new RangeError(`Invalid fuel map id: ${id}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Finds where a fuel map lives for the given ROM data layout.
|
|
61
|
+
*
|
|
62
|
+
* @param id - Map id, 0 to 5 (checked by {@link assertFuelMapId}).
|
|
63
|
+
* @param revision - The ROM's data layout.
|
|
64
|
+
* @returns The map's address and its row scaler's address.
|
|
65
|
+
*/
|
|
66
|
+
export function fuelMapLocation(
|
|
67
|
+
id: number,
|
|
68
|
+
revision: KnownDataOffsetRev,
|
|
69
|
+
): FuelMapLocation {
|
|
70
|
+
// Map 0 is at the same place in both layouts.
|
|
71
|
+
if (id === 0) {
|
|
72
|
+
return {
|
|
73
|
+
offset: MemoryOffset.FuelMap0,
|
|
74
|
+
scalerOffset: MemoryOffset.Map0RowScalerInitValue,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const maps = revision === DataOffsetRev.RevC ? NEW_MAPS : OLD_MAPS;
|
|
79
|
+
const offset = maps[id - 1] ?? 0;
|
|
80
|
+
|
|
81
|
+
return { offset, scalerOffset: offset + FUEL_MAP_ROW_SCALER_OFFSET };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Validates the current fuel map id.
|
|
86
|
+
*
|
|
87
|
+
* @param id - The byte read from 0x202C.
|
|
88
|
+
* @returns The map id, 0 to 5.
|
|
89
|
+
* @throws {@link InvalidReadingError} if `id` is above 5.
|
|
90
|
+
*/
|
|
91
|
+
export function decodeCurrentFuelMap(id: number): number {
|
|
92
|
+
if (id > 5) {
|
|
93
|
+
throw new InvalidReadingError(`Invalid fuel map id: ${id}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return id;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A position within the fuel map and its interpolation weighting.
|
|
101
|
+
*/
|
|
102
|
+
export interface FuelMapIndex {
|
|
103
|
+
/**
|
|
104
|
+
* The row or column index.
|
|
105
|
+
*/
|
|
106
|
+
index: number;
|
|
107
|
+
/**
|
|
108
|
+
* The weighting (0 to 15) used to interpolate toward the next row or column.
|
|
109
|
+
*/
|
|
110
|
+
weighting: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function decodeIndex(byte: number, limit: number, what: string): FuelMapIndex {
|
|
114
|
+
const index = byte >> 4;
|
|
115
|
+
|
|
116
|
+
if (index >= limit) {
|
|
117
|
+
throw new InvalidReadingError(`Invalid fuel map ${what} index: ${index}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { index, weighting: byte & 0x0f };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Decodes the row index byte: the high nibble is the row, the low nibble the
|
|
125
|
+
* weighting.
|
|
126
|
+
*
|
|
127
|
+
* @param byte - The byte read from 0x005B.
|
|
128
|
+
* @returns The row index (0 to 7) and weighting.
|
|
129
|
+
* @throws {@link InvalidReadingError} if the row index is 8 or more.
|
|
130
|
+
*/
|
|
131
|
+
export const decodeFuelMapRowIndex = (byte: number): FuelMapIndex =>
|
|
132
|
+
decodeIndex(byte, FUEL_MAP_ROWS, 'row');
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Decodes the column index byte: the high nibble is the column, the low nibble
|
|
136
|
+
* the weighting.
|
|
137
|
+
*
|
|
138
|
+
* @param byte - The byte read from 0x005C.
|
|
139
|
+
* @returns The column index (0 to 15) and weighting.
|
|
140
|
+
* @throws {@link InvalidReadingError} if the column index is 16 or more.
|
|
141
|
+
*/
|
|
142
|
+
export const decodeFuelMapColumnIndex = (byte: number): FuelMapIndex =>
|
|
143
|
+
decodeIndex(byte, FUEL_MAP_COLUMNS, 'column');
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Gives the address of one entry in the RPM table.
|
|
147
|
+
*
|
|
148
|
+
* @param column - Fuel map column, 0 to 15.
|
|
149
|
+
* @returns The address of the column's two-byte pulse width.
|
|
150
|
+
*/
|
|
151
|
+
export const rpmTableEntryAddress = (column: number): number =>
|
|
152
|
+
MemoryOffset.RPMTable + column * 4;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Gives the slot in the result array for an RPM table column. The C library
|
|
156
|
+
* stores column 0 last.
|
|
157
|
+
*
|
|
158
|
+
* @param column - Fuel map column, 0 to 15.
|
|
159
|
+
* @returns The index in the result array.
|
|
160
|
+
*/
|
|
161
|
+
export const rpmTableSlot = (column: number): number =>
|
|
162
|
+
FUEL_MAP_COLUMNS - column - 1;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Converts one RPM table pulse width to RPM; see {@link pulseWidthToRpm}.
|
|
166
|
+
*/
|
|
167
|
+
export const decodeRpmTableEntry = pulseWidthToRpm;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
import { Gear } from '../constants.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Decodes the gear selection from the neutral switch ADC byte. The thresholds
|
|
10
|
+
* are those hardcoded in the ECU firmware.
|
|
11
|
+
*
|
|
12
|
+
* @param adc - The byte read from 0x2000.
|
|
13
|
+
* @returns Park/neutral below 0x4D, drive/reverse above 0xB3, otherwise manual gearbox.
|
|
14
|
+
*/
|
|
15
|
+
export function decodeGear(adc: number): Gear {
|
|
16
|
+
if (adc < 0x4d) {
|
|
17
|
+
return Gear.ParkOrNeutral;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (adc > 0xb3) {
|
|
21
|
+
return Gear.DriveOrReverse;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Manual gearboxes have a resistor fitted to give a midpoint value.
|
|
25
|
+
return Gear.ManualGearbox;
|
|
26
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
const FULLY_CLOSED = 180;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Decodes the idle bypass motor position. 180 counts is fully closed.
|
|
10
|
+
*
|
|
11
|
+
* @param raw - The byte read from 0x006D.
|
|
12
|
+
* @returns Position as a fraction from 0 (closed) to 1 (widest opening).
|
|
13
|
+
*/
|
|
14
|
+
export function decodeIdleBypassPosition(raw: number): number {
|
|
15
|
+
return (FULLY_CLOSED - Math.min(raw, FULLY_CLOSED)) / FULLY_CLOSED;
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Decodes a lambda fueling trim. A larger number means more fuel.
|
|
8
|
+
*
|
|
9
|
+
* @param raw - The 16-bit value read from the trim location.
|
|
10
|
+
* @returns Trim in counts, from -256 to 255.
|
|
11
|
+
*/
|
|
12
|
+
export function decodeLambdaTrim(raw: number): number {
|
|
13
|
+
return Math.trunc(raw / 0x80) - 0x100;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Decodes the MAF CO trim voltage from the even-bank long-term trim value.
|
|
18
|
+
*
|
|
19
|
+
* @param raw - The 16-bit value read from 0x0046.
|
|
20
|
+
* @returns Voltage in volts.
|
|
21
|
+
*/
|
|
22
|
+
export function decodeCoTrimVoltage(raw: number): number {
|
|
23
|
+
return (5.0 * (raw >> 7)) / 1024.0;
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
import { InvalidReadingError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reverses the quadratic the ECU applies to the battery voltage ADC count,
|
|
10
|
+
* then maps the count linearly to volts.
|
|
11
|
+
*
|
|
12
|
+
* @param stored - The 16-bit value read from 0x0055.
|
|
13
|
+
* @param a - First coefficient (a byte).
|
|
14
|
+
* @param b - Second coefficient (a byte).
|
|
15
|
+
* @param c - Third coefficient (16 bits).
|
|
16
|
+
* @returns The main relay voltage in volts.
|
|
17
|
+
* @throws {@link InvalidReadingError} if the coefficients cannot produce a real
|
|
18
|
+
* result for `stored`.
|
|
19
|
+
*/
|
|
20
|
+
export function decodeMainVoltage(
|
|
21
|
+
stored: number,
|
|
22
|
+
a: number,
|
|
23
|
+
b: number,
|
|
24
|
+
c: number,
|
|
25
|
+
): number {
|
|
26
|
+
const discriminant = 4 * a * stored - a * c + 64 * b * b;
|
|
27
|
+
|
|
28
|
+
if (discriminant < 0) {
|
|
29
|
+
throw new InvalidReadingError(
|
|
30
|
+
'Main voltage coefficients do not fit reading',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const adcCount = Math.trunc((-16 * (Math.sqrt(discriminant) - 8 * b)) / a);
|
|
35
|
+
|
|
36
|
+
return 0.07 * adcCount - 0.09;
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Derived from libcomm14cux (https://github.com/colinbourassa/libcomm14cux)
|
|
3
|
+
// Copyright (C) Colin Bourassa. Licensed under the GNU GPL v3.
|
|
4
|
+
// Ported to TypeScript and modified for comm14cux-ts, 2026.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Converts the road speed byte to miles per hour.
|
|
8
|
+
*
|
|
9
|
+
* @param kph - The byte read from 0x2003, in kilometres per hour.
|
|
10
|
+
* @returns Speed in whole miles per hour, truncated.
|
|
11
|
+
*/
|
|
12
|
+
export function decodeRoadSpeedMph(kph: number): number {
|
|
13
|
+
return Math.trunc(kph * 0.621371192);
|
|
14
|
+
}
|