ECG Monitor

In this blog post we are going to show how to track on-demand data, using ECG as an example. Make sure you are acquainted with the general overview on using the Samsung Health Sensor SDK, by reading Samsung Health Sensor SDK Introduction. We created a sample application called ECG Monitor which allows you to track ECG using functionality covered in the article. You can download it and try it yourself.

ECG Monitor version 1.2.0
(132,6KB) Aug 20, 2024

How to Prepare for a Measurement

On successful connection to the Health Tracking Service, but before starting a measurement, we have to check if the device is able to track ECG data. We can do this by calling checkCapabilities():

List<HealthTrackerType> availableTrackers = 
healthTrackingService.getTrackingCapability().getSupportHealthTrackerTypes();

Make sure the returned list contains HealthTrackerType.ECG_ON_DEMAND:

if (!availableTrackers.contains(HealthTrackerType.ECG_ON_DEMAND)) {
    Toast.makeText(
    getApplicationContext(), getString(R.string.NoECGSupport), Toast.LENGTH_LONG)
    .show();
    Log.e(APP_TAG, "Device does not support ECG tracking");
    finish();
}

After that, we can set up the ECG Tracker object:

ecgTracker = healthTrackingService.getHealthTracker(HealthTrackerType.ECG_ON_DEMAND);

The next step is to prepare an event listener – an object to process incoming data. We create one at the start of the application and implement its functionality.

In this blog, we focus on the onDataReceived() event. It contains data gathered by the Samsung Health Sensor SDK as a list of data points. A full description of all elements of the list can be found in the API Reference document for ValueKey.EcgSet, so here we only focus on the necessary parts, which are LEAD_OFF and, of course, the ECG values.

Checking if Watch Electrodes Are In Contact

In order to track ECG values correctly, the watch electrodes must be in contact – meaning the watch must be worn tightly on the wrist and the user has to hold their index finger on the “Home” key. To track electrode contact, the LEAD_OFF flag is used. If its value is 0 – it means that electrodes are in contact. Otherwise – if it's 5 – it means the electrodes are not in contact. The ECG readings are valid only if the flag is set, otherwise they should be ignored.

The flag can be read in ValueKey.EcgSet:

final int isLeadOff = list.get(0).getValue(ValueKey.EcgSet.LEAD_OFF);

In the sample application we set a variable, which holds the current status of electrodes contact:

final int NO_CONTACT = 5;
if (isLeadOff == NO_CONTACT) {
	leadOff.set(true);
    return;
} else
	leadOff.set(false);

How to Read ECG Values

When the onDataReceived event is called, it provides a list of data points. The list has either 5 or 10 elements, but each of them contains an ECG value, so regardless of the list size we can iterate through all of them. Since ECG has a 500 Hz sample rate, we need to calculate the average ECG value in the received data. The following example calculates the average ECG value obtained in a sample:

private final HealthTracker.TrackerEventListener ecgListener = 
new HealthTracker.TrackerEventListener() {
    @Override
    public void onDataReceived(@NonNull List<DataPoint> list) {
        if (list.size() == 0)
            return;
        float sum = 0;
        for (DataPoint data : list) {
            final float curEcg = data.getValue(ValueKey.EcgSet.ECG_MV);
            sum += curEcg;
        }
        avgEcg.set(sum / list.size());

How to Track Incoming ECG Data

With the prerequisites in the previous steps done, we are now ready to measure ECG. First, we set up a listener:

ecgHandler.post(() -> ecgTracker.setEventListener(ecgListener));

We can now start receiving ECG data. Since the sampling frequency is 500 Hz, we cannot update the UI with each sample. Instead, we create a timer which updates ECG values every second for 30 seconds – the time required to make a successful measurement. In the timer, we analyze ECG data and act accordingly.

All on-demand sensors need about 2 seconds to warm up in order to provide accurate data – therefore we ignore the first 2 seconds of the measurement:

if (timeLeft > MEASUREMENT_DURATION - 2000)
    return;

After the warm up time, we check if both electrodes are in contact. If they are not, we show a warning to the user:

if (leadOff.get()) {
	runOnUiThread(() -> binding.txtOutput.setText(R.string.outputWarning));
}

If the data received is valid, we read the current average ECG value. After that, we update the UI with the result. For convenience, we also provide information on how much time is left until the end of the measurement:

else {
	final String measureValue = getString(R.string.MeasurementUpdate, timeLeft / 1000, 
    	String.format(Locale.ENGLISH, "%.2f", avgEcg.get()));
    runOnUiThread(() -> binding.txtOutput.setText(measureValue));
}

After 30 seconds, the timer reaches its end, and we stop the measurement by unsetting the listener:

ecgTracker.unsetEventListener();

Finally, we update the UI for the last time – with either the last measured ECG value or an error if electrodes were not in contact.

These steps show how we can use on-demand sensors to perform an ECG measurement. We encourage you to try doing it yourself and explore the other available features provided by the Samsung Health Sensor SDK.