Movesense ECG Sampling Package Released

As part of continued support for cardiovascular studies (like REAFEL and CATCH) we have now released the carp_movesense_package sampling package for the CARP Mobile Sensing (CAMS) framework that supports the Movesense devices. In particular, the package supports the devices that can collect and stream HR and ECG data over BLE to CAMS:

Figure 1 – The Movesense Medical is a small strap-based ECG sensor that is small and convenient to wear.

The Movesense Medical device is a wearable ECG monitor and movement sensor for health wearables. It is Class IIa certified for medical use and forms an essential building block for new healthcare solutions. The HR+ and HR2 devices are optimized for exercise and daily activities and are an ideal platform for creating new smart wearables for well-being and sports.

The key features of these devices are:

  • Single channel ECG, heart rate, R-R intervals
  • Movement measurement (9-axis IMU: accelerometer, gyroscope, magnetometer)
  • Wireless data transmission with Bluetooth Low Energy
  • Small and lightweight (9.4g/0.33oz with battery)
  • User-replaceable CR 2025 battery
  • Class IIa Medical Device, EU Medical Device Regulation MDR 2017/745 (only the Movesense Medical)

The CAMS sampling package supports the collection of the following measures:

  • State changes (like moving, tapping, etc.)
  • Heart rate (HR)
  • Electrocardiogram (ECG)
  • Device temperature
  • 9-axis Inertial Movement Unit (IMU)

An example of how to add these measures to a CAMS protocol would look like this:

  // Create a study protocol
  StudyProtocol protocol = StudyProtocol(
    ownerId: 'owner@dtu.dk',
    name: 'Movesense Sensing Example',
  );

  // define which devices are used for data collection - both phone and eSense
  var phone = Smartphone();
  var movesense = MovesenseDevice(
    serial: '220330000122',
    address: '0C:8C:DC:3F:B2:CD',
    name: 'Movesense Medical',
    deviceType: MovesenseDeviceType.MD,
  );

  protocol
    ..addPrimaryDevice(phone)
    ..addConnectedDevice(movesense, phone);

  // Add a background task that immediately starts collecting HR and ECG data
  // from the Movesense device.
  protocol.addTaskControl(
      ImmediateTrigger(),
      BackgroundTask(measures: [
        Measure(type: MovesenseSamplingPackage.HR),
        Measure(type: MovesenseSamplingPackage.ECG),
      ]),
      movesense);

Example

To illustrate the use of the Movesense sampling package in the CARP Study App, we have created a simple protocol that:

  • Can connect to a Movesense Device over BLE
  • Samples single-tap events, HR, ECG, temperature, and IMU measures
  • Whenever a tap event is detected, a User Task is added to the list of tasks for the user to fill in details on symptoms.

The UI of this protocol is shown below.

Figure 2 – The UI of the CARP Studies app using a Movesense protocol. The Movesense device is connected and shown in the list of devices (left), the heart rate (HR) is sampled and shown in the data visualization page (middle), and when the Movesense device is tapped, a new user task is added to the list of tasks (right).

The protocol for this study looks like this (some non-Movesense details omitted):

protocol = SmartphoneStudyProtocol(
  name: 'Movesense Demo Protocol',
  ownerId: 'jakba',
)..studyDescription = StudyDescription(
   title: 'Movesense Demo Protocol',
   description: 'A simple study used to test the Movesense device.',
   purpose: '',
  );

Smartphone phone = Smartphone();
protocol.addPrimaryDevice(phone);

// Add a location service, since we ask for location in the symptoms survey below
LocationService locationService = LocationService();
protocol.addConnectedDevice(locationService, phone);

// Add the Movesense ECG device
var movesense = MovesenseDevice();
protocol!.addConnectedDevice(movesense, phone);

// Add background sensing to the phone
protocol.addTaskControl(
    ImmediateTrigger(),
    BackgroundTask(measures: [
      Measure(type: SensorSamplingPackage.AMBIENT_LIGHT),
      Measure(type: SensorSamplingPackage.STEP_COUNT),
      Measure(type: DeviceSamplingPackage.FREE_MEMORY),
      Measure(type: DeviceSamplingPackage.BATTERY_STATE),
      Measure(type: DeviceSamplingPackage.SCREEN_EVENT),
      Measure(type: ContextSamplingPackage.ACTIVITY),
    ]),
    phone);

// Add background sensing of Movesense measures
protocol.addTaskControl(
    ImmediateTrigger(),
    BackgroundTask(measures: [
      Measure(type: MovesenseSamplingPackage.STATE),
      Measure(type: MovesenseSamplingPackage.HR),
      Measure(type: MovesenseSamplingPackage.ECG),
      Measure(type: MovesenseSamplingPackage.TEMPERATURE),
      Measure(type: MovesenseSamplingPackage.IMU),
    ]),
    movesense);

// Create an app task that asks for symptoms when the user taps the Movesense device
// and collect location on where this survey was filled in.
var symptomsTask = RPAppTask(
    type: SurveyUserTask.SURVEY_TYPE,
    title: 'Cardiovascular Event',
    description: 'You tapped the heart rate monitor. '
        'Please provide details on the event you experienced.',
    minutesToComplete: 2,
    expire: const Duration(hours: 4),
    notification: true,
    rpTask: RPOrderedTask(
      identifier: "symptoms_survey",
      steps: [
        RPInstructionStep(
            identifier: 'cvd_instruction',
            title: "Cardiovascular Event",
            text:
                "In the following pages, you will be able to report on any cardiovascular "
                "event you may have experienced."
                "\nThis event is based on you tapping the device."),
        RPQuestionStep(
            identifier: 'cvd_timestamp',
            title: 'When did you experience this event?',
            answerFormat:
               RPDateTimeAnswerFormat(
                 dateTimeAnswerStyle: RPDateTimeAnswerStyle.DateAndTime)),
        RPQuestionStep(
            identifier: "cvd_symptoms",
            title: "Which symptom(s) did you experience when you tapped the device?",
            answerFormat:
                RPChoiceAnswerFormat(answerStyle: RPChoiceAnswerStyle.MultipleChoice, 
                  choices: [
              RPChoice(text: "None", value: 1),
              RPChoice(text: "Palpitation", value: 2),
              RPChoice(text: "Dizziness", value: 3),
              RPChoice(text: "Shortness of Breath", value: 4),
              RPChoice(text: "Chest Pain", value: 5),
              RPChoice(text: "Sweating", value: 6),
              RPChoice(text: "Heart Burn", value: 7),
            ])),
        RPQuestionStep(
          identifier: "cvd_duration",
          title: "For how long time did you experience the symptom(s)?",
          answerFormat: RPIntegerAnswerFormat(minValue: 0, maxValue: 20, suffix: "minutes"),
          optional: true,
        ),
        RPQuestionStep(
            identifier: "cvd_activity",
            title: "What activity were you doing when experienced these symptom(s)?",
            answerFormat: 
                RPChoiceAnswerFormat(answerStyle: RPChoiceAnswerStyle.SingleChoice, 
                  choices: [
              RPChoice(text: "Running", value: 1),
              RPChoice(text: "Walking", value: 2),
              RPChoice(text: "Cycling", value: 3),
              RPChoice(text: "Climbing Stairs", value: 4),
              RPChoice(text: "Bathing", value: 5),
              RPChoice(text: "Watching TV", value: 6),
              RPChoice(text: "Reading", value: 7),
              RPChoice(text: "Talking", value: 8),
              RPChoice(text: "None of the above", value: 9),
           ])),
        RPQuestionStep(
          identifier: "cvd_note",
          answerFormat: RPTextAnswerFormat(hintText: "Add note..."),
          title: "Additional notes",
          optional: true,
        ),
      ],
    ),
    measures: [Measure(type: ContextSamplingPackage.CURRENT_LOCATION)]);

// Add the symptoms task to the list of user tasks whenever there is a tap event detected
protocol.addTaskControl(
    SamplingEventTrigger(
      measureType: MovesenseSamplingPackage.STATE,
      triggerCondition: MovesenseStateChange(MovesenseDeviceState.tap),
    ),
    symptomsTask,
    phone);
    

The user task for collecting symptoms is a re-implementation of the same feature in the mCardia app and looks like this in the Study App:

Figure 3 – The UI of the CARP Studies app showing the user task for collecting data on a cardiovascular event: (i) date and time of the event, (ii) symptoms experienced during the event, and (iii) what activity was done during the event.