hondavert-dev/lib/core/protocol/s300_parser.dart
HVBT Dev 11cf7c2b63 v1.0.1 — Phase 1 complete + BT connection test UI
- Phase 1 core protocol: temp_table, neg8, sensor_state, s300_parser,
  kpro_parser, dtc_map, sensor_defs (50/50 tests passing)
- BT layer: bt_service.dart, bt_poller.dart (100ms poll, NEG8 validation)
- Connection test UI: device picker, protocol selector, live sensor screen
  with LIVE DATA + DEBUG LOG tabs
- Runtime BT permission request (Android 12+) + auto-enable Bluetooth
- Android: minSdk=26, all BT+location permissions in manifest
- Fixed flutter_bluetooth_serial namespace for AGP compatibility
2026-04-12 18:07:37 +05:30

150 lines
4.1 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 NEG8 checksum fails.
SensorState parseS300(Uint8List frame) {
if (frame.length != 128) {
throw ArgumentError('S300 frame must be 128 bytes, got ${frame.length}');
}
if (!validateFrame(frame)) {
throw ArgumentError('S300 frame NEG8 checksum failed');
}
// RPM: bytes 3..4 uint16 BE, used directly
final int rpmRaw = _readUint16BE(frame, 3);
final double rpm = rpmRaw.toDouble();
// VSS: bytes 5..6
final int vssRaw = _readUint16BE(frame, 5);
final double vss =
(vssRaw < 893 || vssRaw == 0xFFFF) ? 0.0 : 228480.0 / vssRaw;
// MAP: bytes 7..8 — raw / 10 = kPa
final int mapRaw = _readUint16BE(frame, 7);
final double map = mapRaw / 10.0;
// TPS: byte 9
final int tpsRaw = frame[9];
final double tps = (tpsRaw < 25) ? 0.0 : (tpsRaw * 51.0) / 46.0;
// INJ: bytes 10..11 raw ms
final int injRaw = _readUint16BE(frame, 10);
final double inj = injRaw.toDouble();
// IGN: byte 12 — (raw + 120) / 2
final double ign = (frame[12] + 120) / 2.0;
// O2: byte 16 raw
final double o2 = frame[16].toDouble();
// 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 0x2C hex = 44 decimal
final double pa = frame[0x2C].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 0x30 hex = 48 decimal
final double bat = frame[0x30] * 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];
}