Accept live S300 frames with bad checksum

This commit is contained in:
HVBT Dev 2026-07-27 00:23:33 +05:30
parent 1424cafbb4
commit 0886bef200
5 changed files with 25 additions and 10 deletions

View File

@ -85,7 +85,11 @@ class BtPoller {
final Uint8List frame = final Uint8List frame =
Uint8List.fromList(_rxBuf.sublist(startIdx, startIdx + 128)); Uint8List.fromList(_rxBuf.sublist(startIdx, startIdx + 128));
if (validateFrame(frame)) { final checksumOk = validateFrame(frame);
final canUseFrame = checksumOk || ecuType == EcuType.s300;
if (canUseFrame) {
if (!checksumOk) droppedFrames++;
validFrames++; validFrames++;
_frameController.add(frame); _frameController.add(frame);
_rxBuf.removeRange(0, startIdx + 128); _rxBuf.removeRange(0, startIdx + 128);

View File

@ -4,12 +4,13 @@ import 'temp_table.dart';
import 'neg8.dart'; import 'neg8.dart';
/// Parses a 128-byte S300 ECU response frame into a [SensorState]. /// Parses a 128-byte S300 ECU response frame into a [SensorState].
/// Throws [ArgumentError] if frame length is wrong or NEG8 checksum fails. /// Throws [ArgumentError] if frame length is wrong, or if [validateChecksum]
SensorState parseS300(Uint8List frame) { /// is true and the NEG8 checksum fails.
SensorState parseS300(Uint8List frame, {bool validateChecksum = true}) {
if (frame.length != 128) { if (frame.length != 128) {
throw ArgumentError('S300 frame must be 128 bytes, got ${frame.length}'); throw ArgumentError('S300 frame must be 128 bytes, got ${frame.length}');
} }
if (!validateFrame(frame)) { if (validateChecksum && !validateFrame(frame)) {
throw ArgumentError('S300 frame NEG8 checksum failed'); throw ArgumentError('S300 frame NEG8 checksum failed');
} }

View File

@ -14,7 +14,9 @@ final sensorStateProvider = StreamProvider<SensorState>((ref) {
final btNotifier = ref.watch(btProvider.notifier); final btNotifier = ref.watch(btProvider.notifier);
return btNotifier.frameStream.map((frame) { return btNotifier.frameStream.map((frame) {
if (ecuType == EcuType.s300) return parseS300(frame); if (ecuType == EcuType.s300) {
return parseS300(frame, validateChecksum: false);
}
return parseKPro(frame); return parseKPro(frame);
}); });
}); });
@ -22,8 +24,6 @@ final sensorStateProvider = StreamProvider<SensorState>((ref) {
/// Last successfully parsed sensor state (never null after first frame). /// Last successfully parsed sensor state (never null after first frame).
/// Falls back to SensorState.zero() before any data arrives. /// Falls back to SensorState.zero() before any data arrives.
final latestSensorProvider = Provider<SensorState>((ref) { final latestSensorProvider = Provider<SensorState>((ref) {
return ref return ref.watch(sensorStateProvider).whenData((s) => s).value ??
.watch(sensorStateProvider) SensorState.zero();
.whenData((s) => s)
.value ?? SensorState.zero();
}); });

View File

@ -51,7 +51,10 @@ class RawFrameDebugScreen extends ConsumerWidget {
_RowData('Dropped', bt.droppedFrames.toString()), _RowData('Dropped', bt.droppedFrames.toString()),
_RowData('Last frame bytes', frame?.length.toString() ?? 'none'), _RowData('Last frame bytes', frame?.length.toString() ?? 'none'),
if (frame != null) if (frame != null)
_RowData('Checksum', validateFrame(frame) ? 'valid' : 'bad'), _RowData(
'Checksum',
validateFrame(frame) ? 'valid' : 'bad, accepted for S300',
),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),

View File

@ -152,5 +152,12 @@ void main() {
frame[127] = 0x00; // corrupt checksum frame[127] = 0x00; // corrupt checksum
expect(() => parseS300(frame), throwsArgumentError); expect(() => parseS300(frame), throwsArgumentError);
}); });
test('can parse bad checksum frame when validation is disabled', () {
final frame = _buildS300Frame(rpm: 1000);
frame[127] = 0x00; // corrupt checksum
final state = parseS300(frame, validateChecksum: false);
expect(state.rpm, equals(1000.0));
});
}); });
} }