elm327
Version:
Node.js/TypeScript library for ELM327 OBD2 adapters over USB, Bluetooth and WiFi
156 lines (138 loc) • 5.46 kB
JavaScript
/**
* ============================================================
* Flow Control Example — elm327
* ============================================================
*
* This example demonstrates how to configure Flow Control (AT FC)
* for ISO-TP multiframe messages (like VIN retrieval via Mode 09).
*
* Flow Control is essential for proper communication with ECUs
* that send multi-frame responses (like VIN, calibration IDs, etc.)
*
* ── Prerequisites ─────────────────────────────────────────
*
* 1. Build the project:
* npm run build
*
* 2. Plug your ELM327 adapter into the car (ignition ON)
* and into your computer.
*
* ── How to run ────────────────────────────────────────────
*
* Auto-detect serial port:
* node examples/flow-control.js
*
* Specify port manually:
* node examples/flow-control.js /dev/ttyUSB0 # Linux
* node examples/flow-control.js /dev/tty.usbserial-XXXX # macOS
* node examples/flow-control.js COM3 # Windows
*
* ── Flow Control Explained ────────────────────────────────
*
* For ISO-TP (CAN) multiframe messages, the ECU needs to know
* where to send Flow Control frames. The ELM327 uses these AT commands:
*
* - AT FC SH <header> : Set Flow Control Header (ECU response ID + 8)
* - AT FC SD <data> : Set Flow Control Data (up to 5 bytes)
* - AT FC SM <mode> : Set Flow Control Mode (0=normal, 1=continuous)
* - AT CFC1/CFC0 : Enable/Disable Flow Control
*
* Typical configuration for standard OBD-II:
* - Header: 0x7E0 (request) -> Flow Control: 0x7E8 (response + 8)
*
* ============================================================
*/
const { listSerialPorts, OBD2_COMMANDS, OBD2Client } = require('../dist');
async function main() {
const port = process.argv[2];
if (port) {
await runWithPort(port);
} else {
console.log('Listing available serial ports...\n');
const ports = await listSerialPorts();
if (ports.length === 0) {
console.log('No serial ports found.');
console.log('');
console.log('Make sure your OBD2 adapter is plugged in via USB.');
return;
}
console.log('Available ports:');
for (const p of ports) {
console.log(` - ${p.path} (${p.manufacturer || 'Unknown'})`);
}
console.log('');
await runWithPort(ports[0].path);
}
}
async function runWithPort(port) {
// Configuration with Flow Control enabled
const config = {
type: 'serial',
port: port,
baudRate: 38400,
timeout: 10000, // Longer timeout for multiframe
// Flow Control configuration for ISO-TP multiframe
flowControl: {
enabled: true, // Enable Flow Control (AT CFC1)
header: '07E0', // Request ID (standard OBD-II)
// Flow Control response ID is typically request + 8 (0x7E0 -> 0x7E8)
// The ELM327 automatically calculates this, but we can set it explicitly
data: '', // No additional data bytes needed for standard OBD
mode: 0, // Normal mode (wait for Flow Control frames)
},
};
const client = new OBD2Client(config);
client.on('connected', () => console.log('[✓] Connected to adapter'));
client.on('ready', (info) => {
console.log('[✓] Adapter initialized:');
console.log(` Version: ${info.version}`);
console.log(` Device: ${info.device}`);
console.log(` Protocol: ${info.protocol}`);
console.log('');
});
client.on('error', (error) => console.error('[✗] Error:', error.message));
try {
console.log(`Connecting to ${port} with Flow Control enabled...`);
await client.connect();
console.log('');
console.log('Reading VIN using ISO-TP multiframe (with Flow Control)...');
console.log('');
// Get VIN - this uses Mode 09 PID 02 which requires multiframe ISO-TP
const vinCommand = OBD2_COMMANDS.VIN;
if (vinCommand) {
try {
const vinResponse = await client.queryCommand(vinCommand);
console.log(` VIN: ${vinResponse.value}`);
} catch (error) {
console.error(` [✗] Failed to get VIN: ${error instanceof Error ? error.message : error}`);
console.log(' (This is normal if your adapter/cloned ELM327 does not support multiframe)');
}
}
console.log('');
console.log('Reading other O2 sensor data...');
console.log('');
// Try reading some oxygen sensor data (these are single-frame, but good to test)
const o2Sensors = [
{ cmd: OBD2_COMMANDS.O2S1_WR, name: 'O2 Sensor 1 Wide Range Ratio' },
{ cmd: OBD2_COMMANDS.O2S1_V, name: 'O2 Sensor 1 Voltage' },
];
for (const sensor of o2Sensors) {
if (sensor.cmd) {
try {
const response = await client.queryCommand(sensor.cmd);
console.log(` ${sensor.name}: ${response.value} ${response.unit || ''}`);
} catch {
console.log(` ${sensor.name}: Not available`);
}
}
}
} catch (error) {
console.error('');
console.error('[✗] Failed:', error instanceof Error ? error.message : error);
} finally {
await client.disconnect();
console.log('');
console.log('Disconnected.');
}
}
main().catch(console.error);