---
title: How to Build a Heart Rate Monitor with Capacitor
description: Learn how to build a simple heart rate monitor app with Capacitor and the Bluetooth Low Energy plugin in just 15 minutes.
date: 
  created: 2024-08-05
  updated: 2026-07-14
authors:
  - robingenz
categories:
  - Capacitor
  - Guides
  - SDKs
links:
  - Capacitor Bluetooth Low Energy: sdks/capacitor/bluetooth-low-energy.md
faq: true
---

# How to Build a Heart Rate Monitor with Capacitor

Capacitor makes building a cross-platform app with one codebase easier than ever before.
Today we will use the Ionic Framework and Capacitor to create a simple heart rate monitor app for Android and iOS in just 15 minutes.
For this, we will use the [Capacitor Bluetooth Low Energy plugin](../../sdks/capacitor/bluetooth-low-energy.md) to connect to a heart rate sensor.

<!-- more -->

The heart rate sensor we use in this tutorial is the [Polar H9 Heart Rate Sensor](https://www.polar.com/en/sensors/h9-heart-rate-sensor){:target="_blank"}, but any other Bluetooth heart rate sensor should work as well.
You can find the complete app code referenced in this guide on [GitHub](https://github.com/capawesome-team/capacitor-heart-rate-monitor-app){:target="_blank"}.

<figure markdown>
  ![Capacitor Heart Rate Monitor App](../../assets/images/gifs/capacitor-heart-rate-monitor.gif){ width="300" }
  <figcaption>Capacitor Heart Rate Monitor App</figcaption>
</figure>

## Basics

### Bluetooth Low Energy

First, let's talk about Bluetooth Low Energy (BLE) and how it works.
BLE is a wireless communication technology that is designed to reduce power consumption while maintaining a similar communication range to classic Bluetooth.
BLE is used in many devices, such as heart rate sensors, fitness trackers, smartwatches, and more.

#### Centrals and Peripherals

BLE devices are divided into two categories: peripherals and centrals.
Peripherals are devices that provide data, such as heart rate sensors, while centrals are devices that consume data, such as smartphones.
In our case, the heart rate sensor is the peripheral, and the smartphone is the central.

#### Services and Characteristics

BLE communication is based on the concept of services and characteristics.
A service is a collection of characteristics, and a characteristic is a piece of data that can be read, written, or notified.
For example, a heart rate sensor has a service that contains a characteristic for the heart rate measurement.
Each service and characteristic is represented by a unique UUID (Universally Unique Identifier).
The UUIDs are standardized and defined by the Bluetooth SIG (Special Interest Group).

## Prerequisites

Before we start, download and install the following tools to ensure an optimal developer experience:

- [Node.js](https://nodejs.org/en/download){:target="_blank"} to install the required dependencies
- A code editor for... writing code! **Tip**: Visual Studio Code supports the new [Ionic VS Code Extension](https://ionicframework.com/docs/intro/vscode-extension){:target="_blank"}
- [Android Studio](https://developer.android.com/studio){:target="_blank"} to build the Android app
- [Xcode](https://apps.apple.com/de/app/xcode/id497799835){:target="_blank"} to build the iOS app (only available on macOS)

## Create a new app

To create a new project, we simply use the [Ionic CLI](https://ionicframework.com/docs/cli){:target="_blank"}. For this, first install the CLI globally:

```bash
npm install -g @ionic/cli
```

Then you can create a new project with the `ionic start` command:

```bash
npx ionic start heart-rate-monitor-app blank --type=angular --capacitor
```

In this case, the app is called `heart-rate-monitor-app`, the starter template is `blank`, and the project type for the purposes of this guide is Angular. 
You can also choose Vue or React, for example. Additionally, we enable the Capacitor integration with `--capacitor`.

Once everything is ready, you should see this output:

```bash
Your Ionic app is ready! Follow these next steps:

- Go to your new project: cd .\heart-rate-monitor-app
- Run ionic serve within the app directory to see your app in the browser
- Run ionic capacitor add to add a native iOS or Android project using Capacitor
- Generate your app icon and splash screens using cordova-res --skip-config --copy
- Explore the Ionic docs for components, tutorials, and more: https://ion.link/docs
- Building an enterprise app? Ionic has Enterprise Support and Features: https://ion.link/enterprise-edition
```

### Add the Android platform

If you want to build the app for Android, you need to add the Android platform.
For this, first install the `@capacitor/android` package:

```bash
npm install @capacitor/android
```

Then add the Android platform using the following command:

```bash
npx cap add android
```

### Add the iOS platform

If you want to build the app for iOS, you need to add the iOS platform.
For this, first install the `@capacitor/ios` package:

```bash
npm install @capacitor/ios
```

Then add the iOS platform using the following command:

```bash
npx cap add ios
```

## Implement the Heart Rate Monitor

### Install the Bluetooth Low Energy plugin

In order to connect to the heart rate sensor, we need to install the [Capacitor Bluetooth Low Energy plugin](../../sdks/capacitor/bluetooth-low-energy.md).
Please note that the plugin is currently only available to [Insiders](../../insiders/index.md).
See [Getting started with Insiders](../../insiders/getting-started.md/?plugin=capacitor-bluetooth-low-energy) and follow the instructions to install the plugin.

On **Android**, this plugin requires the following permissions be added to your `AndroidManifest.xml` before or after the `application` tag:

```xml
<!-- Needed only if your app looks for Bluetooth devices.  -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- Needed only if your app communicates with already-paired Bluetooth devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Needed only if your app uses Bluetooth scan results to derive physical location. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Needed only if your app uses the foreground service. -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
```

You can read more about Bluetooth permissions in the [Android documentation](https://developer.android.com/develop/connectivity/bluetooth/bt-permissions){:target="_blank"}.

You also need to add the following service **inside** the `application` tag in your `AndroidManifest.xml` (usually `android/app/src/main/AndroidManifest.xml`):

```xml
<service android:name="io.capawesome.capacitorjs.plugins.bluetoothle.BluetoothLowEnergyService" android:foregroundServiceType="connectedDevice" />
```

On **iOS**, add the `NSBluetoothPeripheralUsageDescription` and `NSBluetoothAlwaysUsageDescription` keys to the `Info.plist` file (usually `ios/App/App/Info.plist`), which tells the user why the app needs access to Bluetooth peripherals:

```xml
<key>NSBluetoothAlwaysUsageDescription</key>
<string>The app needs access to Bluetooth peripherals to communicate with Bluetooth devices.</string>
```

The plugin is now ready to use.

### Build the page

Let's start with the actual implementation.
Since the app already has a blank page called `home`, we can use this page to display the heart rate.
The page consists of a title and the current heart rate.
For reason of simplicity, there will be no interactive elements. 
The app will try to establish a connection to the heart rate sensor via BLE **directly after the page is loaded** and display the current heart rate.

The following code goes to your `src/app/home/home.page.ts` file:

```ts
import { Component, OnInit, signal } from '@angular/core';
import { Capacitor } from '@capacitor/core';
import { BluetoothLowEnergy } from '@capawesome-team/capacitor-bluetooth-low-energy';
import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton } from '@ionic/angular/standalone';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
  standalone: true,
  imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonButton],
})
export class HomePage implements OnInit {
  public heartRate = signal<undefined | number>(undefined);

  constructor() {}

  public async ngOnInit() {
    // 1. Initialize the Bluetooth Low Energy plugin
    if (Capacitor.getPlatform() === 'ios') {
      await BluetoothLowEnergy.initialize();
    } else {
      await BluetoothLowEnergy.requestPermissions();
    }
    // 2. Add a listener for the `deviceScanned` event
    await BluetoothLowEnergy.addListener('deviceScanned', async (event) => {
      if (event.name?.startsWith('Polar H9')) {
        // 4. Stop scanning for devices
        void BluetoothLowEnergy.stopScan();
        // 5. Connect to the device
        await BluetoothLowEnergy.connect({
          deviceId: event.id,
        });
        // 6. Discover services
        await BluetoothLowEnergy.discoverServices({
          deviceId: event.id,
        });
        // 7. Add a listener for the `characteristicChanged` event
        await BluetoothLowEnergy.addListener('characteristicChanged', (event) => {
          let byteArray = new Uint8Array(event.value);
          let firstBitValue = byteArray[0] & 0x01;
          if (firstBitValue === 0) {
            this.heartRate.set(byteArray[1]);
          } else {
            this.heartRate.set((byteArray[1] << 8) | byteArray[2]);
          }
        });
        // 8. Start notifications for the Heart Rate Measurement characteristic
        await BluetoothLowEnergy.startCharacteristicNotifications({
          deviceId: event.id,
          serviceId: '0000180D-0000-1000-8000-00805F9B34FB',
          characteristicId: '00002A37-0000-1000-8000-00805F9B34FB',
        });
      }
    });
    // 3. Start scanning for devices
    await BluetoothLowEnergy.startScan();
  }
}
```

Let's break down the code:

1. **Initialize the Bluetooth Low Energy plugin**: First, we need to initialize the plugin. For iOS, we need to call [`initialize()`](../../sdks/capacitor/bluetooth-low-energy.md#initialize), and for Android, we need to request the necessary permissions with `requestPermissions()`.
2. **Add the `deviceScanned` listener**: We need to add a listener for the [`deviceScanned`](../../sdks/capacitor/bluetooth-low-energy.md#addlistenerdevicescanned) event before we start scanning for devices. This listener will be called whenever a BLE device is found.
3. **Start scanning for devices**: To start scanning for devices, we call [`startScan()`](../../sdks/capacitor/bluetooth-low-energy.md#startscan). As soon as a device is found, the [`deviceScanned`](../../sdks/capacitor/bluetooth-low-energy.md#addlistenerdevicescanned) event is emitted.
4. **Stop scanning for devices**: After we found the heart rate sensor, we stop scanning for devices. This is important to save battery life.
5. **Connect to the device**: To connect to the heart rate sensor, we call [`connect()`](../../sdks/capacitor/bluetooth-low-energy.md#connect) with the device ID.
6. **Discover services**: After connecting to the device, we need to discover the services. This is necessary to find the service that contains the heart rate measurement characteristic. For this, we call [`discoverServices()`](../../sdks/capacitor/bluetooth-low-energy.md#discoverservices).
7. **Add the `characteristicChanged` listener**: We need to add a listener for the [`characteristicChanged`](../../sdks/capacitor/bluetooth-low-energy.md#addlistenercharacteristicchanged) event to receive the heart rate measurements. This event is emitted whenever the heart rate measurement characteristic changes. The value of the characteristic is a byte array that contains the heart rate measurement. Depending on the first bit of the first byte, the heart rate is either a single byte or two bytes. If the heart rate is a single byte, the heart rate is the second byte. If the heart rate is two bytes, the heart rate is the second byte shifted left by 8 bits and combined with the third byte.
8. **Start notifications for the Heart Rate Measurement characteristic**: To receive the heart rate measurements, we need to start notifications for the heart rate measurement characteristic. For this, we call [`startCharacteristicNotifications()`](../../sdks/capacitor/bluetooth-low-energy.md#startcharacteristicnotifications). The service ID and characteristic ID are standardized and defined by the Bluetooth SIG. The service ID for the heart rate service is `0000180D-0000-1000-8000-00805F9B34FB`, and the characteristic ID for the heart rate measurement is `00002A37-0000-1000-8000-00805F9B34FB`.

Next, we need to create the HTML and CSS for the page.
As mentioned earlier, we will keep it simple and only display a title and the current heart rate.
The following code goes to your `src/app/home/home.page.html` file:

```html
<ion-header>
  <ion-toolbar>
    <ion-title>
      Heart Rate Monitor
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <div class="title">Current heart rate</div>
  <div class="body">{{ heartRate() ?? '--' }}</div>
</ion-content>
```

The following code goes to your `src/app/home/home.page.scss` file:

```css
ion-content {
  text-align: center;
}

.title {
  font-size: 32px;
  margin-top: 36px;
}

.body {
  font-size: 64px;
  font-weight: bold;
  margin-top: 48px;
}
```

That's it! Now everything is set up to build and run the app. 🎉

## Run the app

To run your app on Android, use the following command:

```bash
npx ionic cap run android
```

To run your app on iOS, use the following command:

```bash
npx ionic cap run ios
```

## Reading the Device's Built-In Sensors

The heart rate monitor above reads data from an external sensor over [Bluetooth Low Energy](../../sdks/capacitor/bluetooth-low-energy.md). But every phone also ships with a set of built-in sensors you can read directly, no pairing required. If your app needs motion, orientation, or environmental data, these plugins expose them behind the same simple, listener-based API:

- **[Compass](../../sdks/capacitor/compass.md)** — read the device heading and listen for live updates, including true north on iOS.
- **[Gyroscope](../../sdks/capacitor/gyroscope.md)** — real-time rotation rate measurements for motion-based interactions.
- **[Proximity Sensor](../../sdks/capacitor/proximity-sensor.md)** — detect nearby objects, for example to blank the screen during a call.
- **[Light Sensor](../../sdks/capacitor/light-sensor.md)** — read ambient illuminance in lux to adapt your UI to the surroundings (Android).
- **[Barometer](../../sdks/capacitor/barometer.md)** — obtain static air pressure and altitude from the barometer sensor.
- **[Thermal State](../../sdks/capacitor/thermal-state.md)** — react to the device thermal state before the OS throttles your app during heavy work.

## FAQ

### Do I need a physical device to build this heart rate monitor?

Yes. Bluetooth Low Energy talks to real hardware, so you need to run the app on a physical Android or iOS device — the iOS Simulator and Android emulator don't expose Bluetooth. You also need an actual BLE heart rate sensor, such as a chest strap or a compatible smartwatch.

### Does the Capacitor Bluetooth Low Energy plugin work on both Android and iOS?

Yes, on both. Each platform requires the appropriate Bluetooth permissions in its native configuration; see the [Installation](../../sdks/capacitor/bluetooth-low-energy.md#installation) section of the plugin documentation for the exact setup.

### My app can't find the heart rate sensor — what's wrong?

The usual causes are missing Bluetooth (and, on Android, location) permissions, a sensor that isn't in pairing mode, or scanning for the wrong service. Double-check the permissions and confirm you're filtering for the Heart Rate service (`0x180D`).

### Can I read sensors that don't use Bluetooth?

Yes. The phone's built-in sensors — compass, gyroscope, proximity, ambient light, barometer, and thermal state — are available directly without pairing. See [Reading the Device's Built-In Sensors](#reading-the-devices-built-in-sensors) above.

## Conclusion

In this guide, we learned how to build a simple heart rate monitor app with Capacitor.
We used the [Capacitor Bluetooth Low Energy plugin](../../sdks/capacitor/bluetooth-low-energy.md) to connect to a heart rate sensor and receive the heart rate measurements.
This approach reads live measurements straight from the sensor; if you instead want the heart rate history a smartwatch already writes to Apple Health or Health Connect, take a look at the [Capacitor Health plugin](./announcing-the-capacitor-health-plugin.md).
If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-plugins){:target="_blank"} in the GitHub repository. Make sure you follow Capawesome on [X](https://twitter.com/capawesomeio){:target="_blank"} so you don't miss any future updates.
