Checked against the USB tool on the bench (3210 rpm): - O2: raw x 5/256 V (raw 195 -> 3.81 V, as the USB tool shows). - New derived sensor: injector duty = INJ ms x RPM / 1200 (16.67 ms @ 3210 rpm -> 44 %, as the USB tool shows). - Default dashboard flags: MIL, Fuel Pump, FAN Out, VTEC Out (the requirement's "FuelCut" is the fuel pump flag on the S300). - Log one raw frame (bytes 0-63) every 5 s so USB-tool readings can be matched to bytes; INJ, IGN, TPS, MIL, AF and PA still need that check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
137 lines
5.7 KiB
Dart
137 lines
5.7 KiB
Dart
import '../protocol/sensor_state.dart';
|
||
import 'sensor_def.dart';
|
||
import 'flag_def.dart';
|
||
|
||
/// Master list of all analog sensor definitions. Units, ranges and decimals
|
||
/// are the base (metric) ones; see units.dart for the displayed unit.
|
||
const List<SensorDef> sensorDefs = [
|
||
SensorDef(
|
||
id: 'rpm', displayName: 'RPM', unit: 'rpm', min: 0, max: 9000, decimals: 0),
|
||
SensorDef(
|
||
id: 'vss', displayName: 'Speed', unit: 'km/h', min: 0, max: 280, decimals: 0),
|
||
SensorDef(
|
||
id: 'map', displayName: 'Manifold pressure', unit: 'kPa', min: 0, max: 300),
|
||
SensorDef(
|
||
id: 'tps', displayName: 'Throttle position', unit: '%', min: 0, max: 100),
|
||
SensorDef(
|
||
id: 'inj',
|
||
displayName: 'Injector duration',
|
||
unit: 'ms',
|
||
min: 0,
|
||
max: 20,
|
||
decimals: 2),
|
||
SensorDef(
|
||
id: 'ign', displayName: 'Timing advance', unit: '°', min: -20, max: 60),
|
||
SensorDef(
|
||
id: 'ect', displayName: 'Coolant temp', unit: '°C', min: -40, max: 150),
|
||
SensorDef(
|
||
id: 'iat',
|
||
displayName: 'Intake air temp',
|
||
unit: '°C',
|
||
min: -40,
|
||
max: 150),
|
||
SensorDef(
|
||
id: 'bat', displayName: 'Battery voltage', unit: 'V', min: 0, max: 20),
|
||
SensorDef(
|
||
id: 'o2', displayName: 'O2 sensor', unit: 'V', min: 0, max: 5, decimals: 2),
|
||
// Derived: injector duration × RPM (USB tool: 16.67 ms @ 3210 rpm = 44 %).
|
||
SensorDef(
|
||
id: 'duty', displayName: 'Injector duty', unit: '%', min: 0, max: 100, decimals: 0),
|
||
// AFR/AIN scales not yet bench-verified: shown as raw values.
|
||
SensorDef(
|
||
id: 'gear', displayName: 'Gear', unit: '', min: 0, max: 6, decimals: 0),
|
||
SensorDef(
|
||
id: 'eth', displayName: 'Ethanol', unit: '%', min: 0, max: 100, decimals: 0),
|
||
SensorDef(
|
||
id: 'pa', displayName: 'Barometric pressure', unit: 'kPa', min: 0, max: 120),
|
||
SensorDef(
|
||
id: 'afr', displayName: 'AFR', unit: 'raw', min: 0, max: 255, decimals: 0),
|
||
SensorDef(
|
||
id: 'strim', displayName: 'Short trim', unit: '%', min: -30, max: 30),
|
||
SensorDef(
|
||
id: 'ltrim', displayName: 'Long trim', unit: '%', min: -30, max: 30),
|
||
SensorDef(
|
||
id: 'ain0', displayName: 'Analog input 0', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
SensorDef(
|
||
id: 'ain1', displayName: 'Analog input 1', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
SensorDef(
|
||
id: 'ain2', displayName: 'Analog input 2', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
SensorDef(
|
||
id: 'ain3', displayName: 'Analog input 3', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
SensorDef(
|
||
id: 'ain4', displayName: 'Analog input 4', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
SensorDef(
|
||
id: 'ain5', displayName: 'Analog input 5', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
SensorDef(
|
||
id: 'ain6', displayName: 'Analog input 6', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
SensorDef(
|
||
id: 'ain7', displayName: 'Analog input 7', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
||
];
|
||
|
||
SensorDef sensorDef(String id) =>
|
||
sensorDefs.firstWhere((d) => d.id == id, orElse: () => sensorDefs.first);
|
||
|
||
/// Base-unit value of sensor [id] in [s].
|
||
double sensorValue(SensorState s, String id) {
|
||
switch (id) {
|
||
case 'rpm': return s.rpm;
|
||
case 'vss': return s.vss;
|
||
case 'map': return s.map;
|
||
case 'tps': return s.tps;
|
||
case 'inj': return s.inj;
|
||
case 'ign': return s.ign;
|
||
case 'ect': return s.ect;
|
||
case 'iat': return s.iat;
|
||
case 'bat': return s.bat;
|
||
case 'o2': return s.o2;
|
||
// One injection per 2 revolutions: ms × rpm / 120000 × 100.
|
||
case 'duty': return s.inj * s.rpm / 1200;
|
||
case 'gear': return s.gear.toDouble();
|
||
case 'eth': return s.eth;
|
||
case 'pa': return s.pa;
|
||
case 'afr': return s.afr;
|
||
case 'strim': return s.strim;
|
||
case 'ltrim': return s.ltrim;
|
||
case 'ain0': return s.ain0;
|
||
case 'ain1': return s.ain1;
|
||
case 'ain2': return s.ain2;
|
||
case 'ain3': return s.ain3;
|
||
case 'ain4': return s.ain4;
|
||
case 'ain5': return s.ain5;
|
||
case 'ain6': return s.ain6;
|
||
case 'ain7': return s.ain7;
|
||
default: return 0;
|
||
}
|
||
}
|
||
|
||
/// Master list of boolean flags (selectable on the dashboard). The first
|
||
/// eight follow the requirement document's examples; the rest are the other
|
||
/// status bits the ECU frame carries.
|
||
final List<FlagDef> flagDefs = [
|
||
FlagDef(id: 'mil', displayName: 'MIL', read: (s) => s.mil),
|
||
FlagDef(id: 'fanout', displayName: 'FAN Out', read: (s) => s.fanOut),
|
||
FlagDef(id: 'vtec', displayName: 'VTEC Out', read: (s) => s.vtec),
|
||
FlagDef(id: 'knock', displayName: 'Knock', read: (s) => s.knock),
|
||
FlagDef(id: 'o2error', displayName: 'WBO Error', read: (s) => s.o2Error),
|
||
FlagDef(id: 'iaterror', displayName: 'IAT Error', read: (s) => s.iatError),
|
||
FlagDef(id: 'ecterror', displayName: 'CLT Error', read: (s) => s.ectError),
|
||
FlagDef(id: 'fuelpump', displayName: 'Fuel Pump', read: (s) => s.fuelPump),
|
||
FlagDef(id: 'revlimit', displayName: 'Rev Limit', read: (s) => s.revLimit),
|
||
FlagDef(id: 'igncut', displayName: 'Ign Cut', read: (s) => s.ignCut),
|
||
FlagDef(id: 'boostcut', displayName: 'Boost Cut', read: (s) => s.boostCut),
|
||
FlagDef(id: 'launch', displayName: 'Launch Cut', read: (s) => s.launch),
|
||
FlagDef(
|
||
id: 'launchretard',
|
||
displayName: 'Launch Retard',
|
||
read: (s) => s.launchRetard),
|
||
FlagDef(id: 'shiftcut', displayName: 'Shift Cut', read: (s) => s.shiftCut),
|
||
FlagDef(
|
||
id: 'closedloop', displayName: 'Closed Loop', read: (s) => s.closedLoop),
|
||
FlagDef(id: 'vtp', displayName: 'VTEC Press', read: (s) => s.vtp),
|
||
FlagDef(id: 'acswitch', displayName: 'AC Switch', read: (s) => s.acSwitch),
|
||
FlagDef(id: 'brake', displayName: 'Brake', read: (s) => s.brakeSwitch),
|
||
];
|
||
|
||
FlagDef flagDef(String id) =>
|
||
flagDefs.firstWhere((f) => f.id == id, orElse: () => flagDefs.first);
|