diff --git a/lib/communication/sensors/mlx90614.dart b/lib/communication/sensors/mlx90614.dart new file mode 100644 index 000000000..327bd5532 --- /dev/null +++ b/lib/communication/sensors/mlx90614.dart @@ -0,0 +1,51 @@ +import 'dart:async'; +import '../peripherals/i2c.dart'; + +class MLX90614 { + final I2C i2c; + // The default I2C address for MLX90614 is 0x5A + static const int address = 0x5A; + + // Register addresses + static const int ambientTempReg = 0x06; + static const int objectTempReg = 0x07; + + MLX90614(this.i2c); + + /// Reads the Ambient (Room) Temperature + Future getAmbientTemperature() async { + return _readTemperature(ambientTempReg); + } + + /// Reads the Object (Target) Temperature + Future getObjectTemperature() async { + return _readTemperature(objectTempReg); + } + + /// Helper function to handle the math + Future _readTemperature(int reg) async { + // Read 2 bytes from the specific register + List data = await i2c.readBulk(address, reg, 2); + + if (data.length < 2) { + throw Exception("Failed to read temperature from MLX90614"); + } + + // MLX90614 sends LSB first, then MSB. + int lsb = data[0]; + int msb = data[1]; + + // Combine the bytes: (MSB << 8) | LSB + // We apply the mask 0x7FFF to ignore the error flag (Bit 15) + // ensuring we only process valid temperature data bits. + int rawValue = ((msb << 8) | lsb) & 0x7FFF; + + // Formula from datasheet: + // The sensor returns temperature in Kelvin * 50. + // Multiply by 0.02 to get Kelvin. + // Subtract 273.15 to convert Kelvin to Celsius. + double tempCelsius = (rawValue * 0.02) - 273.15; + + return tempCelsius; + } +} diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 89c646d0a..8e357cdbe 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -523,5 +523,8 @@ "light": "Light", "darkExperimental": "Dark (Experimental)", "system": "System", - "shareApp": "Share App" + "shareApp": "Share App", + "mlxObjectTemp": "Object Temperature", + "mlxAmbientTemp": "Ambient Temperature", + "mlx90614Config": "MLX90614 Configurations" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 1e7d78ab2..9a4ea08eb 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -3255,6 +3255,24 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Share App'** String get shareApp; + + /// No description provided for @mlxObjectTemp. + /// + /// In en, this message translates to: + /// **'Object Temperature'** + String get mlxObjectTemp; + + /// No description provided for @mlxAmbientTemp. + /// + /// In en, this message translates to: + /// **'Ambient Temperature'** + String get mlxAmbientTemp; + + /// No description provided for @mlx90614Config. + /// + /// In en, this message translates to: + /// **'MLX90614 Configurations'** + String get mlx90614Config; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 0aaf83864..e0e009aaf 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsDe extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 87be82ce9..0b8d16a42 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsEn extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index b8e9c8af0..9a440212a 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsEs extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index f9eb670d5..de6e7df84 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsFr extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_he.dart b/lib/l10n/app_localizations_he.dart index f92fd94fb..75dd7f4b4 100644 --- a/lib/l10n/app_localizations_he.dart +++ b/lib/l10n/app_localizations_he.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsHe extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index ce10ef9d7..03fff3a60 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsHi extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index ddf3a4b3b..bc0d68aa5 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsId extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index a78a7cf4c..56a9e1e47 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsJa extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_nb.dart b/lib/l10n/app_localizations_nb.dart index 9673b798e..cc7d0780c 100644 --- a/lib/l10n/app_localizations_nb.dart +++ b/lib/l10n/app_localizations_nb.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsNb extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 9cbbb629a..2ff871ede 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -1696,6 +1696,15 @@ class AppLocalizationsPt extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } /// The translations for Portuguese, as used in Brazil (`pt_BR`). diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index a18304cb8..00b791812 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsRu extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 4b68ab5b0..b87a27eb8 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsUk extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_vi.dart b/lib/l10n/app_localizations_vi.dart index 108d08d96..583a0811a 100644 --- a/lib/l10n/app_localizations_vi.dart +++ b/lib/l10n/app_localizations_vi.dart @@ -1696,4 +1696,13 @@ class AppLocalizationsVi extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index e88630fa2..c3e7fe478 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -1696,6 +1696,15 @@ class AppLocalizationsZh extends AppLocalizations { @override String get shareApp => 'Share App'; + + @override + String get mlxObjectTemp => 'Object Temperature'; + + @override + String get mlxAmbientTemp => 'Ambient Temperature'; + + @override + String get mlx90614Config => 'MLX90614 Configurations'; } /// The translations for Chinese, using the Han script (`zh_Hant`). diff --git a/lib/main.dart b/lib/main.dart index 25d75094e..32e80e3d2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:pslab/l10n/app_localizations.dart'; import 'package:pslab/providers/board_state_provider.dart'; import 'package:pslab/providers/locator.dart'; import 'package:pslab/providers/settings_config_provider.dart'; +import 'package:pslab/providers/mlx90614_provider.dart'; // <--- 1. IMPORT ADDED import 'package:pslab/view/accelerometer_screen.dart'; import 'package:pslab/view/barometer_screen.dart'; import 'package:pslab/view/connect_device_screen.dart'; @@ -42,6 +43,10 @@ void main() { ChangeNotifierProvider( create: (context) => getIt(), ), + // <--- 2. PROVIDER ADDED + ChangeNotifierProvider( + create: (context) => getIt(), + ), ], child: const MyApp(), ), diff --git a/lib/providers/locator.dart b/lib/providers/locator.dart index e5b635738..781588679 100644 --- a/lib/providers/locator.dart +++ b/lib/providers/locator.dart @@ -10,6 +10,7 @@ import 'package:pslab/communication/science_lab.dart'; import 'package:pslab/communication/socket_client.dart'; import 'package:pslab/others/science_lab_common.dart'; import 'package:pslab/providers/board_state_provider.dart'; +import 'package:pslab/providers/mlx90614_provider.dart'; // <--- 1. IMPORT ADDED final GetIt getIt = GetIt.instance; @@ -29,6 +30,9 @@ void setupLocator() { getIt.registerLazySingleton( () => getIt.get().getScienceLab()); getIt.registerLazySingleton(() => BoardStateProvider()); + + // <--- 2. REGISTRATION ADDED + getIt.registerLazySingleton(() => MLX90614Provider()); } void registerAppLocalizations(AppLocalizations appLocalizations) { diff --git a/lib/providers/mlx90614_provider.dart b/lib/providers/mlx90614_provider.dart new file mode 100644 index 000000000..29d3e32b2 --- /dev/null +++ b/lib/providers/mlx90614_provider.dart @@ -0,0 +1,49 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import '../communication/sensors/mlx90614.dart'; +import '../communication/peripherals/i2c.dart'; + +class MLX90614Provider with ChangeNotifier { + MLX90614? _sensor; + bool isWorking = false; + + // This sensor provides two values + double ambientTemp = 0.0; // Room temperature + double objectTemp = 0.0; // Target temperature + + // Initialize the sensor with the I2C connection + Future init(I2C i2c) async { + _sensor ??= MLX90614(i2c); + } + + // Start the loop to read data + Future startDataLog() async { + if (_sensor == null) return; + + // Check if loop is already running to prevent duplicates + if (isWorking) return; + + isWorking = true; + notifyListeners(); + + while (isWorking) { + try { + // Read both values safely + ambientTemp = await _sensor!.getAmbientTemperature(); + objectTemp = await _sensor!.getObjectTemperature(); + notifyListeners(); + } catch (e) { + debugPrint("Error reading MLX90614: $e"); + } + + // Wait 1 second before next read + await Future.delayed(const Duration(milliseconds: 1000)); + } + } + + // Stop the loop + void stopDataLog() { + isWorking = false; + notifyListeners(); + } +} diff --git a/lib/view/mlx90614_screen.dart b/lib/view/mlx90614_screen.dart new file mode 100644 index 000000000..8ea091023 --- /dev/null +++ b/lib/view/mlx90614_screen.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:pslab/communication/science_lab.dart'; +import 'package:pslab/communication/peripherals/i2c.dart'; +import 'package:pslab/providers/locator.dart'; +import 'package:pslab/l10n/app_localizations.dart'; // Ensure this import is here +import '../providers/mlx90614_provider.dart'; + +class MLX90614Screen extends StatefulWidget { + const MLX90614Screen({super.key}); + + @override + State createState() => _MLX90614ScreenState(); +} + +class _MLX90614ScreenState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + + final provider = Provider.of(context, listen: false); + final scienceLab = getIt(); + // FIX: Use localized string for errors + final appLocalizations = AppLocalizations.of(context)!; + + if (scienceLab.isConnected()) { + I2C i2c = I2C(scienceLab.mPacketHandler); + provider.init(i2c); + provider.startDataLog(); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(appLocalizations.notConnected)), + ); + } + }); + } + + @override + void dispose() { + if (mounted) { + Provider.of(context, listen: false).stopDataLog(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // FIX: Get localizations + final appLocalizations = AppLocalizations.of(context)!; + + return Scaffold( + appBar: AppBar( + title: const Text( + 'MLX90614 Sensor'), // This one is usually okay hardcoded for the sensor name + ), + body: Consumer( + builder: (context, provider, child) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + _buildSensorCard( + // FIX: Use localized strings + title: appLocalizations.mlxObjectTemp, + value: provider.objectTemp.toStringAsFixed(2), + unit: "°C", + icon: Icons.thermostat_auto, + color: Colors.deepOrangeAccent, + ), + const SizedBox(height: 20), + _buildSensorCard( + // FIX: Use localized strings + title: appLocalizations.mlxAmbientTemp, + value: provider.ambientTemp.toStringAsFixed(2), + unit: "°C", + icon: Icons.home_mini, + color: Colors.blueGrey, + ), + ], + ), + ); + }, + ), + ); + } + + Widget _buildSensorCard({ + required String title, + required String value, + required String unit, + required IconData icon, + required Color color, + }) { + return Card( + elevation: 4, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Row( + children: [ + Icon(icon, size: 40, color: color), + const SizedBox(width: 20), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, + style: const TextStyle(fontSize: 16, color: Colors.grey)), + Row( + children: [ + Text(value, + style: const TextStyle( + fontSize: 32, fontWeight: FontWeight.bold)), + const SizedBox(width: 5), + Text(unit, style: const TextStyle(fontSize: 20)), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/sensors_screen.dart b/lib/view/sensors_screen.dart index d43df3832..927a98dfd 100644 --- a/lib/view/sensors_screen.dart +++ b/lib/view/sensors_screen.dart @@ -3,6 +3,7 @@ import 'package:provider/provider.dart'; import 'package:pslab/view/bmp180_screen.dart'; import 'package:pslab/view/ads1115_screen.dart'; import 'package:pslab/view/vl53l0x_screen.dart'; +import 'package:pslab/view/mlx90614_screen.dart'; import 'package:pslab/view/widgets/common_scaffold_widget.dart'; import '../../providers/board_state_provider.dart'; import '../l10n/app_localizations.dart'; @@ -229,9 +230,15 @@ class _SensorsScreenState extends State { break; case 'APDS9960': targetScreen = const APDS9960Screen(); + break; case 'VL53L0X': targetScreen = const VL53L0XScreen(); break; + + case 'MLX90614': + targetScreen = const MLX90614Screen(); + break; + default: ScaffoldMessenger.of(context).showSnackBar( SnackBar(