@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/dist/ecu.js
ADDED
|
@@ -0,0 +1,700 @@
|
|
|
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
|
+
import { be16 } from './bytes.js';
|
|
6
|
+
import { AirflowType, Bank, DataOffsetRev, DataSize, DEFAULT_READ_TIMEOUT_MS, FUEL_MAP_COLUMNS, MemoryOffset, RevAMainVoltageFactor, ThrottlePosType, } from './constants.js';
|
|
7
|
+
import { decodeAcCompressor, decodeFuelPumpRelay, decodeIdleMode, decodeMilOn, decodePurgeValveState, decodeScreenHeater, } from './decoders/flags.js';
|
|
8
|
+
import { decodeEngineRpm, pulseWidthToRpm } from './decoders/engineSpeed.js';
|
|
9
|
+
import { decodeGear } from './decoders/gear.js';
|
|
10
|
+
import { decodeIdleBypassPosition } from './decoders/idleBypass.js';
|
|
11
|
+
import { decodeMafDirect, decodeMafLinear } from './decoders/airflow.js';
|
|
12
|
+
import { decodeMainVoltage } from './decoders/mainVoltage.js';
|
|
13
|
+
import { decodeRoadSpeedMph } from './decoders/roadSpeed.js';
|
|
14
|
+
import { decodeTemperatureF } from './decoders/temperature.js';
|
|
15
|
+
import { decodeThrottlePosition, assertThrottleReading, } from './decoders/throttle.js';
|
|
16
|
+
import { decodeCoTrimVoltage, decodeLambdaTrim, } from './decoders/lambdaTrim.js';
|
|
17
|
+
import { FAULT_CODE_BLOCK_SIZE, decodeFaultCodes, } from './decoders/faultCodes.js';
|
|
18
|
+
import { decodeCurrentFuelMap, decodeFuelMapColumnIndex, decodeFuelMapRowIndex, assertFuelMapId, fuelMapLocation, rpmTableEntryAddress, rpmTableSlot, } from './decoders/fuelMap.js';
|
|
19
|
+
import { classifyOldRevision, isRevC } from './decoders/romRevision.js';
|
|
20
|
+
import { decodeTuneRevision, } from './decoders/tuneRevision.js';
|
|
21
|
+
import { InvalidReadingError, NotConnectedError } from './errors.js';
|
|
22
|
+
import { Protocol } from './protocol/protocol.js';
|
|
23
|
+
import { CommandQueue } from './queue.js';
|
|
24
|
+
import { LIBRARY_VERSION } from './version.js';
|
|
25
|
+
/**
|
|
26
|
+
* Connection to a 14CUX ECU. Mirrors the public API of libcomm14cux: each
|
|
27
|
+
* `c14cux_*` function is a method here, with its out-parameters returned as
|
|
28
|
+
* values and its `false` result thrown as an error.
|
|
29
|
+
*
|
|
30
|
+
* Every public operation is queued, so calls may be made concurrently.
|
|
31
|
+
* {@link Ecu.cancelRead} is the exception: it acts immediately.
|
|
32
|
+
*
|
|
33
|
+
* Unless a method says otherwise, it rejects with:
|
|
34
|
+
* - {@link NotConnectedError} if {@link Ecu.connect} has not been called;
|
|
35
|
+
* - {@link TimeoutError} if the ECU stops responding;
|
|
36
|
+
* - {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
37
|
+
*
|
|
38
|
+
* All of these extend {@link Comm14cuxError}.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const ecu = new Ecu(new WebSerialTransport(port));
|
|
43
|
+
*
|
|
44
|
+
* await ecu.connect();
|
|
45
|
+
* console.log(await ecu.getEngineRPM());
|
|
46
|
+
* await ecu.disconnect();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export class Ecu {
|
|
50
|
+
#transport;
|
|
51
|
+
#protocol;
|
|
52
|
+
#queue = new CommandQueue();
|
|
53
|
+
#connected = false;
|
|
54
|
+
// Operations are numbered when called. cancelRead() cancels the multi-chunk
|
|
55
|
+
// reads of every operation numbered up to #cancelledThrough.
|
|
56
|
+
#requested = 0;
|
|
57
|
+
#running = 0;
|
|
58
|
+
#cancelledThrough = 0;
|
|
59
|
+
#promRev = undefined;
|
|
60
|
+
#voltageFactorA = 0;
|
|
61
|
+
#voltageFactorB = 0;
|
|
62
|
+
#voltageFactorC = 0;
|
|
63
|
+
/**
|
|
64
|
+
* Creates a connection object. Nothing is sent until {@link Ecu.connect}.
|
|
65
|
+
*
|
|
66
|
+
* @param transport - The byte-level link to the ECU, for example a
|
|
67
|
+
* {@link WebSerialTransport} or a {@link SimulatedTransport}.
|
|
68
|
+
* @param options - Optional settings.
|
|
69
|
+
*/
|
|
70
|
+
constructor(transport, options = {}) {
|
|
71
|
+
this.#transport = transport;
|
|
72
|
+
this.#protocol = new Protocol(transport, options.readTimeoutMs ?? DEFAULT_READ_TIMEOUT_MS);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Returns the version of this library (`c14cux_getLibraryVersion`).
|
|
76
|
+
*
|
|
77
|
+
* @returns The major, minor and patch version numbers.
|
|
78
|
+
*/
|
|
79
|
+
static getLibraryVersion() {
|
|
80
|
+
return { ...LIBRARY_VERSION };
|
|
81
|
+
}
|
|
82
|
+
// ---- connection ---------------------------------------------------------
|
|
83
|
+
/**
|
|
84
|
+
* Reports whether the transport is open (`c14cux_isConnected`).
|
|
85
|
+
*
|
|
86
|
+
* @returns `true` after a successful {@link Ecu.connect} and before {@link Ecu.disconnect}.
|
|
87
|
+
*/
|
|
88
|
+
isConnected() {
|
|
89
|
+
return this.#connected;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Opens the transport (`c14cux_connect`). Does nothing if already connected.
|
|
93
|
+
*
|
|
94
|
+
* Unlike the C function this takes no device path or baud rate; those
|
|
95
|
+
* belong to the {@link Transport}.
|
|
96
|
+
*
|
|
97
|
+
* @returns A promise that resolves when the operation is complete.
|
|
98
|
+
* @throws Whatever the transport's `open()` rejects with, for example if the serial port is busy.
|
|
99
|
+
*/
|
|
100
|
+
connect() {
|
|
101
|
+
return this.#queue.run(async () => {
|
|
102
|
+
if (!this.#connected) {
|
|
103
|
+
await this.#transport.open();
|
|
104
|
+
this.#connected = true;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Closes the transport (`c14cux_disconnect`) and forgets cached ROM details. Does nothing if not connected.
|
|
110
|
+
*
|
|
111
|
+
* Waits for any operation in progress to finish first.
|
|
112
|
+
*
|
|
113
|
+
* @returns A promise that resolves when the operation is complete.
|
|
114
|
+
* @throws Whatever the transport's `close()` rejects with.
|
|
115
|
+
*/
|
|
116
|
+
disconnect() {
|
|
117
|
+
return this.#queue.run(async () => {
|
|
118
|
+
if (this.#connected) {
|
|
119
|
+
await this.#transport.close();
|
|
120
|
+
this.#connected = false;
|
|
121
|
+
this.#protocol.resetCache();
|
|
122
|
+
// A different ECU may be connected next time.
|
|
123
|
+
this.#promRev = undefined;
|
|
124
|
+
this.#voltageFactorA = 0;
|
|
125
|
+
this.#voltageFactorB = 0;
|
|
126
|
+
this.#voltageFactorC = 0;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Cancels every multi-chunk read requested before this call (`c14cux_cancelRead`).
|
|
132
|
+
*
|
|
133
|
+
* A read in progress stops after the chunk currently in flight; a read still
|
|
134
|
+
* waiting in the queue is cancelled before anything is sent. Each rejects
|
|
135
|
+
* with {@link ReadCancelledError}. Reads short enough for a single chunk
|
|
136
|
+
* (every getter except {@link Ecu.getFuelMap}) are not affected, and nor
|
|
137
|
+
* is anything requested after this call.
|
|
138
|
+
*
|
|
139
|
+
* Unlike every other method this acts immediately instead of waiting in the
|
|
140
|
+
* queue.
|
|
141
|
+
*/
|
|
142
|
+
cancelRead() {
|
|
143
|
+
this.#cancelledThrough = this.#requested;
|
|
144
|
+
}
|
|
145
|
+
// ---- raw access ---------------------------------------------------------
|
|
146
|
+
/**
|
|
147
|
+
* Reads a block of ECU memory (`c14cux_readMem`).
|
|
148
|
+
*
|
|
149
|
+
* Reads longer than the ECU can return in one command are split into
|
|
150
|
+
* chunks automatically.
|
|
151
|
+
*
|
|
152
|
+
* @param addr - First address to read, 0 to 0xFFFF.
|
|
153
|
+
* @param length - Number of bytes to read. `addr + length` must not exceed 0x10000.
|
|
154
|
+
* @returns The bytes read, `length` long.
|
|
155
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
156
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
157
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
158
|
+
* @throws {@link RangeError} if the address or length is out of range.
|
|
159
|
+
* @throws {@link ReadCancelledError} if {@link Ecu.cancelRead} was called before the read finished.
|
|
160
|
+
*/
|
|
161
|
+
readMem(addr, length) {
|
|
162
|
+
return this.#run(() => this.#read(addr, length));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Writes one byte to ECU memory (`c14cux_writeMem`).
|
|
166
|
+
*
|
|
167
|
+
* Writing to ECU memory can affect a running engine; use with care.
|
|
168
|
+
*
|
|
169
|
+
* @param addr - Address to write, 0 to 0xFFFF.
|
|
170
|
+
* @param value - Byte to write, 0 to 255.
|
|
171
|
+
* @returns A promise that resolves when the operation is complete.
|
|
172
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
173
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
174
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
175
|
+
* @throws {@link RangeError} if the address or value is out of range.
|
|
176
|
+
*/
|
|
177
|
+
writeMem(addr, value) {
|
|
178
|
+
return this.#run(() => this.#protocol.writeMem(addr, value));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Reads the entire 16 KiB firmware image from 0xC000 (`c14cux_dumpROM`).
|
|
182
|
+
*
|
|
183
|
+
* This takes several seconds; use {@link Ecu.cancelRead} to stop it.
|
|
184
|
+
*
|
|
185
|
+
* @returns The 0x4000 bytes of ROM.
|
|
186
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
187
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
188
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
189
|
+
* @throws {@link ReadCancelledError} if {@link Ecu.cancelRead} was called before the read finished.
|
|
190
|
+
*/
|
|
191
|
+
dumpROM() {
|
|
192
|
+
return this.readMem(MemoryOffset.ROMAddress, DataSize.ROM);
|
|
193
|
+
}
|
|
194
|
+
// ---- simple readings ----------------------------------------------------
|
|
195
|
+
/**
|
|
196
|
+
* Reads the vehicle road speed (`c14cux_getRoadSpeed`).
|
|
197
|
+
*
|
|
198
|
+
* @returns Road speed in whole miles per hour (the ECU reports km/h; the value is converted and truncated).
|
|
199
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
200
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
201
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
202
|
+
*/
|
|
203
|
+
getRoadSpeed() {
|
|
204
|
+
return this.#run(async () => decodeRoadSpeedMph(await this.#byte(MemoryOffset.RoadSpeed)));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Reads the engine coolant temperature (`c14cux_getCoolantTemp`).
|
|
208
|
+
*
|
|
209
|
+
* @returns Temperature in degrees Fahrenheit.
|
|
210
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
211
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
212
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
213
|
+
*/
|
|
214
|
+
getCoolantTemp() {
|
|
215
|
+
return this.#run(async () => decodeTemperatureF(await this.#byte(MemoryOffset.CoolantTemp)));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Reads the fuel temperature (`c14cux_getFuelTemp`).
|
|
219
|
+
*
|
|
220
|
+
* @returns Temperature in degrees Fahrenheit.
|
|
221
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
222
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
223
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
224
|
+
*/
|
|
225
|
+
getFuelTemp() {
|
|
226
|
+
return this.#run(async () => decodeTemperatureF(await this.#byte(MemoryOffset.FuelTemp)));
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Reads the mass airflow meter (`c14cux_getMAFReading`).
|
|
230
|
+
*
|
|
231
|
+
* @param type - {@link AirflowType.Direct} changes linearly with sensor voltage but logarithmically with airflow; {@link AirflowType.Linearized} changes linearly with airflow.
|
|
232
|
+
* @returns Airflow as a fraction from 0 to 1 of the highest possible measurement.
|
|
233
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
234
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
235
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
236
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce.
|
|
237
|
+
*/
|
|
238
|
+
getMAFReading(type) {
|
|
239
|
+
return this.#run(async () => type === AirflowType.Direct
|
|
240
|
+
? decodeMafDirect(await this.#word(MemoryOffset.MassAirflowDirect))
|
|
241
|
+
: decodeMafLinear(await this.#word(MemoryOffset.MassAirflowLinear)));
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Reads the engine speed (`c14cux_getEngineRPM`).
|
|
245
|
+
*
|
|
246
|
+
* @returns Engine speed in revolutions per minute; 0 when the ignition is on but the engine is not running.
|
|
247
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
248
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
249
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
250
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce. (a zero pulse width)
|
|
251
|
+
*/
|
|
252
|
+
getEngineRPM() {
|
|
253
|
+
return this.#run(async () => decodeEngineRpm(await this.#word(MemoryOffset.EngineSpeedFiltered)));
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Reads the rev limit (`c14cux_getRPMLimit`).
|
|
257
|
+
*
|
|
258
|
+
* @returns The RPM limit in revolutions per minute.
|
|
259
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
260
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
261
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
262
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce. (a zero pulse width)
|
|
263
|
+
*/
|
|
264
|
+
getRPMLimit() {
|
|
265
|
+
return this.#run(async () => pulseWidthToRpm(await this.#word(MemoryOffset.RPMLimit)));
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Reads the current target idle speed (`c14cux_getTargetIdle`).
|
|
269
|
+
*
|
|
270
|
+
* @returns Target idle speed in revolutions per minute.
|
|
271
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
272
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
273
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
274
|
+
*/
|
|
275
|
+
getTargetIdle() {
|
|
276
|
+
return this.#run(() => this.#word(MemoryOffset.TargetIdleSpeed));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Reads the throttle position (`c14cux_getThrottlePosition`).
|
|
280
|
+
*
|
|
281
|
+
* @param type - {@link ThrottlePosType.Absolute} is a simple fraction of the maximum ADC reading; {@link ThrottlePosType.Corrected} is adjusted so the lowest value the ECU has seen reads as 0.
|
|
282
|
+
* @returns Throttle position as a fraction from 0 (closed) to 1 (wide open).
|
|
283
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
284
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
285
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
286
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce.
|
|
287
|
+
*/
|
|
288
|
+
getThrottlePosition(type) {
|
|
289
|
+
return this.#run(async () => {
|
|
290
|
+
const raw = await this.#word(MemoryOffset.ThrottlePosition);
|
|
291
|
+
assertThrottleReading(raw);
|
|
292
|
+
const minimum = type === ThrottlePosType.Corrected
|
|
293
|
+
? await this.#word(MemoryOffset.ThrottleMinimumPosition)
|
|
294
|
+
: 0;
|
|
295
|
+
return decodeThrottlePosition(raw, minimum);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Reads the transmission gear selection (`c14cux_getGearSelection`).
|
|
300
|
+
*
|
|
301
|
+
* @returns Park/neutral, drive/reverse, or manual gearbox (which does not report a gear).
|
|
302
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
303
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
304
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
305
|
+
*/
|
|
306
|
+
getGearSelection() {
|
|
307
|
+
return this.#run(async () => decodeGear(await this.#byte(MemoryOffset.TransmissionGear)));
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Reads the idle bypass (idle air control) motor position (`c14cux_getIdleBypassMotorPosition`).
|
|
311
|
+
*
|
|
312
|
+
* @returns Position as a fraction from 0 (closed) to 1 (widest opening).
|
|
313
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
314
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
315
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
316
|
+
*/
|
|
317
|
+
getIdleBypassMotorPosition() {
|
|
318
|
+
return this.#run(async () => decodeIdleBypassPosition(await this.#byte(MemoryOffset.IdleBypassPosition)));
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Reads the injector pulse width (`c14cux_getInjectorPulseWidth`).
|
|
322
|
+
*
|
|
323
|
+
* The ECU uses one location for both banks, and which bank it describes at
|
|
324
|
+
* the moment of the read is nondeterministic. It is most useful in open-loop
|
|
325
|
+
* mode, where both banks match.
|
|
326
|
+
*
|
|
327
|
+
* @returns Pulse width in microseconds.
|
|
328
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
329
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
330
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
331
|
+
*/
|
|
332
|
+
getInjectorPulseWidth() {
|
|
333
|
+
return this.#run(() => this.#word(MemoryOffset.InjectorPulseWidth));
|
|
334
|
+
}
|
|
335
|
+
// ---- fuel trims ---------------------------------------------------------
|
|
336
|
+
/**
|
|
337
|
+
* Reads the short-term lambda fueling trim (`c14cux_getLambdaTrimShort`). A larger number means more fuel.
|
|
338
|
+
*
|
|
339
|
+
* @param bank - Which engine bank to read.
|
|
340
|
+
* @returns Trim in counts, from -256 to 255.
|
|
341
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
342
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
343
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
344
|
+
*/
|
|
345
|
+
getLambdaTrimShort(bank) {
|
|
346
|
+
return this.#run(async () => decodeLambdaTrim(await this.#word(bank === Bank.Odd
|
|
347
|
+
? MemoryOffset.ShortTermLambdaFuelingTrimOdd
|
|
348
|
+
: MemoryOffset.ShortTermLambdaFuelingTrimEven)));
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Reads the long-term lambda fueling trim (`c14cux_getLambdaTrimLong`). A larger number means more fuel.
|
|
352
|
+
*
|
|
353
|
+
* @param bank - Which engine bank to read.
|
|
354
|
+
* @returns Trim in counts, from -256 to 255.
|
|
355
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
356
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
357
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
358
|
+
*/
|
|
359
|
+
getLambdaTrimLong(bank) {
|
|
360
|
+
return this.#run(async () => decodeLambdaTrim(await this.#word(bank === Bank.Odd
|
|
361
|
+
? MemoryOffset.LongTermLambdaFuelingTrimOdd
|
|
362
|
+
: MemoryOffset.LongTermLambdaFuelingTrimEven)));
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Reads the MAF CO trim voltage (`c14cux_getCOTrimVoltage`).
|
|
366
|
+
*
|
|
367
|
+
* @returns Voltage in volts.
|
|
368
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
369
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
370
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
371
|
+
*/
|
|
372
|
+
getCOTrimVoltage() {
|
|
373
|
+
return this.#run(async () => decodeCoTrimVoltage(await this.#word(MemoryOffset.LongTermLambdaFuelingTrimEven)));
|
|
374
|
+
}
|
|
375
|
+
// ---- main voltage -------------------------------------------------------
|
|
376
|
+
/**
|
|
377
|
+
* Reads the voltage supplied to the ECU (`c14cux_getMainVoltage`).
|
|
378
|
+
*
|
|
379
|
+
* The first call also reads the ECU's ROM to find the coefficients needed to
|
|
380
|
+
* reverse its ADC computation; they are cached until {@link Ecu.disconnect}.
|
|
381
|
+
*
|
|
382
|
+
* @returns Main relay voltage in volts.
|
|
383
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
384
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
385
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
386
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce. (coefficients of zero, or a reading that does not fit them)
|
|
387
|
+
*/
|
|
388
|
+
getMainVoltage() {
|
|
389
|
+
return this.#run(async () => {
|
|
390
|
+
if (this.#voltageFactorA === 0 ||
|
|
391
|
+
this.#voltageFactorB === 0 ||
|
|
392
|
+
this.#voltageFactorC === 0) {
|
|
393
|
+
await this.#readVoltageCoefficients();
|
|
394
|
+
}
|
|
395
|
+
return decodeMainVoltage(await this.#word(MemoryOffset.MainVoltage), this.#voltageFactorA, this.#voltageFactorB, this.#voltageFactorC);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
// ---- fuel maps ----------------------------------------------------------
|
|
399
|
+
/**
|
|
400
|
+
* Reads a fuel map (`c14cux_getFuelMap`).
|
|
401
|
+
*
|
|
402
|
+
* The first call also detects the ROM's data layout, which determines where
|
|
403
|
+
* maps 1 to 5 are stored.
|
|
404
|
+
*
|
|
405
|
+
* @param fuelMapId - Map to read, 0 to 5.
|
|
406
|
+
* @returns The 128 bytes of map data (8 rows by 16 columns), the adjustment factor stored after it, and the row scaler.
|
|
407
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
408
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
409
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
410
|
+
* @throws {@link RangeError} if `fuelMapId` is not 0 to 5 (no I/O is performed).
|
|
411
|
+
* @throws {@link ReadCancelledError} if {@link Ecu.cancelRead} was called before the map data was read.
|
|
412
|
+
*/
|
|
413
|
+
getFuelMap(fuelMapId) {
|
|
414
|
+
try {
|
|
415
|
+
assertFuelMapId(fuelMapId);
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
return Promise.reject(error);
|
|
419
|
+
}
|
|
420
|
+
return this.#run(async () => {
|
|
421
|
+
const location = fuelMapLocation(fuelMapId, await this.#determineDataOffsets());
|
|
422
|
+
const data = await this.#read(location.offset, DataSize.FuelMap);
|
|
423
|
+
const adjustmentFactor = await this.#word(location.offset + DataSize.FuelMap);
|
|
424
|
+
const rowScaler = await this.#byte(location.scalerOffset);
|
|
425
|
+
return { data, adjustmentFactor, rowScaler };
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Reads which fuel map is in use (`c14cux_getCurrentFuelMap`).
|
|
430
|
+
*
|
|
431
|
+
* Selected by a tune resistor in the harness on non-NAS Land Rovers;
|
|
432
|
+
* unmodified NAS vehicles are locked to map 5.
|
|
433
|
+
*
|
|
434
|
+
* @returns The map id, 0 to 5.
|
|
435
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
436
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
437
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
438
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce.
|
|
439
|
+
*/
|
|
440
|
+
getCurrentFuelMap() {
|
|
441
|
+
return this.#run(async () => decodeCurrentFuelMap(await this.#byte(MemoryOffset.CurrentFuelMapId)));
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Reads the current fuel map row index (`c14cux_getFuelMapRowIndex`).
|
|
445
|
+
*
|
|
446
|
+
* @returns The row index (0 to 7) and the row weighting.
|
|
447
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
448
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
449
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
450
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce.
|
|
451
|
+
*/
|
|
452
|
+
getFuelMapRowIndex() {
|
|
453
|
+
return this.#run(async () => decodeFuelMapRowIndex(await this.#byte(MemoryOffset.FuelMapRowIndex)));
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Reads the current fuel map column index (`c14cux_getFuelMapColumnIndex`).
|
|
457
|
+
*
|
|
458
|
+
* @returns The column index (0 to 15) and the column weighting.
|
|
459
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
460
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
461
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
462
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce.
|
|
463
|
+
*/
|
|
464
|
+
getFuelMapColumnIndex() {
|
|
465
|
+
return this.#run(async () => decodeFuelMapColumnIndex(await this.#byte(MemoryOffset.FuelMapColumnIndex)));
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Reads the table of RPM thresholds that divides engine speed into the sixteen fuel map columns (`c14cux_getRpmTable`).
|
|
469
|
+
*
|
|
470
|
+
* @returns Sixteen RPM values, indexed as in the C library (column 0 is last).
|
|
471
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
472
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
473
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
474
|
+
* @throws {@link InvalidReadingError} if the ECU returns a value outside the range its firmware can produce. (a zero pulse width)
|
|
475
|
+
*/
|
|
476
|
+
getRpmTable() {
|
|
477
|
+
return this.#run(async () => {
|
|
478
|
+
const table = new Array(FUEL_MAP_COLUMNS).fill(0);
|
|
479
|
+
for (let column = 0; column < FUEL_MAP_COLUMNS; column++) {
|
|
480
|
+
table[rpmTableSlot(column)] = pulseWidthToRpm(await this.#word(rpmTableEntryAddress(column)));
|
|
481
|
+
}
|
|
482
|
+
return table;
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
// ---- faults and state ---------------------------------------------------
|
|
486
|
+
/**
|
|
487
|
+
* Reads the stored fault codes (`c14cux_getFaultCodes`).
|
|
488
|
+
*
|
|
489
|
+
* @returns A flag for each fault; `true` means the fault is set.
|
|
490
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
491
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
492
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
493
|
+
*/
|
|
494
|
+
getFaultCodes() {
|
|
495
|
+
return this.#run(async () => decodeFaultCodes(await this.#read(MemoryOffset.FaultCodes, FAULT_CODE_BLOCK_SIZE)));
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Clears the stored fault codes by writing zero to each byte of the fault block (`c14cux_clearFaultCodes`).
|
|
499
|
+
*
|
|
500
|
+
* Stops at the first failed write, so the codes may be partly cleared.
|
|
501
|
+
*
|
|
502
|
+
* @returns A promise that resolves when the operation is complete.
|
|
503
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
504
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
505
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
506
|
+
*/
|
|
507
|
+
clearFaultCodes() {
|
|
508
|
+
return this.#run(async () => {
|
|
509
|
+
for (let i = 0; i < FAULT_CODE_BLOCK_SIZE; i++) {
|
|
510
|
+
await this.#protocol.writeMem(MemoryOffset.FaultCodes + i, 0x00);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Reads the state of the line driving the fuel pump relay (`c14cux_getFuelPumpRelayState`).
|
|
516
|
+
*
|
|
517
|
+
* @returns `true` when the relay is closed (pump running).
|
|
518
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
519
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
520
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
521
|
+
*/
|
|
522
|
+
getFuelPumpRelayState() {
|
|
523
|
+
return this.#run(async () => decodeFuelPumpRelay(await this.#byte(MemoryOffset.Port1)));
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Reads the malfunction indicator lamp state (`c14cux_isMILOn`). A lit MIL implies at least one fault code, but not every fault lights it.
|
|
527
|
+
*
|
|
528
|
+
* @returns `true` when the MIL is lit.
|
|
529
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
530
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
531
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
532
|
+
*/
|
|
533
|
+
isMILOn() {
|
|
534
|
+
return this.#run(async () => decodeMilOn(await this.#byte(MemoryOffset.Port1)));
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Reads whether the ECU is driving an idle speed (`c14cux_getIdleMode`).
|
|
538
|
+
*
|
|
539
|
+
* @returns `true` in idle mode.
|
|
540
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
541
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
542
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
543
|
+
*/
|
|
544
|
+
getIdleMode() {
|
|
545
|
+
return this.#run(async () => decodeIdleMode(await this.#byte(MemoryOffset.IdleMode)));
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Reads the state of the carbon canister purge valve (`c14cux_getPurgeValveState`).
|
|
549
|
+
*
|
|
550
|
+
* @returns Closed, toggling, or open.
|
|
551
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
552
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
553
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
554
|
+
*/
|
|
555
|
+
getPurgeValveState() {
|
|
556
|
+
return this.#run(async () => decodePurgeValveState(await this.#word(MemoryOffset.PurgeValveState)));
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Reads the heated screen state (`c14cux_getScreenHeaterState`).
|
|
560
|
+
*
|
|
561
|
+
* @returns `true` when the screen heater is on.
|
|
562
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
563
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
564
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
565
|
+
*/
|
|
566
|
+
getScreenHeaterState() {
|
|
567
|
+
return this.#run(async () => decodeScreenHeater(await this.#byte(MemoryOffset.Bits00DD)));
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Reads the A/C compressor load input (`c14cux_getACCompressorState`).
|
|
571
|
+
*
|
|
572
|
+
* @returns `true` when the compressor is on.
|
|
573
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
574
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
575
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
576
|
+
*/
|
|
577
|
+
getACCompressorState() {
|
|
578
|
+
return this.#run(async () => decodeAcCompressor(await this.#byte(MemoryOffset.Bits008A)));
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Reads the tune identification from the ROM (`c14cux_getTuneRevision`).
|
|
582
|
+
*
|
|
583
|
+
* @returns The decimal tune number, checksum fixer byte and ident word.
|
|
584
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
585
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
586
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
587
|
+
*/
|
|
588
|
+
getTuneRevision() {
|
|
589
|
+
return this.#run(async () => decodeTuneRevision(await this.#read(MemoryOffset.TuneRevision, 5)));
|
|
590
|
+
}
|
|
591
|
+
// ---- actuators ----------------------------------------------------------
|
|
592
|
+
/**
|
|
593
|
+
* Closes the fuel pump relay for a single timeout period of about two seconds (`c14cux_runFuelPump`).
|
|
594
|
+
*
|
|
595
|
+
* Affects a running engine; use with care.
|
|
596
|
+
*
|
|
597
|
+
* @returns A promise that resolves when the operation is complete.
|
|
598
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
599
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
600
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
601
|
+
*/
|
|
602
|
+
runFuelPump() {
|
|
603
|
+
return this.#run(async () => {
|
|
604
|
+
const port1 = await this.#byte(MemoryOffset.Port1);
|
|
605
|
+
await this.#protocol.writeMem(MemoryOffset.FuelPumpTimer, 0xff);
|
|
606
|
+
await this.#protocol.writeMem(MemoryOffset.Port1, port1 & 0xbf);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Commands the idle air control motor to move (`c14cux_driveIdleAirControlMotor`).
|
|
611
|
+
*
|
|
612
|
+
* Unlike the C function this reports failure if any step fails. Affects a
|
|
613
|
+
* running engine; use with care.
|
|
614
|
+
*
|
|
615
|
+
* @param direction - 0 opens the valve; any other value closes it.
|
|
616
|
+
* @param steps - Number of steps to travel, 0 to 255.
|
|
617
|
+
* @returns A promise that resolves when the operation is complete.
|
|
618
|
+
* @throws {@link NotConnectedError} if the ECU is not connected.
|
|
619
|
+
* @throws {@link TimeoutError} if the ECU stops responding.
|
|
620
|
+
* @throws {@link ProtocolError} if the ECU echoes a command byte incorrectly.
|
|
621
|
+
* @throws {@link RangeError} if `steps` is not an integer from 0 to 255 (no I/O is performed).
|
|
622
|
+
*/
|
|
623
|
+
driveIdleAirControlMotor(direction, steps) {
|
|
624
|
+
// Checked up front: the direction bit is written before the step count.
|
|
625
|
+
if (!Number.isInteger(steps) || steps < 0 || steps > 0xff) {
|
|
626
|
+
return Promise.reject(new RangeError(`Invalid step count: ${steps}`));
|
|
627
|
+
}
|
|
628
|
+
return this.#run(async () => {
|
|
629
|
+
const bits = await this.#byte(MemoryOffset.Bits008A);
|
|
630
|
+
await this.#protocol.writeMem(MemoryOffset.Bits008A, direction === 0 ? bits & 0xfe : bits | 0x01);
|
|
631
|
+
await this.#protocol.writeMem(MemoryOffset.IdleAirControlStepCount, steps);
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
// ---- internals ----------------------------------------------------------
|
|
635
|
+
#run(task) {
|
|
636
|
+
const ticket = ++this.#requested;
|
|
637
|
+
return this.#queue.run(() => {
|
|
638
|
+
if (!this.#connected) {
|
|
639
|
+
return Promise.reject(new NotConnectedError('Not connected to ECU'));
|
|
640
|
+
}
|
|
641
|
+
this.#running = ticket;
|
|
642
|
+
return task();
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
#read(addr, length) {
|
|
646
|
+
return this.#protocol.readMem(addr, length, () => this.#running <= this.#cancelledThrough);
|
|
647
|
+
}
|
|
648
|
+
async #byte(addr) {
|
|
649
|
+
return (await this.#read(addr, 1))[0] ?? 0;
|
|
650
|
+
}
|
|
651
|
+
async #word(addr) {
|
|
652
|
+
return be16(await this.#read(addr, 2));
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Works out the ROM's data layout (`c14cux_determineDataOffsets`) and caches
|
|
656
|
+
* it. Does nothing if already known.
|
|
657
|
+
*
|
|
658
|
+
* @returns The ROM's data layout.
|
|
659
|
+
*/
|
|
660
|
+
async #determineDataOffsets() {
|
|
661
|
+
if (this.#promRev !== undefined) {
|
|
662
|
+
return this.#promRev;
|
|
663
|
+
}
|
|
664
|
+
const firstRow = await this.#read(MemoryOffset.OldFuelMap1, FUEL_MAP_COLUMNS);
|
|
665
|
+
// Very old units (about 1990 and earlier) hardcode some main-voltage
|
|
666
|
+
// factors, so check for that as well.
|
|
667
|
+
this.#promRev = isRevC(firstRow)
|
|
668
|
+
? DataOffsetRev.RevC
|
|
669
|
+
: classifyOldRevision(await this.#byte(MemoryOffset.RevBMainVoltageFactorA));
|
|
670
|
+
return this.#promRev;
|
|
671
|
+
}
|
|
672
|
+
async #readVoltageCoefficients() {
|
|
673
|
+
const rev = await this.#determineDataOffsets();
|
|
674
|
+
let a;
|
|
675
|
+
let b;
|
|
676
|
+
let c;
|
|
677
|
+
if (rev === DataOffsetRev.RevA) {
|
|
678
|
+
({ A: a, B: b, C: c } = RevAMainVoltageFactor);
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
const revC = rev === DataOffsetRev.RevC;
|
|
682
|
+
a = await this.#byte(revC
|
|
683
|
+
? MemoryOffset.RevCMainVoltageFactorA
|
|
684
|
+
: MemoryOffset.RevBMainVoltageFactorA);
|
|
685
|
+
b = await this.#byte(revC
|
|
686
|
+
? MemoryOffset.RevCMainVoltageFactorB
|
|
687
|
+
: MemoryOffset.RevBMainVoltageFactorB);
|
|
688
|
+
c = await this.#word(revC
|
|
689
|
+
? MemoryOffset.RevCMainVoltageFactorC
|
|
690
|
+
: MemoryOffset.RevBMainVoltageFactorC);
|
|
691
|
+
}
|
|
692
|
+
if (a === 0 || b === 0 || c === 0) {
|
|
693
|
+
throw new InvalidReadingError('ECU reports zero main-voltage factors');
|
|
694
|
+
}
|
|
695
|
+
this.#voltageFactorA = a;
|
|
696
|
+
this.#voltageFactorB = b;
|
|
697
|
+
this.#voltageFactorC = c;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
//# sourceMappingURL=ecu.js.map
|