Skip to content

Commit 3a68e77

Browse files
feat: Added sensor MPU6050 (#3305)
Co-authored-by: Marc Nause <marc.nause@audioattack.de>
1 parent 7eb2432 commit 3a68e77

5 files changed

Lines changed: 722 additions & 2 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import 'package:pslab/communication/peripherals/i2c.dart';
2+
import 'package:pslab/communication/science_lab.dart';
3+
import 'package:pslab/others/logger_service.dart';
4+
5+
class MPU6050 {
6+
static const String tag = "MPU6050";
7+
static const int address = 0x68;
8+
9+
static const int gyroConfig = 0x1B;
10+
static const int accelConfig = 0x1C;
11+
static const int dataStart = 0x3B;
12+
13+
static const List<double> gyroScaling = [131.0, 65.5, 32.8, 16.4];
14+
static const List<double> accelScaling = [16384.0, 8192.0, 4096.0, 2048.0];
15+
16+
int arIndex = 3;
17+
int grIndex = 3;
18+
19+
final I2C i2c;
20+
21+
MPU6050._(this.i2c);
22+
23+
static Future<MPU6050> create(I2C i2c, ScienceLab scienceLab) async {
24+
final mpu = MPU6050._(i2c);
25+
await mpu._initialize(scienceLab);
26+
return mpu;
27+
}
28+
29+
Future<void> _initialize(ScienceLab scienceLab) async {
30+
if (!scienceLab.isConnected()) {
31+
throw Exception("ScienceLab not connected");
32+
}
33+
try {
34+
await i2c.write(address, [0], 0x6B);
35+
await setAccelerationRange(16);
36+
await setGyroRange(2000);
37+
} catch (e) {
38+
logger.e("Error initializing MPU6050: $e");
39+
rethrow;
40+
}
41+
}
42+
43+
Future<void> setGyroRange(int range) async {
44+
List<int> validRanges = [250, 500, 1000, 2000];
45+
grIndex = validRanges.indexOf(range);
46+
if (grIndex == -1) grIndex = 3;
47+
await i2c.write(address, [grIndex << 3], gyroConfig);
48+
}
49+
50+
Future<void> setAccelerationRange(int range) async {
51+
List<int> validRanges = [2, 4, 8, 16];
52+
arIndex = validRanges.indexOf(range);
53+
if (arIndex == -1) arIndex = 3;
54+
await i2c.write(address, [arIndex << 3], accelConfig);
55+
}
56+
57+
int _toSigned16(int msb, int lsb) {
58+
int val = (msb << 8) | lsb;
59+
if (val >= 0x8000) val -= 0x10000;
60+
return val;
61+
}
62+
63+
Future<Map<String, double>> getRawData() async {
64+
try {
65+
List<int> data = await i2c.readBulk(address, dataStart, 14);
66+
if (data.length < 14) {
67+
throw Exception("Expected 14 bytes but got ${data.length}");
68+
}
69+
70+
double ax = _toSigned16(data[0], data[1]) / accelScaling[arIndex];
71+
double ay = _toSigned16(data[2], data[3]) / accelScaling[arIndex];
72+
double az = _toSigned16(data[4], data[5]) / accelScaling[arIndex];
73+
74+
double temp = _toSigned16(data[6], data[7]) / 340.0 + 36.53;
75+
76+
double gx = _toSigned16(data[8], data[9]) / gyroScaling[grIndex];
77+
double gy = _toSigned16(data[10], data[11]) / gyroScaling[grIndex];
78+
double gz = _toSigned16(data[12], data[13]) / gyroScaling[grIndex];
79+
80+
return {
81+
'ax': ax,
82+
'ay': ay,
83+
'az': az,
84+
'gx': gx,
85+
'gy': gy,
86+
'gz': gz,
87+
'temperature': temp,
88+
};
89+
} catch (e) {
90+
logger.e("Error reading MPU6050 data: $e");
91+
rethrow;
92+
}
93+
}
94+
}

lib/l10n/app_en.arb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -730,5 +730,11 @@
730730
"pin_dpl_name": "D+",
731731
"pin_dpl_desc": "Test pin for USB Data +",
732732
"pin_dmi_name": "D-",
733-
"pin_dmi_desc": "Test pin for USB Data -"
734-
}
733+
"pin_dmi_desc": "Test pin for USB Data -",
734+
"mpu6050" : "MPU6050",
735+
"gyroRange" : "Gyro Range",
736+
"accelerationRange" : "Acceleration Range",
737+
"acceleration" : "Acceleration",
738+
"angularVelocity" : "Angular Velocity",
739+
"temp" : "Temp"
740+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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/mpu6050.dart';
6+
import '../models/chart_data_points.dart';
7+
import 'package:pslab/others/logger_service.dart';
8+
9+
class MPU6050Provider extends ChangeNotifier {
10+
MPU6050? _mpu6050;
11+
Timer? _dataTimer;
12+
13+
Map<String, double> _currentValues = {
14+
'ax': 0.0,
15+
'ay': 0.0,
16+
'az': 0.0,
17+
'gx': 0.0,
18+
'gy': 0.0,
19+
'gz': 0.0,
20+
'temperature': 0.0,
21+
};
22+
23+
final List<ChartDataPoint> _axData = [];
24+
final List<ChartDataPoint> _ayData = [];
25+
final List<ChartDataPoint> _azData = [];
26+
final List<ChartDataPoint> _gxData = [];
27+
final List<ChartDataPoint> _gyData = [];
28+
final List<ChartDataPoint> _gzData = [];
29+
30+
bool _isRunning = false;
31+
bool _isLooping = false;
32+
int _timegapMs = 500;
33+
int _numberOfReadings = 100;
34+
int _collectedReadings = 0;
35+
double _currentTime = 0.0;
36+
37+
int _selectedAccelRange = 16;
38+
int _selectedGyroRange = 2000;
39+
double? _selectedFilter; // null means OFF
40+
final String _selectedHighPassFilter = 'OFF';
41+
42+
Map<String, double> get currentValues => _currentValues;
43+
List<ChartDataPoint> get axData => List.unmodifiable(_axData);
44+
List<ChartDataPoint> get ayData => List.unmodifiable(_ayData);
45+
List<ChartDataPoint> get azData => List.unmodifiable(_azData);
46+
List<ChartDataPoint> get gxData => List.unmodifiable(_gxData);
47+
List<ChartDataPoint> get gyData => List.unmodifiable(_gyData);
48+
List<ChartDataPoint> get gzData => List.unmodifiable(_gzData);
49+
50+
bool get isRunning => _isRunning;
51+
bool get isLooping => _isLooping;
52+
int get timegapMs => _timegapMs;
53+
int get numberOfReadings => _numberOfReadings;
54+
55+
int get selectedAccelRange => _selectedAccelRange;
56+
int get selectedGyroRange => _selectedGyroRange;
57+
double? get selectedFilter => _selectedFilter;
58+
String get selectedHighPassFilter => _selectedHighPassFilter;
59+
60+
Future<void> initializeSensors({
61+
required Function(String) onError,
62+
required I2C? i2c,
63+
required ScienceLab? scienceLab,
64+
}) async {
65+
try {
66+
if (i2c == null || scienceLab == null || !scienceLab.isConnected()) {
67+
onError("PSLab not connected");
68+
return;
69+
}
70+
_mpu6050 = await MPU6050.create(i2c, scienceLab);
71+
notifyListeners();
72+
} catch (e) {
73+
logger.e('Error initializing MPU6050: $e');
74+
}
75+
}
76+
77+
Future<void> updateAccelRange(int range) async {
78+
_selectedAccelRange = range;
79+
await _mpu6050?.setAccelerationRange(range);
80+
notifyListeners();
81+
}
82+
83+
Future<void> updateGyroRange(int range) async {
84+
_selectedGyroRange = range;
85+
await _mpu6050?.setGyroRange(range);
86+
notifyListeners();
87+
}
88+
89+
void toggleDataCollection() {
90+
_isRunning ? _stopDataCollection() : _startDataCollection();
91+
}
92+
93+
void _startDataCollection() {
94+
if (_mpu6050 == null) return;
95+
_isRunning = true;
96+
_collectedReadings = 0;
97+
98+
_dataTimer =
99+
Timer.periodic(Duration(milliseconds: _timegapMs), (timer) async {
100+
try {
101+
await _fetchSensorData();
102+
_collectedReadings++;
103+
104+
if (!_isLooping && _collectedReadings >= _numberOfReadings) {
105+
_stopDataCollection();
106+
}
107+
if (_isLooping && _axData.length >= 1000) {
108+
_removeOldestDataPoints();
109+
}
110+
} catch (e) {
111+
logger.e('Error fetching MPU6050 data: $e');
112+
}
113+
});
114+
notifyListeners();
115+
}
116+
117+
void _stopDataCollection() {
118+
_isRunning = false;
119+
_dataTimer?.cancel();
120+
notifyListeners();
121+
}
122+
123+
Future<void> _fetchSensorData() async {
124+
if (_mpu6050 == null) return;
125+
try {
126+
_currentValues = await _mpu6050!.getRawData();
127+
_currentTime += _timegapMs / 1000.0;
128+
129+
_addDataPoint(_axData, _currentValues['ax']!);
130+
_addDataPoint(_ayData, _currentValues['ay']!);
131+
_addDataPoint(_azData, _currentValues['az']!);
132+
_addDataPoint(_gxData, _currentValues['gx']!);
133+
_addDataPoint(_gyData, _currentValues['gy']!);
134+
_addDataPoint(_gzData, _currentValues['gz']!);
135+
136+
notifyListeners();
137+
} catch (e) {
138+
logger.e('Error in _fetchSensorData: $e');
139+
}
140+
}
141+
142+
void _addDataPoint(List<ChartDataPoint> dataList, double value) {
143+
dataList.add(ChartDataPoint(_currentTime, value));
144+
if (dataList.length > 50) dataList.removeAt(0);
145+
}
146+
147+
void _removeOldestDataPoints() {
148+
final removeCount = _axData.length - 800;
149+
_axData.removeRange(0, removeCount);
150+
_ayData.removeRange(0, removeCount);
151+
_azData.removeRange(0, removeCount);
152+
_gxData.removeRange(0, removeCount);
153+
_gyData.removeRange(0, removeCount);
154+
_gzData.removeRange(0, removeCount);
155+
}
156+
157+
void toggleLooping() {
158+
_isLooping = !_isLooping;
159+
notifyListeners();
160+
}
161+
162+
void setTimegap(int ms) {
163+
_timegapMs = ms;
164+
if (_isRunning) {
165+
_stopDataCollection();
166+
_startDataCollection();
167+
}
168+
notifyListeners();
169+
}
170+
171+
void setNumberOfReadings(int val) {
172+
_numberOfReadings = val;
173+
notifyListeners();
174+
}
175+
176+
void clearData() {
177+
_axData.clear();
178+
_ayData.clear();
179+
_azData.clear();
180+
_gxData.clear();
181+
_gyData.clear();
182+
_gzData.clear();
183+
_currentTime = 0.0;
184+
_collectedReadings = 0;
185+
_currentValues.updateAll((key, value) => 0.0);
186+
notifyListeners();
187+
}
188+
189+
@override
190+
void dispose() {
191+
_stopDataCollection();
192+
super.dispose();
193+
}
194+
}

0 commit comments

Comments
 (0)