Skip to content

Commit 7d07ca4

Browse files
authored
feat: Implement MLX90614 sensor (#3365)
1 parent 2536408 commit 7d07ca4

6 files changed

Lines changed: 567 additions & 4 deletions

File tree

assets/images/mlx90614.jpg

101 KB
Loading
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import 'package:pslab/communication/peripherals/i2c.dart';
2+
import 'package:pslab/communication/science_lab.dart';
3+
import 'package:pslab/constants.dart';
4+
import 'package:pslab/others/logger_service.dart';
5+
6+
class MLX90614 {
7+
static const String tag = "MLX90614";
8+
9+
static const int address = 0x5A;
10+
static const int objTempRegister = 0x07;
11+
static const int ambTempRegister = 0x06;
12+
13+
final I2C i2c;
14+
double objectTemperature = 0.0;
15+
double ambientTemperature = 0.0;
16+
17+
MLX90614._(this.i2c);
18+
19+
static Future<MLX90614> create(I2C i2c, ScienceLab scienceLab) async {
20+
final mlx = MLX90614._(i2c);
21+
await mlx._init(scienceLab);
22+
return mlx;
23+
}
24+
25+
Future<void> _init(ScienceLab scienceLab) async {
26+
if (!scienceLab.isConnected()) {
27+
throw Exception(appLocalizations.notConnected);
28+
}
29+
try {
30+
await i2c.config(100000);
31+
} catch (e) {
32+
logger.e("Error initializing MLX90614: $e");
33+
rethrow;
34+
}
35+
}
36+
37+
Future<double?> _readTemp(int register) async {
38+
try {
39+
List<int> vals = await i2c.readBulk(address, register, 3);
40+
41+
if (vals.length >= 2) {
42+
int lsb = vals[0];
43+
int msb = vals[1];
44+
45+
return ((((msb & 0x007F) << 8) + lsb) * 0.02) - 0.01 - 273.15;
46+
}
47+
return null;
48+
} catch (e) {
49+
logger.e("Error reading from MLX90614 register $register: $e");
50+
rethrow;
51+
}
52+
}
53+
54+
Future<Map<String, double>> getRawData() async {
55+
try {
56+
double? objTemp = await _readTemp(objTempRegister);
57+
double? ambTemp = await _readTemp(ambTempRegister);
58+
59+
objectTemperature = objTemp ?? objectTemperature;
60+
ambientTemperature = ambTemp ?? ambientTemperature;
61+
62+
return {
63+
'objectTemperature': objectTemperature,
64+
'ambientTemperature': ambientTemperature,
65+
};
66+
} catch (e) {
67+
logger.e("Error getting raw data: $e");
68+
rethrow;
69+
}
70+
}
71+
}

lib/l10n/app_en.arb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -764,13 +764,13 @@
764764
"mightNotConnected" : "might not be connected.",
765765
"hmc5883l" : "HMC5883L",
766766
"gauss" : "Gauss",
767+
"mlx90614" : "MLX90614",
768+
"objectTemperature" : "Object Temp",
769+
"ambientTemperature" : "Ambient Temp",
767770
"i2cError" : "Check I2C wiring.",
768771
"ccs811" : "CCS811",
769772
"eco2" : "eCO2",
770773
"tvoc" : "TVOC",
771774
"ppb" : "ppb",
772-
"concentrationPpm" : "Concentration (PPM)",
773-
"gasSensorConfig" : "Gas Sensor Configurations",
774-
"gasSensorPeriodHint" : "Enter update period (100 - 5000 ms)",
775-
"mq135" : "MQ-135"
775+
"concentrationPpm" : "Concentration (PPM)"
776776
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import 'dart:async';
2+
import 'package:flutter/material.dart';
3+
import 'package:pslab/communication/peripherals/i2c.dart';
4+
import 'package:pslab/communication/science_lab.dart';
5+
import '../communication/sensors/mlx90614.dart';
6+
import '../l10n/app_localizations.dart';
7+
import '../models/chart_data_points.dart';
8+
import 'package:pslab/others/logger_service.dart';
9+
import 'locator.dart';
10+
11+
class MLX90614Provider extends ChangeNotifier {
12+
AppLocalizations get appLocalizations => getIt.get<AppLocalizations>();
13+
14+
MLX90614? _mlx90614;
15+
Timer? _dataTimer;
16+
17+
double _objectTemperature = 0.0;
18+
double _ambientTemperature = 0.0;
19+
20+
final List<ChartDataPoint> _objectTemperatureData = [];
21+
final List<ChartDataPoint> _ambientTemperatureData = [];
22+
23+
bool _isRunning = false;
24+
bool _isLooping = false;
25+
int _timegapMs = 1000;
26+
int _numberOfReadings = 100;
27+
int _collectedReadings = 0;
28+
29+
double _currentTime = 0.0;
30+
static const int maxDataPoints = 1000;
31+
32+
double get objectTemperature => _objectTemperature;
33+
double get ambientTemperature => _ambientTemperature;
34+
35+
List<ChartDataPoint> get objectTemperatureData =>
36+
List.unmodifiable(_objectTemperatureData);
37+
List<ChartDataPoint> get ambientTemperatureData =>
38+
List.unmodifiable(_ambientTemperatureData);
39+
40+
bool get isRunning => _isRunning;
41+
bool get isLooping => _isLooping;
42+
int get timegapMs => _timegapMs;
43+
int get numberOfReadings => _numberOfReadings;
44+
int get collectedReadings => _collectedReadings;
45+
46+
MLX90614Provider();
47+
48+
Future<void> initializeSensors({
49+
required Function(String) onError,
50+
required I2C? i2c,
51+
required ScienceLab? scienceLab,
52+
}) async {
53+
try {
54+
if (i2c == null || scienceLab == null) {
55+
onError(appLocalizations.pslabNotConnected);
56+
return;
57+
}
58+
59+
if (!scienceLab.isConnected()) {
60+
onError(appLocalizations.pslabNotConnected);
61+
return;
62+
}
63+
64+
_mlx90614 = await MLX90614.create(i2c, scienceLab);
65+
notifyListeners();
66+
} catch (e) {
67+
logger.e('Error initializing MLX90614: $e');
68+
}
69+
}
70+
71+
void toggleDataCollection() {
72+
if (_isRunning) {
73+
_stopDataCollection();
74+
} else {
75+
_startDataCollection();
76+
}
77+
}
78+
79+
void _startDataCollection() {
80+
if (_mlx90614 == null) return;
81+
82+
_isRunning = true;
83+
_collectedReadings = 0;
84+
85+
_dataTimer =
86+
Timer.periodic(Duration(milliseconds: _timegapMs), (timer) async {
87+
try {
88+
await _fetchSensorData();
89+
_collectedReadings++;
90+
91+
if (!_isLooping && _collectedReadings >= _numberOfReadings) {
92+
_stopDataCollection();
93+
}
94+
95+
if (_isLooping && _objectTemperatureData.length >= maxDataPoints) {
96+
_removeOldestDataPoints();
97+
}
98+
} catch (e) {
99+
logger.e('Error fetching sensor data: $e');
100+
}
101+
});
102+
notifyListeners();
103+
}
104+
105+
void _stopDataCollection() {
106+
_isRunning = false;
107+
_dataTimer?.cancel();
108+
_dataTimer = null;
109+
notifyListeners();
110+
}
111+
112+
Future<void> _fetchSensorData() async {
113+
if (_mlx90614 == null) return;
114+
115+
try {
116+
final rawData = await _mlx90614!.getRawData();
117+
118+
_objectTemperature = rawData['objectTemperature'] ?? 0.0;
119+
_ambientTemperature = rawData['ambientTemperature'] ?? 0.0;
120+
121+
_currentTime += _timegapMs / 1000.0;
122+
123+
_addDataPoint(_objectTemperatureData, _objectTemperature);
124+
_addDataPoint(_ambientTemperatureData, _ambientTemperature);
125+
126+
notifyListeners();
127+
} catch (e) {
128+
logger.e('Error in _fetchSensorData: $e');
129+
rethrow;
130+
}
131+
}
132+
133+
void _addDataPoint(List<ChartDataPoint> dataList, double value) {
134+
dataList.add(ChartDataPoint(_currentTime, value));
135+
if (dataList.length > 50) {
136+
dataList.removeAt(0);
137+
}
138+
}
139+
140+
void _removeOldestDataPoints() {
141+
const keepPoints = 800;
142+
143+
if (_objectTemperatureData.length > keepPoints) {
144+
final removeCount = _objectTemperatureData.length - keepPoints;
145+
_objectTemperatureData.removeRange(0, removeCount);
146+
_ambientTemperatureData.removeRange(0, removeCount);
147+
}
148+
}
149+
150+
void toggleLooping() {
151+
_isLooping = !_isLooping;
152+
notifyListeners();
153+
}
154+
155+
void setTimegap(int timegapMs) {
156+
_timegapMs = timegapMs;
157+
158+
if (_isRunning) {
159+
_stopDataCollection();
160+
_startDataCollection();
161+
}
162+
163+
notifyListeners();
164+
}
165+
166+
void setNumberOfReadings(int numberOfReadings) {
167+
_numberOfReadings = numberOfReadings;
168+
notifyListeners();
169+
}
170+
171+
void clearData() {
172+
_objectTemperatureData.clear();
173+
_ambientTemperatureData.clear();
174+
_objectTemperature = 0.0;
175+
_ambientTemperature = 0.0;
176+
_currentTime = 0.0;
177+
_collectedReadings = 0;
178+
notifyListeners();
179+
}
180+
181+
bool get isCollectionComplete {
182+
return !_isLooping && _collectedReadings >= _numberOfReadings;
183+
}
184+
185+
@override
186+
void dispose() {
187+
_stopDataCollection();
188+
super.dispose();
189+
}
190+
}

0 commit comments

Comments
 (0)