Skip to content

Commit 8348c0e

Browse files
committed
movesense_flutter initial release ready
1 parent 554ca92 commit 8348c0e

File tree

8 files changed

+653
-201
lines changed

8 files changed

+653
-201
lines changed
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1-
## 0.0.1
1+
## 1.0.0
22

3-
* TODO: Describe initial release.
3+
Initial release supporting:
4+
5+
* scan for Movensense devices (and stop scanning again)
6+
* connect and disconnect to a device
7+
* get device and state information
8+
* get a stream of the following data from a device:
9+
* heart rate
10+
* ECG
11+
* IMU
12+
* temperature
13+
* state events (movement, battery, connectors, double tap, tap, free fall)

packages/movesense_flutter/README.md

Lines changed: 108 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
<!--
2-
This README describes the package. If you publish this package to pub.dev,
3-
this README's contents appear on the landing page for your package.
4-
5-
For information about how to write a good package README, see the guide for
6-
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
7-
8-
For general information about developing packages, see the Dart guide for
9-
[creating packages](https://dart.dev/guides/libraries/create-packages)
10-
and the Flutter guide for
11-
[developing packages and plugins](https://flutter.dev/to/develop-packages).
12-
-->
13-
141
A Flutter plugin for accessing the [Movesense](https://www.movesense.com/) family of ECG devices. This is a developer-friendly wrapper of the [mdsflutter](https://pub.dev/packages/mdsflutter) plugin.
152

163
## Features
@@ -19,10 +6,9 @@ This plugin supports the following features, which is the most commonly used sub
196

207
* scan for Movensense devices (and stop scanning again)
218
* connect and disconnect to a device
22-
* get device information
23-
* turn on/off the LED on the device
9+
* get device and state information
2410
* get a stream of the following data from a device:
25-
* heartrate
11+
* heart rate
2612
* ECG
2713
* IMU
2814
* temperature
@@ -37,7 +23,7 @@ Similar to the [mdsflutter](https://pub.dev/packages/mdsflutter) plugin, the Mov
3723
Install the Movesense iOS library using CocoaPods with adding this line to your app's Podfile:
3824

3925
```shell
40-
pod 'Movesense', :git => 'ssh://git@altssh.bitbucket.org:443/movesense/movesense-mobile-lib.git'
26+
pod 'Movesense', :git => 'ssh://git@altssh.bitbucket.org:443/movesense/movesense-mobile-lib.git'
4127
```
4228

4329
Then set up your `ios/Podfile` as follows:
@@ -74,15 +60,113 @@ allprojects {
7460

7561
## Usage
7662

77-
TODO: Include short and useful examples for package users. Add longer examples
78-
to `/example` folder.
63+
### Scanning for devices
64+
65+
The singleton `Movesense` can be used for scanning devices:
66+
67+
```dart
68+
/// Listen for discovered devices
69+
Movesense().devices.listen((device) {
70+
debugPrint('Discovered device: ${device.name} [${device.address}]');
71+
});
72+
/// Start scanning.
73+
Movesense().scan();
74+
75+
/// Stop scanning at some later point.
76+
Movesense().stopScan();
77+
```
78+
79+
### Connecting to a device
80+
81+
Scanning return a `MovesenseDevice` which can be used to connect to the device, like:
82+
83+
```dart
84+
if (!device.isConnected) {
85+
device.connect();
86+
}
87+
```
88+
89+
A `MovesenseDevice` can either be obtained using the scan method above, or can be create if the address of the device is know:
90+
91+
```dart
92+
final MovesenseDevice device = MovesenseDevice(address: '0C:8C:DC:1B:23:BF');
93+
94+
device.connect();
95+
```
96+
97+
Connection status is available in the `status` can is emitted in the `statusEvents` stream.
7998

8099
```dart
81-
const like = 'sample';
100+
print(device.status);
101+
102+
device.statusEvents.listen((status) {
103+
print('Device connection status changed: ${status.name}');
104+
});
82105
```
83106

84-
## Additional information
107+
### Accessing device information and battery status
108+
109+
The following device information and status is available as getter methods:
110+
111+
* `getDeviceInfo` - get the full [device info](https://www.movesense.com/docs/esw/api_reference/#info) of this Movesense device.
112+
* `getBatteryStatus` - get the [battery level](https://www.movesense.com/docs/esw/api_reference/#systemstates) ("OK" or "LOW") from the device.
113+
* `getState` - get the [system state](https://www.movesense.com/docs/esw/api_reference/#systemstates) from the device (movement, connectors, doubleTap, tap, freeFall).
114+
115+
For example, getting device info and battery level:
116+
117+
```dart
118+
var info = await device.getDeviceInfo();
119+
print('Product name: ${info?.productName}');
120+
121+
var battery = await device.getBatteryStatus();
122+
print('Battery level: ${battery.name}');
123+
```
124+
125+
### Streaming sensor data
126+
127+
The following sensor data is available as streams:
128+
129+
* `hr` - Heart rate as an `int` at 1 Hz.
130+
* `ecg` - Electrocardiography (ECG) as a sample of reading at 125 Hz.
131+
* `imu` - 9-axis Inertial Movement Unit (IMU) at 13 Hz.
132+
* `temperature` - Surface temperature of the device in Kelvin.
133+
134+
For example, you can listen to the heart rate stream like this:
135+
136+
```dart
137+
// Start listening to the stream of heart rate readings
138+
var hrSubscription = device.hr.listen((hr) {
139+
print('Heart Rate: $hr');
140+
});
141+
142+
// Stop listening.
143+
hrSubscription?.cancel();
144+
```
145+
146+
The example app shows streaming heart rate data, once connected to a device.
147+
148+
### Streaming state change events
149+
150+
You can also listen to a stream of [system state](https://www.movesense.com/docs/esw/api_reference/#systemstates) from the device (movement, connectors, doubleTap, tap, freeFall). For example, listening for 'tap' events like this:
151+
152+
```dart
153+
stateSubscription = device
154+
.getStateEvents(SystemStateComponent.tap)
155+
.listen((state) {
156+
print('State: $state');
157+
});
158+
```
159+
160+
Note, however, that 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. 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.
161+
162+
## Example App
163+
164+
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.
165+
166+
## Features and bugs
167+
168+
Please read about existing issues and file new feature requests and bug reports at the issue tracker.
169+
170+
## License
85171

86-
TODO: Tell users more about the package: where to find more information, how to
87-
contribute to the package, how to file issues, what response they can expect
88-
from the package authors, and more.
172+
This software is copyright (c) the [Technical University of Denmark](https://dtu.dk/) (DTU) and is part of the [Copenhagen Research Platform](https://carp.dk/). This software is available 'as-is' under a [MIT license](LICENSE).

packages/movesense_flutter/example/lib/main.dart

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class MovesenseHomePageState extends State<MovesenseHomePage> {
2828
final MovesenseDevice device = MovesenseDevice(address: '0C:8C:DC:1B:23:BF');
2929
bool isSampling = false;
3030
StreamSubscription<int>? hrSubscription;
31+
StreamSubscription<MovesenseState>? stateSubscription;
3132

3233
@override
3334
void initState() {
@@ -54,21 +55,17 @@ class MovesenseHomePageState extends State<MovesenseHomePage> {
5455
mainAxisAlignment: MainAxisAlignment.center,
5556
children: <Widget>[
5657
StreamBuilder<DeviceConnectionStatus>(
57-
stream: device.statusStream,
58+
stream: device.statusEvents,
5859
builder: (context, snapshot) =>
5960
Text('Movesense [${device.address}] - ${device.status.name}'),
6061
),
6162
const Text('Your heart rate is:'),
6263
StreamBuilder<int>(
63-
stream: device.heartRate,
64-
builder: (context, snapshot) {
65-
var displayText = '...';
66-
if (snapshot.hasData) displayText = '${snapshot.data}';
67-
return Text(
68-
displayText,
69-
style: Theme.of(context).textTheme.headlineMedium,
70-
);
71-
},
64+
stream: device.hr,
65+
builder: (context, snapshot) => Text(
66+
snapshot.hasData ? '${snapshot.data}' : '...',
67+
style: Theme.of(context).textTheme.headlineMedium,
68+
),
7269
),
7370
],
7471
),
@@ -84,25 +81,40 @@ class MovesenseHomePageState extends State<MovesenseHomePage> {
8481
);
8582
}
8683

84+
/// Handle button press to connect to device and start/stop sampling.
8785
void onButtonPressed() {
8886
setState(() {
89-
// Movesense().devices.listen((device) {
90-
// debugPrint('Discovered device: ${device.name} [${device.address}]');
91-
// });
92-
// Movesense().scan();
9387
if (!device.isConnected) {
88+
// if not connected, connect to the device
9489
device.connect();
9590
} else {
96-
device.getDeviceInfo();
97-
// // if (!isSampling) {
98-
// // hrSubscription = device.heartRate.listen((hr) {
99-
// // debugPrint('Heart Rate: $hr');
100-
// // });
101-
// // isSampling = true;
102-
// // } else {
103-
// // hrSubscription?.cancel();
104-
// // isSampling = false;
105-
// // }
91+
// when connected, first get device info and battery status
92+
device.getDeviceInfo().then(
93+
(info) => debugPrint('>> Product name: ${info?.productName}'),
94+
);
95+
96+
device.getBatteryStatus().then(
97+
(battery) => debugPrint('>> Battery level: ${battery.name}'),
98+
);
99+
// then start/stop sampling
100+
if (!isSampling) {
101+
// Example of subscribing to heart rate data
102+
hrSubscription = device.hr.listen((hr) {
103+
debugPrint('>> Heart Rate: $hr');
104+
});
105+
106+
// Example of subscribing to tap state changes
107+
stateSubscription = device
108+
.getStateEvents(SystemStateComponent.tap)
109+
.listen((state) {
110+
debugPrint('>> State: ${state.toString()}');
111+
});
112+
isSampling = true;
113+
} else {
114+
hrSubscription?.cancel();
115+
stateSubscription?.cancel();
116+
isSampling = false;
117+
}
106118
}
107119
});
108120
}

packages/movesense_flutter/lib/device_info.dart

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

0 commit comments

Comments
 (0)