Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/images/mlx90614.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions lib/communication/sensors/mlx90614.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'package:pslab/communication/peripherals/i2c.dart';
import 'package:pslab/communication/science_lab.dart';
import 'package:pslab/constants.dart';
import 'package:pslab/others/logger_service.dart';

class MLX90614 {
static const String tag = "MLX90614";

static const int address = 0x5A;
static const int objTempRegister = 0x07;
static const int ambTempRegister = 0x06;

final I2C i2c;
double objectTemperature = 0.0;
double ambientTemperature = 0.0;

MLX90614._(this.i2c);

static Future<MLX90614> create(I2C i2c, ScienceLab scienceLab) async {
final mlx = MLX90614._(i2c);
await mlx._init(scienceLab);
return mlx;
}

Future<void> _init(ScienceLab scienceLab) async {
if (!scienceLab.isConnected()) {
throw Exception(appLocalizations.notConnected);
}
try {
await i2c.config(100000);
} catch (e) {
logger.e("Error initializing MLX90614: $e");
rethrow;
}
}

Future<double?> _readTemp(int register) async {
try {
List<int> vals = await i2c.readBulk(address, register, 3);

if (vals.length >= 2) {
int lsb = vals[0];
int msb = vals[1];

return ((((msb & 0x007F) << 8) + lsb) * 0.02) - 0.01 - 273.15;
}
return null;
} catch (e) {
logger.e("Error reading from MLX90614 register $register: $e");
rethrow;
}
}

Future<Map<String, double>> getRawData() async {
try {
double? objTemp = await _readTemp(objTempRegister);
double? ambTemp = await _readTemp(ambTempRegister);

objectTemperature = objTemp ?? objectTemperature;
ambientTemperature = ambTemp ?? ambientTemperature;

return {
'objectTemperature': objectTemperature,
'ambientTemperature': ambientTemperature,
};
} catch (e) {
logger.e("Error getting raw data: $e");
rethrow;
}
}
}
5 changes: 4 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -763,5 +763,8 @@
"sensorDescVL53L0X": "Time-of-Flight (ToF) Laser Distance",
"mightNotConnected" : "might not be connected.",
"hmc5883l" : "HMC5883L",
"gauss" : "Gauss"
"gauss" : "Gauss",
"mlx90614" : "MLX90614",
"objectTemperature" : "Object Temp",
"ambientTemperature" : "Ambient Temp"
}
190 changes: 190 additions & 0 deletions lib/providers/mlx90614_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:pslab/communication/peripherals/i2c.dart';
import 'package:pslab/communication/science_lab.dart';
import '../communication/sensors/mlx90614.dart';
import '../l10n/app_localizations.dart';
import '../models/chart_data_points.dart';
import 'package:pslab/others/logger_service.dart';
import 'locator.dart';

class MLX90614Provider extends ChangeNotifier {
AppLocalizations get appLocalizations => getIt.get<AppLocalizations>();

MLX90614? _mlx90614;
Timer? _dataTimer;

double _objectTemperature = 0.0;
double _ambientTemperature = 0.0;

final List<ChartDataPoint> _objectTemperatureData = [];
final List<ChartDataPoint> _ambientTemperatureData = [];

bool _isRunning = false;
bool _isLooping = false;
int _timegapMs = 1000;
int _numberOfReadings = 100;
int _collectedReadings = 0;

double _currentTime = 0.0;
static const int maxDataPoints = 1000;

double get objectTemperature => _objectTemperature;
double get ambientTemperature => _ambientTemperature;

List<ChartDataPoint> get objectTemperatureData =>
List.unmodifiable(_objectTemperatureData);
List<ChartDataPoint> get ambientTemperatureData =>
List.unmodifiable(_ambientTemperatureData);

bool get isRunning => _isRunning;
bool get isLooping => _isLooping;
int get timegapMs => _timegapMs;
int get numberOfReadings => _numberOfReadings;
int get collectedReadings => _collectedReadings;

MLX90614Provider();

Future<void> initializeSensors({
required Function(String) onError,
required I2C? i2c,
required ScienceLab? scienceLab,
}) async {
try {
if (i2c == null || scienceLab == null) {
onError(appLocalizations.pslabNotConnected);
return;
}

if (!scienceLab.isConnected()) {
onError(appLocalizations.pslabNotConnected);
return;
}

_mlx90614 = await MLX90614.create(i2c, scienceLab);
notifyListeners();
} catch (e) {
logger.e('Error initializing MLX90614: $e');
}
}

void toggleDataCollection() {
if (_isRunning) {
_stopDataCollection();
} else {
_startDataCollection();
}
}

void _startDataCollection() {
if (_mlx90614 == null) return;

_isRunning = true;
_collectedReadings = 0;

_dataTimer =
Timer.periodic(Duration(milliseconds: _timegapMs), (timer) async {
try {
await _fetchSensorData();
_collectedReadings++;

if (!_isLooping && _collectedReadings >= _numberOfReadings) {
_stopDataCollection();
}

if (_isLooping && _objectTemperatureData.length >= maxDataPoints) {
_removeOldestDataPoints();
}
} catch (e) {
logger.e('Error fetching sensor data: $e');
}
});
notifyListeners();
}

void _stopDataCollection() {
_isRunning = false;
_dataTimer?.cancel();
_dataTimer = null;
notifyListeners();
}

Future<void> _fetchSensorData() async {
if (_mlx90614 == null) return;

try {
final rawData = await _mlx90614!.getRawData();

_objectTemperature = rawData['objectTemperature'] ?? 0.0;
_ambientTemperature = rawData['ambientTemperature'] ?? 0.0;

_currentTime += _timegapMs / 1000.0;

_addDataPoint(_objectTemperatureData, _objectTemperature);
_addDataPoint(_ambientTemperatureData, _ambientTemperature);

notifyListeners();
} catch (e) {
logger.e('Error in _fetchSensorData: $e');
rethrow;
}
}

void _addDataPoint(List<ChartDataPoint> dataList, double value) {
dataList.add(ChartDataPoint(_currentTime, value));
if (dataList.length > 50) {
dataList.removeAt(0);
}
}

void _removeOldestDataPoints() {
const keepPoints = 800;

if (_objectTemperatureData.length > keepPoints) {
final removeCount = _objectTemperatureData.length - keepPoints;
_objectTemperatureData.removeRange(0, removeCount);
_ambientTemperatureData.removeRange(0, removeCount);
}
}

void toggleLooping() {
_isLooping = !_isLooping;
notifyListeners();
}

void setTimegap(int timegapMs) {
_timegapMs = timegapMs;

if (_isRunning) {
_stopDataCollection();
_startDataCollection();
}

notifyListeners();
}

void setNumberOfReadings(int numberOfReadings) {
_numberOfReadings = numberOfReadings;
notifyListeners();
}

void clearData() {
_objectTemperatureData.clear();
_ambientTemperatureData.clear();
_objectTemperature = 0.0;
_ambientTemperature = 0.0;
_currentTime = 0.0;
_collectedReadings = 0;
notifyListeners();
}

bool get isCollectionComplete {
return !_isLooping && _collectedReadings >= _numberOfReadings;
}

@override
void dispose() {
_stopDataCollection();
super.dispose();
}
}
Loading
Loading