carp_mobile_sensing 1.1.0 copy "carp_mobile_sensing: ^1.1.0" to clipboard
carp_mobile_sensing: ^1.1.0 copied to clipboard

Mobile Sensing Framework for Flutter. A software framework for collecting sensor data from the phone and attached wearable devices via probes. Can be extended.

CARP Mobile Sensing Framework in Flutter #

pub package pub points github stars MIT License arXiv

This library contains the core Flutter package for the CARP Mobile Sensing (CAMS) framework. Supports cross-platform (iOS and Android) mobile sensing.

For an overview of all CAMS packages, see CARP Mobile Sensing in Flutter. For documentation on how to use CAMS, see the CAMS wiki.

Usage #

To use this plugin, add carp_mobile_sensing as dependencies in your pubspec.yaml file.

dependencies:
  flutter:
    sdk: flutter
  carp_core: ^latest
  carp_mobile_sensing: ^latest

Configuration #

When you want to add CAMS to you app, there are a few things to do in terms of configuring your app.

First, since CAMS rely on the awesome_notifications plugin, you should configure your app following their configuration guide for both Android and iOS.

Android Integration #

Set the minimum android SDK to 21 and Java SDK Version to 33 by setting the minSdkVersion, the compileSdkVersion, and targetSdkVersion in the build.gradle file, located in the android/app/ folder:

android {
    compileSdkVersion 33

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 33
        ...
    }
    ...
}

The pedometer (step count) probe needs permission to ACTIVITY_RECOGNITION. Add the following to your app's manifest.xml file located in android/app/src/main:

<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>

iOS Integration #

The pedometer (step count) probe uses NSMotion on iOS and the NSMotionUsageDescription needs to be specified in the app's Info.plist file located in ios/Runner:

<key>NSMotionUsageDescription</key>
<string>Collecting step count.</string>

For notification to work in background mode, you need to configure your iOS app to be set up for background actions.


NOTE: Other CAMS sampling packages require additional permissions in the manifest.xml or Info.plist files. See the documentation for each package.

Documentation #

The Dart API doc describes the different libraries and classes.

The CAMS wiki] contains detailed documentation on the CARP Mobile Sensing Framework, including the domain model, how to use it by create a study configuration, how to extend it, and an overview of the available measure types.

More scientific documentation of CAMS is available in the following papers:

@article{bardram2020carp,
  title={The CARP Mobile Sensing Framework--A Cross-platform, Reactive, Programming Framework and Runtime Environment for Digital Phenotyping},
  author={Bardram, Jakob E},
  journal={arXiv preprint arXiv:2006.11904},
  year={2020}
}

@article{bardram2022software,
  title={Software Architecture Patterns for Extending Sensing Capabilities and Data Formatting in Mobile Sensing},
  author={Bardram, Jakob E},
  journal={Sensors},
  volume={22},
  number={7},
  year={2022},
  publisher={MDPI}
}

Please use these references in any scientific papers using CAMS.

Examples of configuring and using CAMS #

There is a very simple example app app which shows how a study can be created with different tasks and measures. This app just prints the sensing data to a console screen on the phone. There is also a range of different examples on how to create a study to take inspiration from.

However, the CARP Mobile Sensing App provides a MUCH better example of how to use the framework in a Flutter BLoC architecture, including good documentation of how to do this.

Below is a small primer in the use of CAMS.

Following carp_core, a CAMS study can be configured, deployed, executed, and used in different steps:

  1. Define a SmartphoneStudyProtocol.
  2. Deploy this protocol to a DeploymentService.
  3. Get a study deployment for the phone and start executing this study deployment using a SmartPhoneClientManager.
  4. Use the generated data locally in the app or specify how and where to store or upload it using a DataEndPoint.

As a mobile sensing framework running on a phone, CAMS could be limited to support only step 3 and 4. However, to support the 'full cycle', CAMS also supports 1-2. This allows for local creation, deployment, and execution of study protocols (which in many applications have shown to be useful).

Defining a SmartphoneStudyProtocol #

In CAMS, a sensing protocol is configured in a SmartphoneStudyProtocol. Below is a simple example of how to set up a protocol that samples step counts, ambient light, screen events, and battery events.

// Create a study protocol storing data in a local SQLite database.
SmartphoneStudyProtocol protocol = SmartphoneStudyProtocol(
  ownerId: 'abc@dtu.dk',
  name: 'Track patient movement',
  dataEndPoint: SQLiteDataEndPoint(),
);

// Define which devices are used for data collection.
// In this case, its only this smartphone.
Smartphone phone = Smartphone();
protocol.addPrimaryDevice(phone);

// Automatically collect step count, ambient light, screen activity, and
// battery level. Sampling is delaying by 10 seconds.
protocol.addTaskControl(
  ImmediateTrigger(),
  BackgroundTask(measures: [
    Measure(type: SensorSamplingPackage.STEP_COUNT),
    Measure(type: SensorSamplingPackage.AMBIENT_LIGHT),
    Measure(type: DeviceSamplingPackage.SCREEN_EVENT),
    Measure(type: DeviceSamplingPackage.BATTERY_STATE),
  ]),
  phone,
);

The above example defines a simple SmartphoneStudyProtocol which will store data in a SQLite database locally on the phone using a SQLiteDataEndPoint. Sampling is configured by adding a TaskControl to the protocol using an ImmediateTrigger which triggers a BackgroundTask containing four different Measures.

Sampling can be configured in very sophisticated ways, by specifying different types of devices, triggers, tasks, measures and sampling configurations. See the CAMS wiki for an overview and more details.

You can write your own DataEndPoint definitions and corresponding DataManagers for uploading data to your own data endpoint. See the wiki on how to add a new data manager.

Using a DeploymentService #

A device deployment specifies how a study protocol is executed on a specific device - in this case a smartphone. According to the CARP Core domain model, a StudyProtocol can be deployed to a DeploymentService which handles the deployment of protocols for different devices. CAMS comes with a simple deployment service (the SmartphoneDeploymentService) which runs locally on the phone. This can be used to deploy a protocol and get back a MasterDeviceDeployment, which can be executed on the phone.

...

// Use the on-phone deployment service.
DeploymentService deploymentService = SmartphoneDeploymentService();

// Create a study deployment using the protocol
var status = await deploymentService.createStudyDeployment(protocol);

Creating a SmartPhoneClientManager and Running a SmartphoneDeploymentController #

A study deployment for a phone (master device) is handled by a SmartPhoneClientManager. This client manager controls the execution of one or more study deployments using a SmartphoneDeploymentController.

...

String studyDeploymentId = ... // any id obtained e.g. from an invitation
String roleName = ... // the role name of this phone in the protocol

// Create and configure a client manager for this phone
SmartPhoneClientManager client = SmartPhoneClientManager();
await client.configure(deploymentService: deploymentService);

// Add the study to the client manager and get a study runtime to
// control this deployment
Study study = await client.addStudy(studyDeploymentId, roleName);
SmartphoneDeploymentController? controller = client.getStudyRuntime(study);

// Deploy the study on this phone.
await controller?.tryDeployment();

// Configure the controller and start sampling.
await controller?.configure();
controller?.start();

Using the generated data #

The generated data can be accessed and used in the app. Access to data is done by listening on the measurements streams from the study deployment controller or some of its underlying executors or probes. Below are a few examples on how to listen on data streams.

// listening to the stream of all measurements from the controller
controller?.measurements.listen((measurement) => print(measurement));

// listen only on CARP measurements
controller?.measurements
    .where(
        (measurement) => measurement.data.format.namespace == NameSpace.CARP)
    .listen((event) => print(event));

// listen on ambient light measurements only
controller?.measurements
    .where((measurement) =>
        measurement.data.format.toString() ==
        SensorSamplingPackage.AMBIENT_LIGHT)
    .listen((measurement) => print(measurement));

// map measurements to JSON and then print
controller?.measurements
    .map((measurement) => measurement.toJson())
    .listen((json) => print(json));

// subscribe to the stream of measurements
StreamSubscription<Measurement> subscription =
    controller!.measurements.listen((Measurement measurement) {
  // do something w. the measurement, e.g. print the json
  print(JsonEncoder.withIndent(' ').convert(measurement));
});

Controlling the sampling of data #

The execution of sensing can be controlled on runtime in a number of ways. For example:

// Sampling can be stopped and started
controller.executor?.stop();
controller.executor?.start();

// Stop specific probe(s)
controller.executor
    ?.lookupProbe(CarpDataTypes.ACCELERATION_TYPE_NAME)
    .forEach((probe) => probe.stop());

// Adapt a measure
//
// Note that this will only work if the protocol is created locally on the
// phone (as in the example above)
// If downloaded and deserialized from json, then we need to locate the
// measure in the deployment
lightMeasure.overrideSamplingConfiguration = PeriodicSamplingConfiguration(
  interval: const Duration(minutes: 5),
  duration: const Duration(seconds: 10),
);

// Restart the light probe(s) in order to load the new configuration
controller.executor
    ?.lookupProbe(SensorSamplingPackage.AMBIENT_LIGHT)
    .forEach((probe) => probe.restart());


  // Once the sampling has to stop, e.g. in a Flutter dispose method,
  // call the controller's dispose method.
  controller.dispose();

  // Cancel the subscription.
  await subscription.cancel();

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) Copenhagen Center for Health Technology (CACHET) at the Technical University of Denmark (DTU). This software is available 'as-is' under a MIT license.