hondavert-dev/lib/core/protocol/s300_parser.dart
2026-07-27 00:23:33 +05:30

158 lines
4.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:typed_data';
import 'sensor_state.dart';
import 'temp_table.dart';
import 'neg8.dart';
/// Parses a 128-byte S300 ECU response frame into a [SensorState].
/// Throws [ArgumentError] if frame length is wrong, or if [validateChecksum]
/// is true and the NEG8 checksum fails.
SensorState parseS300(Uint8List frame, {bool validateChecksum = true}) {
if (frame.length != 128) {
throw ArgumentError('S300 frame must be 128 bytes, got ${frame.length}');
}
if (validateChecksum && !validateFrame(frame)) {
throw ArgumentError('S300 frame NEG8 checksum failed');
}
// RPM: bytes 2..3 uint16 BE, used directly.
// The S300 live frame starts its sensor payload at byte 2; byte 0..1 are
// the protocol header. Reading from byte 3 shifts all high-rate values.
final int rpmRaw = _readUint16BE(frame, 2);
final double rpm = (rpmRaw - 256).clamp(0, 12000).toDouble();
// VSS: bytes 5..6 little-endian pulse period.
final int vssRaw = _readUint16LE(frame, 5);
final double vss =
(vssRaw < 893 || vssRaw == 0xFFFF) ? 0.0 : 144256.0 / vssRaw;
// MAP: bytes 7..8 little-endian. SManager displays this as gauge pressure
// in its "/psi column rather than raw absolute kPa.
final int mapRaw = _readUint16LE(frame, 7);
final double map = ((mapRaw / 10.0) - 103.8) * 0.1450377377;
// TPS: byte 9
final int tpsRaw = frame[9];
final double tps = ((tpsRaw - 25) * 100.0 / (239 - 25)).clamp(0.0, 100.0);
// INJ: bytes 10..11 little-endian timer ticks.
final int injRaw = _readUint16LE(frame, 10);
final double inj = injRaw / 240.5;
// IGN: byte 12 — SManager-aligned degrees.
final double ign = (frame[12] - 66) / 2.0;
// O2: byte 16, 0..5V scaled over 8-bit ADC range.
final double o2 = frame[16] * 5.0 / 256.0;
// SW bitmaps
final int sw1 = frame[0x11]; // +17
final int sw2 = frame[0x12]; // +18
final int sw3 = frame[0x13]; // +19
final int sw5 = frame[0x43]; // +67
// Gear: byte 0x27 = +39 decimal... wait, +27 hex = decimal 39
final int gear = frame[0x27];
// Strim: byte 0x29 hex = 41 decimal
final double strim = frame[0x29].toDouble();
// Ltrim: byte 0x2B hex = 43 decimal
final double ltrim = frame[0x2B].toDouble();
// PA: byte 0x3C hex = 60 decimal
final double pa = frame[0x3C].toDouble();
// ECT: byte 0x2D hex = 45 decimal
final double ect = (tempXlt[frame[0x2D]] + 40).toDouble();
// IAT: byte 0x2E hex = 46 decimal
final double iat = (tempXlt[frame[0x2E]] + 40).toDouble();
// BAT: byte 0x24 hex = 36 decimal
final double bat = frame[0x24] * 26.0 / 270.0;
// ERR bytes: 0x31..0x34 (4 bytes for S300)
final List<int> errBytes = [
frame[0x31],
frame[0x32],
frame[0x33],
frame[0x34],
];
// Eth: byte 0x38 hex = 56 decimal
final double eth = frame[0x38].toDouble();
// AFR: byte 0x34 overlaps with ERR03 in the spec — use it as raw AFR
final double afr = frame[0x34].toDouble();
// AIN0AIN7: decimal offsets 82..97 (uint16 BE each)
final double ain0 = _readUint16BE(frame, 82).toDouble();
final double ain1 = _readUint16BE(frame, 84).toDouble();
final double ain2 = _readUint16BE(frame, 86).toDouble();
final double ain3 = _readUint16BE(frame, 88).toDouble();
final double ain4 = _readUint16BE(frame, 90).toDouble();
final double ain5 = _readUint16BE(frame, 92).toDouble();
final double ain6 = _readUint16BE(frame, 94).toDouble();
final double ain7 = _readUint16BE(frame, 96).toDouble();
// Flags
// SW1 bit3 = REVL
final bool revLimit = (sw1 & 0x08) != 0;
// SW2 bit5 = MIL
final bool mil = (sw2 & 0x20) != 0;
// SW2 bit2 = Fuel (cut)
final bool fuelCut = (sw2 & 0x04) != 0;
// SW2 bit7 = ALTC → fanOut; also SW5 bit0 = FANC
final bool fanOut = (sw2 & 0x80) != 0 || (sw5 & 0x01) != 0;
// SW2 bit1..0 = VTS (VTEC solenoid — nonzero = active)
final bool vtec = (sw2 & 0x03) != 0;
// Knock = KRtrd > 0 (byte 13)
final bool knock = frame[13] > 0;
// SW3 bit7 = LnchC
final bool launch = (sw3 & 0x80) != 0;
return SensorState(
rpm: rpm,
vss: vss,
map: map,
tps: tps,
inj: inj,
ign: ign,
ect: ect,
iat: iat,
bat: bat,
o2: o2,
gear: gear,
eth: eth,
pa: pa,
afr: afr,
strim: strim,
ltrim: ltrim,
ain0: ain0,
ain1: ain1,
ain2: ain2,
ain3: ain3,
ain4: ain4,
ain5: ain5,
ain6: ain6,
ain7: ain7,
mil: mil,
fuelCut: fuelCut,
fanOut: fanOut,
vtec: vtec,
knock: knock,
revLimit: revLimit,
launch: launch,
errBytes: errBytes,
timestamp: DateTime.now(),
);
}
int _readUint16BE(Uint8List frame, int offset) {
return (frame[offset] << 8) | frame[offset + 1];
}
int _readUint16LE(Uint8List frame, int offset) {
return frame[offset] | (frame[offset + 1] << 8);
}