Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
31 changes: 31 additions & 0 deletions packages/movesense_flutter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/
10 changes: 10 additions & 0 deletions packages/movesense_flutter/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "f6ff1529fd6d8af5f706051d9251ac9231c83407"
channel: "stable"

project_type: package
13 changes: 13 additions & 0 deletions packages/movesense_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## 1.0.0

Initial release supporting:

* scan for Movensense devices (and stop scanning again)
* connect and disconnect to a device
* get device and state information
* get a stream of the following data from a device:
* heart rate
* ECG
* IMU
* temperature
* state events (movement, battery, connectors, double tap, tap, free fall)
9 changes: 9 additions & 0 deletions packages/movesense_flutter/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License.

Copyright 2025 the Technical University of Denmark (DTU).

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ”Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ”AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
172 changes: 172 additions & 0 deletions packages/movesense_flutter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
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.

## Features

This plugin supports the following features, which is the most commonly used sub-set of the total [Movensene API](https://www.movesense.com/docs/esw/api_reference/):

* scan for Movensense devices (and stop scanning again)
* connect and disconnect to a device
* get device and state information
* get a stream of the following data from a device:
* heart rate
* ECG
* IMU
* temperature
* state events (movement, battery, connectors, double tap, tap, free fall)

## Getting started

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

### iOS

Install the Movesense iOS library using CocoaPods with adding this line to your app's Podfile:

```shell
pod 'Movesense', :git => 'ssh://git@altssh.bitbucket.org:443/movesense/movesense-mobile-lib.git'
```

Then set up your `ios/Podfile` as follows:

```pod
target 'Runner' do
use_modular_headers!
use_frameworks! :linkage => :static

pod 'Movesense', :git => 'https://bitbucket.org/movesense/movesense-mobile-lib.git'

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
```

This will ensure that the MDS library is linked correctly. If you need to use frameworks that demand dynamic linking, [follow the instructions here](https://bitbucket.org/movesense/movesense-mobile-lib/issues/119/cannot-use-health-and-mdsflutter-depending).

### Android

Download `mdslib-x.x.x-release.aar` from the [movesense-mobile-lib](https://bitbucket.org/movesense/movesense-mobile-lib/src/master/) repository and put it somewhere under 'android' folder of your app. Preferably create a new folder named `android/libs` and put it there.

In the `build.gradle` of your android project, add the following lines (assuming `.aar` file is in `android/libs`):

```graddle
allprojects {
repositories {
...
flatDir{
dirs "$rootDir/libs"
}
}
}
```

## Usage

### Scanning for devices

The singleton `Movesense` can be used for scanning devices:

```dart
/// Listen for discovered devices
Movesense().devices.listen((device) {
debugPrint('Discovered device: ${device.name} [${device.address}]');
});
/// Start scanning.
Movesense().scan();

/// Stop scanning at some later point.
Movesense().stopScan();
```

### Connecting to a device

Scanning return a `MovesenseDevice` which can be used to connect to the device, like:

```dart
if (!device.isConnected) {
device.connect();
}
```

A `MovesenseDevice` can either be obtained using the scan method above, or can be create if the address of the device is know:

```dart
final MovesenseDevice device = MovesenseDevice(address: '0C:8C:DC:1B:23:BF');

device.connect();
```

Connection status is available in the `status` can is emitted in the `statusEvents` stream.

```dart
print(device.status);

device.statusEvents.listen((status) {
print('Device connection status changed: ${status.name}');
});
```

### Accessing device information and battery status

The following device information and status is available as getter methods:

* `getDeviceInfo` - get the full [device info](https://www.movesense.com/docs/esw/api_reference/#info) of this Movesense device.
* `getBatteryStatus` - get the [battery level](https://www.movesense.com/docs/esw/api_reference/#systemstates) ("OK" or "LOW") from the device.
* `getState` - get the [system state](https://www.movesense.com/docs/esw/api_reference/#systemstates) from the device (movement, connectors, doubleTap, tap, freeFall).

For example, getting device info and battery level:

```dart
var info = await device.getDeviceInfo();
print('Product name: ${info?.productName}');

var battery = await device.getBatteryStatus();
print('Battery level: ${battery.name}');
```

### Streaming sensor data

The following sensor data is available as streams:

* `hr` - Heart rate as an `int` at 1 Hz.
* `ecg` - Electrocardiography (ECG) as a sample of reading at 125 Hz.
* `imu` - 9-axis Inertial Movement Unit (IMU) at 13 Hz.
* `temperature` - Surface temperature of the device in Kelvin.

For example, you can listen to the heart rate stream like this:

```dart
// Start listening to the stream of heart rate readings
var hrSubscription = device.hr.listen((hr) {
print('Heart Rate: $hr');
});

// Stop listening.
hrSubscription?.cancel();
```

The example app shows streaming heart rate data, once connected to a device.

### Streaming state change events

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:

```dart
stateSubscription = device
.getStateEvents(SystemStateComponent.tap)
.listen((state) {
print('State: $state');
});
```

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.

## Example App

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.

## Features and bugs

Please read about existing issues and file new feature requests and bug reports at the issue tracker.

## License

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).
4 changes: 4 additions & 0 deletions packages/movesense_flutter/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
45 changes: 45 additions & 0 deletions packages/movesense_flutter/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
33 changes: 33 additions & 0 deletions packages/movesense_flutter/example/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "f6ff1529fd6d8af5f706051d9251ac9231c83407"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
- platform: android
create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
- platform: ios
create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
3 changes: 3 additions & 0 deletions packages/movesense_flutter/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# movesense_flutter_example

A new Flutter project.
1 change: 1 addition & 0 deletions packages/movesense_flutter/example/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:flutter_lints/flutter.yaml
14 changes: 14 additions & 0 deletions packages/movesense_flutter/example/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/

# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
44 changes: 44 additions & 0 deletions packages/movesense_flutter/example/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}

android {
namespace = "dk.carp.movesense_flutter_example"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "dk.carp.movesense_flutter_example"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}

flutter {
source = "../.."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
Loading