Skip to content

Commit 557224b

Browse files
committed
added RRI to HR data + cleanup
1 parent d6ae449 commit 557224b

File tree

10 files changed

+49
-41
lines changed

10 files changed

+49
-41
lines changed

packages/movesense_plus/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.1.0
2+
3+
* added R-R interval to HR reading
4+
15
## 1.0.0
26

37
Initial release supporting:

packages/movesense_plus/README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ This plugin supports the following features, which is the most commonly used sub
1818

1919
Similar to the [mdsflutter](https://pub.dev/packages/mdsflutter) plugin, the Movesense library needs to be installed in your app.
2020

21+
> **NOTE:** This plugin does not handle permission to access Bluetooth. Use the [permission_handler](https://pub.dev/packages/permission_handler) plugin to request access to scan and connect to BLE devices. See the example app.
22+
2123
### iOS
2224

2325
Install the Movesense iOS library using CocoaPods with adding this line to your app's Podfile:
@@ -130,7 +132,7 @@ print('Battery level: ${battery.name}');
130132

131133
The following sensor data is available as streams:
132134

133-
* `hr` - Heart rate as an `int` at 1 Hz.
135+
* `hr` - Heart rate as average bpm and R-R interval at 1 Hz.
134136
* `ecg` - Electrocardiography (ECG) as a sample of reading at 125 Hz.
135137
* `imu` - 9-axis Inertial Movement Unit (IMU) at 13 Hz.
136138
* `temperature` - Surface temperature of the device in Kelvin.
@@ -139,8 +141,8 @@ For example, you can listen to the heart rate stream like this:
139141

140142
```dart
141143
// Start listening to the stream of heart rate readings
142-
var hrSubscription = device.hr.listen((hr) {
143-
print('Heart Rate: $hr');
144+
hrSubscription = device.hr.listen((hr) {
145+
print('Heart Rate: ${hr.average}, R-R Interval: ${hr.rr} ms');
144146
});
145147
146148
// Stop listening.
@@ -161,15 +163,15 @@ stateSubscription = device
161163
});
162164
```
163165

164-
> **NOTE:** Listening to system state events on a Movensense device comes with a lot of limitations. First of all, you can [only listen to one type of state events at a time](https://github.com/petri-lipponen-movesense/mdsflutter/issues/15). Second, not all Movesense devices seems to support subscription of all types of state events. For example, it seems like only the 'connectors' and 'tap' states are supported on the Movesense MD and HR2 devices.
166+
> **NOTE:** Listening to system state events on a Movensense device comes with some limitations. First of all, you can [only listen to one type of state event at a time](https://github.com/petri-lipponen-movesense/mdsflutter/issues/15). Second, not all Movesense devices seems to support subscription of all types of state events. For example, it seems like only the 'connectors' and 'tap' states are supported on the Movesense MD and HR2 devices.
165167
166168
## Example App
167169

168-
The included example app is very simple. It shows how to connect to a device with a known `address` and once connected, it listens to the heart rate (`hr`) stream and shows it in the app. Use the floating button to (i) connect, (ii) start streaming heart rate data, and (iii) stop streaming again.
170+
The included example app is very simple. It shows how to connect to a device with a known `address` and once connected, it listens to the heart rate (`hr`) stream and shows average bpm in the app. Use the floating button to (i) connect, (ii) start streaming heart rate data, and (iii) stop streaming again.
169171

170172
## Features and bugs
171173

172-
Please read about existing issues and file new feature requests and bug reports at the issue tracker.
174+
Please read about existing [issues](https://github.com/carp-dk/flutter-plugins/issues) and file new feature requests and bug reports as issues. We also happily accept contributions as [pull requests](https://github.com/carp-dk/flutter-plugins/pulls).
173175

174176
## License
175177

packages/movesense_plus/example/README.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

packages/movesense_plus/example/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
22

33
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
4+
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
45
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
56
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
67

packages/movesense_plus/example/lib/main.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ class MovesenseHomePage extends StatefulWidget {
2323
}
2424

2525
class MovesenseHomePageState extends State<MovesenseHomePage> {
26-
// My device address: 0C:8C:DC:1B:23:BF, serial 233830000816
2726
// Replace with your Movesense device address.
2827
final MovesenseDevice device = MovesenseDevice(address: '0C:8C:DC:1B:23:BF');
2928
bool isSampling = false;
30-
StreamSubscription<int>? hrSubscription;
29+
StreamSubscription<MovesenseHR>? hrSubscription;
3130
StreamSubscription<MovesenseState>? stateSubscription;
3231

3332
@override
@@ -60,10 +59,10 @@ class MovesenseHomePageState extends State<MovesenseHomePage> {
6059
Text('Movesense [${device.address}] - ${device.status.name}'),
6160
),
6261
const Text('Your heart rate is:'),
63-
StreamBuilder<int>(
62+
StreamBuilder<MovesenseHR>(
6463
stream: device.hr,
6564
builder: (context, snapshot) => Text(
66-
snapshot.hasData ? '${snapshot.data}' : '...',
65+
snapshot.hasData ? '${snapshot.data?.average}' : '...',
6766
style: Theme.of(context).textTheme.headlineMedium,
6867
),
6968
),
@@ -100,7 +99,9 @@ class MovesenseHomePageState extends State<MovesenseHomePage> {
10099
if (!isSampling) {
101100
// Example of subscribing to heart rate data
102101
hrSubscription = device.hr.listen((hr) {
103-
debugPrint('>> Heart Rate: $hr');
102+
debugPrint(
103+
'>> Heart Rate: ${hr.average}, R-R Interval: ${hr.rr} ms',
104+
);
104105
});
105106

106107
// Example of subscribing to tap state changes

packages/movesense_plus/example/pubspec.yaml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: movesense_plus_example
22
description: "An example of how to use the movesense_plus plugin."
33
publish_to: 'none'
4-
version: 0.1.0
4+
version: 1.1.0
55

66
environment:
77
sdk: ">=3.8.0 <4.0.0"
@@ -11,15 +11,10 @@ dependencies:
1111
flutter:
1212
sdk: flutter
1313

14+
permission_handler: ^11.0.0
1415
movesense_plus:
1516
path: ../../movesense_plus/
1617

17-
cupertino_icons: ^1.0.2
18-
permission_handler: ^11.0.0
19-
polar: ^7.0.0
20-
sembast: ^3.5.0
21-
path_provider: ^2.1.0
22-
mdsflutter: ^2.0.0
2318

2419
dev_dependencies:
2520
flutter_test:

packages/movesense_plus/lib/movesense_data.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,29 @@ class MovesenseState extends MovesenseData {
235235
String toString() => state.name;
236236
}
237237

238+
/// Heart rate (HR) reading with average BPM and latest R-R interval.
239+
///
240+
/// See https://www.movesense.com/docs/esw/api_reference/#meashr
241+
class MovesenseHR {
242+
/// The average heart rate (BPM).
243+
final int average;
244+
245+
/// The latest R-R measurement (ms).
246+
final int? rr;
247+
248+
MovesenseHR(this.average, [this.rr]);
249+
250+
factory MovesenseHR.fromMovesenseData(dynamic data) {
251+
num average = data["Body"]["average"] as num;
252+
// returns a list of R-R measures with only one entry (the latest)
253+
int rr = (data["Body"]["rrData"] as List<dynamic>)
254+
.map((e) => e as int)
255+
.toList()[0];
256+
257+
return MovesenseHR(average.toInt(), rr);
258+
}
259+
}
260+
238261
/// Electrocardiogram (ECG) reading.
239262
///
240263
/// See https://www.movesense.com/docs/esw/api_reference/#measecg

packages/movesense_plus/lib/movesense_device.dart

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,15 @@ class MovesenseDevice {
178178
return completer.future;
179179
}
180180

181-
/// A stream of heart rate (HR) measurements from the Movesense device.
182-
/// Only available when the device is connected.
183-
Stream<int> get hr => !isConnected
181+
/// A stream of heart rate (HR) and R-R interval measurements from the
182+
/// Movesense device.
183+
Stream<MovesenseHR> get hr => !isConnected
184184
? Stream.empty()
185185
: MdsAsync.subscribe(Mds.createSubscriptionUri(serial!, "/Meas/HR"), "{}")
186-
.map((data) => (data["Body"]["average"] as num).toInt())
186+
.map((data) => MovesenseHR.fromMovesenseData(data))
187187
.asBroadcastStream();
188188

189189
/// A stream of ECG measurements from the Movesense device collected at 125 Hz.
190-
/// Only available when the device is connected.
191190
Stream<MovesenseECG> get ecg => !isConnected
192191
? Stream.empty()
193192
: MdsAsync.subscribe(
@@ -198,7 +197,6 @@ class MovesenseDevice {
198197
.asBroadcastStream();
199198

200199
/// A stream of IMU measurements from the Movesense device collected at 13 Hz (lowest).
201-
/// Only available when the device is connected.
202200
Stream<MovesenseIMU> get imu => !isConnected
203201
? Stream.empty()
204202
: MdsAsync.subscribe(
@@ -209,7 +207,6 @@ class MovesenseDevice {
209207
.asBroadcastStream();
210208

211209
/// A stream of temperature measurements from the Movesense device.
212-
/// Only available when the device is connected.
213210
Stream<MovesenseTemperature> get temperature => !isConnected
214211
? Stream.empty()
215212
: MdsAsync.subscribe(
@@ -233,8 +230,6 @@ class MovesenseDevice {
233230
///
234231
/// The returned stream emits [MovesenseState] objects representing
235232
/// the state change events.
236-
///
237-
/// Only available when the device is connected.
238233
Stream<MovesenseState> getStateEvents(SystemStateComponent component) =>
239234
!isConnected
240235
? Stream.empty()

packages/movesense_plus/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: movesense_plus
22
description: "A Flutter package for Movesense sensor integration. Wraps the MDS plugin and provides easy access to Movesense device features and data streams."
3-
version: 1.0.0
3+
version: 1.1.0
44
homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/movesense_plus
55

66
environment:

packages/movesense_plus/test/movesense_plus_test.dart

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)