O2 in volts, injector duty, Fuel Pump default flag, raw frame log
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>
This commit is contained in:
parent
e4bcfb57a6
commit
6b8d069ef4
@ -32,9 +32,12 @@ const List<SensorDef> sensorDefs = [
|
||||
max: 150),
|
||||
SensorDef(
|
||||
id: 'bat', displayName: 'Battery voltage', unit: 'V', min: 0, max: 20),
|
||||
// O2/AFR/AIN scales not yet bench-verified: shown as raw values.
|
||||
SensorDef(
|
||||
id: 'o2', displayName: 'O2 sensor', unit: 'raw', min: 0, max: 255, decimals: 0),
|
||||
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(
|
||||
@ -81,6 +84,8 @@ double sensorValue(SensorState s, String id) {
|
||||
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;
|
||||
|
||||
@ -39,7 +39,8 @@ SensorState parseKPro(Uint8List frame) {
|
||||
final double ign = frame[13].toDouble();
|
||||
|
||||
// O2: byte 14 raw
|
||||
final double o2 = frame[14].toDouble();
|
||||
// O2: volts, raw × 5/256 as on the S300. UNVERIFIED for KPro.
|
||||
final double o2 = frame[14] * 5.0 / 256.0;
|
||||
|
||||
// KRtrd: byte 21
|
||||
final int krtrd = frame[21];
|
||||
|
||||
@ -97,8 +97,8 @@ SensorState parseS300(Uint8List frame, {bool validateChecksum = true}) {
|
||||
// IGN: formula from the PDF. UNVERIFIED (one live check disagreed).
|
||||
final double ign = (frame[12] + 120) / 2.0;
|
||||
|
||||
// O2: raw byte. UNVERIFIED scale — shown as raw.
|
||||
final double o2 = frame[16].toDouble();
|
||||
// O2: volts, raw × 5/256 (USB tool: raw 195 → 3.81 V).
|
||||
final double o2 = frame[16] * 5.0 / 256.0;
|
||||
|
||||
final int sw1 = frame[17];
|
||||
final int sw2 = frame[18];
|
||||
|
||||
@ -245,12 +245,31 @@ class BtNotifier extends StateNotifier<BtState> {
|
||||
} else {
|
||||
_liveFrames++;
|
||||
_lastLiveFrameAt = now;
|
||||
_maybeLogFrame(frame, now);
|
||||
_defaultsSince = null;
|
||||
_frameController.add(frame);
|
||||
}
|
||||
_publishStats(frame);
|
||||
}
|
||||
|
||||
DateTime? _lastFrameLogAt;
|
||||
|
||||
/// Raw hex of one live frame every 5 s, so bench readings from the USB
|
||||
/// tool can be matched to exact bytes (Debug → Copy app log, or logcat).
|
||||
void _maybeLogFrame(Uint8List frame, DateTime now) {
|
||||
final last = _lastFrameLogAt;
|
||||
if (last != null && now.difference(last) < const Duration(seconds: 5)) {
|
||||
return;
|
||||
}
|
||||
_lastFrameLogAt = now;
|
||||
final hex = StringBuffer();
|
||||
for (var i = 0; i < 64; i++) {
|
||||
if (i > 0) hex.write(i % 16 == 0 ? ' | ' : ' ');
|
||||
hex.write(frame[i].toRadixString(16).padLeft(2, '0'));
|
||||
}
|
||||
AppLog.i('FRAME[0..63] $hex');
|
||||
}
|
||||
|
||||
void _publishStats([Uint8List? frame]) {
|
||||
final p = _poller;
|
||||
state = state.copyWith(
|
||||
|
||||
@ -23,30 +23,9 @@ class SensorHistory {
|
||||
if (list.length > _maxPoints) list.removeAt(0);
|
||||
}
|
||||
|
||||
push('rpm', state.rpm);
|
||||
push('vss', state.vss);
|
||||
push('map', state.map);
|
||||
push('tps', state.tps);
|
||||
push('inj', state.inj);
|
||||
push('ign', state.ign);
|
||||
push('ect', state.ect);
|
||||
push('iat', state.iat);
|
||||
push('bat', state.bat);
|
||||
push('o2', state.o2);
|
||||
push('gear', state.gear.toDouble());
|
||||
push('eth', state.eth);
|
||||
push('pa', state.pa);
|
||||
push('afr', state.afr);
|
||||
push('strim', state.strim);
|
||||
push('ltrim', state.ltrim);
|
||||
push('ain0', state.ain0);
|
||||
push('ain1', state.ain1);
|
||||
push('ain2', state.ain2);
|
||||
push('ain3', state.ain3);
|
||||
push('ain4', state.ain4);
|
||||
push('ain5', state.ain5);
|
||||
push('ain6', state.ain6);
|
||||
push('ain7', state.ain7);
|
||||
for (final d in sensorDefs) {
|
||||
push(d.id, sensorValue(state, d.id));
|
||||
}
|
||||
}
|
||||
|
||||
List<FlSpot> get(String id) => List.unmodifiable(_data[id] ?? []);
|
||||
|
||||
@ -29,7 +29,9 @@ class DashboardConfig {
|
||||
'vss', 'ect', 'iat', 'map', 'o2', // left
|
||||
'tps', 'bat', 'ign', 'inj', 'eth', // right
|
||||
],
|
||||
flags: ['mil', 'fanout', 'vtec', 'knock'],
|
||||
// Requirement picture: MIL, FuelCut, FAN Out, VTEC Out. On the S300
|
||||
// the fuel flag is the fuel pump relay (client).
|
||||
flags: ['mil', 'fuelpump', 'fanout', 'vtec'],
|
||||
);
|
||||
|
||||
DashboardConfig copyWith({
|
||||
|
||||
@ -58,6 +58,17 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('values checked against the USB tool (bench, 3210 rpm)', () {
|
||||
test('O2 volts = raw × 5/256 (raw 195 → 3.81 V)', () {
|
||||
expect(parseS300(_s300({16: 195})).o2, closeTo(3.81, 0.005));
|
||||
});
|
||||
|
||||
test('injector duty = INJ ms × RPM / 1200 (16.67 ms @ 3210 → 44 %)', () {
|
||||
final s = parseS300(_s300({})).copyWith(inj: 16.67, rpm: 3210);
|
||||
expect(sensorValue(s, 'duty'), closeTo(44.6, 0.1));
|
||||
});
|
||||
});
|
||||
|
||||
group('S300 flags', () {
|
||||
test('SW3 cut/launch bits (byte 19)', () {
|
||||
final s = parseS300(_s300({19: 0x20 | 0x08}));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user