flutter: IMU attitude indicator and UART health check

- WhiskeyMark widget shows roll/pitch as horizon line
- ArduinoData model includes IMU euler angles
- startup waits for Arduino via /health endpoint
- config_service exposes backendUrl

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Mikkeli Matlock
2026-02-01 17:01:45 +09:00
parent c1a2994d00
commit f7f0af92dd
6 changed files with 304 additions and 14 deletions

View File

@@ -2,14 +2,16 @@ import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
/// Data from Arduino (voltage, rpm, engine temp, gear)
/// Data from Arduino (voltage, rpm, engine temp, gear, IMU)
class ArduinoData {
final double? voltage;
final int? rpm;
final int? engTemp;
final int? gear; // 0 = neutral, 1-6 = gear
final double? roll; // Euler angle in degrees (negative = left, positive = right)
final double? pitch; // Euler angle in degrees (negative = nose down)
ArduinoData({this.voltage, this.rpm, this.engTemp, this.gear});
ArduinoData({this.voltage, this.rpm, this.engTemp, this.gear, this.roll, this.pitch});
factory ArduinoData.fromJson(Map<String, dynamic> json) {
return ArduinoData(
@@ -17,6 +19,8 @@ class ArduinoData {
rpm: (json['rpm'] as num?)?.toInt(),
engTemp: (json['eng_temp'] as num?)?.toInt(),
gear: (json['gear'] as num?)?.toInt(),
roll: (json['roll'] as num?)?.toDouble(), // IMU mounted with axes swapped
pitch: (json['pitch'] as num?)?.toDouble(),
);
}
}

View File

@@ -86,4 +86,11 @@ class ConfigService {
if (value is String && value.isNotEmpty) return value;
return _defaultNavigator;
}
/// Backend URL for API calls
String get backendUrl {
final value = _config?['backend_url'];
if (value is String && value.isNotEmpty) return value;
return 'http://127.0.0.1:5000';
}
}

View File

@@ -184,7 +184,10 @@ class WebSocketService {
final arduino = ArduinoData.fromJson(data);
_latestArduino = arduino;
_arduinoController.add(arduino);
_log('ard: ${arduino.rpm ?? "-"}rpm ${arduino.voltage ?? "-"}V g${arduino.gear ?? "-"}');
final rollStr = arduino.roll != null ? 'r${arduino.roll!.round()}' : '';
final pitchStr = arduino.pitch != null ? 'p${arduino.pitch!.round()}' : '';
final imuStr = (rollStr.isNotEmpty || pitchStr.isNotEmpty) ? ' $rollStr$pitchStr' : '';
_log('ard: ${arduino.rpm ?? "-"}rpm ${arduino.voltage ?? "-"}V g${arduino.gear ?? "-"}$imuStr');
}
});