From 2c8e2c711191610b08ea65a2deab13772bd2cb72 Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Sun, 25 Jan 2026 19:23:03 +0900 Subject: [PATCH] Pi vitals module --- pi/ui/lib/screens/dashboard_screen.dart | 21 ++++++---- pi/ui/lib/services/pi_io.dart | 55 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 pi/ui/lib/services/pi_io.dart diff --git a/pi/ui/lib/screens/dashboard_screen.dart b/pi/ui/lib/screens/dashboard_screen.dart index 79a69e3..7ad6ffd 100644 --- a/pi/ui/lib/screens/dashboard_screen.dart +++ b/pi/ui/lib/screens/dashboard_screen.dart @@ -2,9 +2,10 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; +import '../services/pi_io.dart'; import '../widgets/stat_box.dart'; -/// Main dashboard - placeholder with random updating values +/// Main dashboard - displays Pi vitals and placeholder stats class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @@ -16,7 +17,7 @@ class _DashboardScreenState extends State { final _random = Random(); Timer? _timer; - int _speed = 0; + double? _piTemp; int _rpm = 0; double _voltage = 12.6; int _temp = 25; @@ -24,10 +25,14 @@ class _DashboardScreenState extends State { @override void initState() { super.initState(); - // Update random values every 500ms - simulates live data + + // Update values periodically _timer = Timer.periodic(const Duration(milliseconds: 500), (_) { setState(() { - _speed = _random.nextInt(120); + // Pi temp - sync read from cache, async refresh happens in background + _piTemp = PiIO.instance.getTemperature(); + + // Placeholder random data - will be replaced with real sensors _rpm = 1000 + _random.nextInt(8000); _voltage = 11.5 + _random.nextDouble() * 2; _temp = 20 + _random.nextInt(60); @@ -72,14 +77,14 @@ class _DashboardScreenState extends State { const SizedBox(height: 48), - // Main speed display + // Main Pi temperature display Expanded( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - '$_speed', + _piTemp != null ? _piTemp!.toStringAsFixed(1) : '—', style: const TextStyle( fontSize: 180, fontWeight: FontWeight.w200, @@ -88,7 +93,7 @@ class _DashboardScreenState extends State { ), ), Text( - 'km/h', + 'Pi Temp', style: Theme.of(context).textTheme.headlineSmall?.copyWith( color: Colors.grey, ), @@ -103,7 +108,7 @@ class _DashboardScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ StatBox(label: 'RPM', value: _rpm.toString()), - StatBox(label: 'TEMP', value: '$_temp°C'), + StatBox(label: 'ENG', value: '$_temp°C'), StatBox(label: 'GEAR', value: '—'), ], ), diff --git a/pi/ui/lib/services/pi_io.dart b/pi/ui/lib/services/pi_io.dart new file mode 100644 index 0000000..ebd533c --- /dev/null +++ b/pi/ui/lib/services/pi_io.dart @@ -0,0 +1,55 @@ +import 'dart:io'; + +/// Abstraction for Raspberry Pi hardware I/O +/// +/// Uses fire-and-forget async reads with synchronous cache returns. +/// UI always gets immediate response, cache updates in background. +class PiIO { + PiIO._() { + // Kick off initial read + _refreshTemperature(); + } + static final instance = PiIO._(); + + // Thermal zone file path (returns millidegrees) + static const _thermalPath = '/sys/class/thermal/thermal_zone0/temp'; + + // Cache + double? _tempCache; + bool _tempReadInProgress = false; + + /// Get CPU temperature in Celsius (synchronous, returns cached value) + /// + /// Returns immediately with cached value (or null if no read has completed). + /// Triggers background refresh if not already in progress. + double? getTemperature() { + // Fire off background read if not already running + if (!_tempReadInProgress) { + _refreshTemperature(); + } + return _tempCache; + } + + /// Background read - updates cache when complete + Future _refreshTemperature() async { + if (_tempReadInProgress) return; + _tempReadInProgress = true; + + try { + final file = File(_thermalPath); + if (await file.exists()) { + final content = await file.readAsString(); + _tempCache = int.parse(content.trim()) / 1000.0; + } + } catch (e) { + // Not on Pi, or permission issue - cache stays as-is + } finally { + _tempReadInProgress = false; + } + } + + /// Force clear cache (next getTemperature will return null until read completes) + void clearCache() { + _tempCache = null; + } +}