Mobile Development 14 min read

How to Capture Real‑Time Heart Rate Data in a Taro Mini‑Program Using BLE

This tutorial walks through building a Taro mini‑program that connects to a BLE heart‑rate strap, discovers services and characteristics, reads device name and heart‑rate measurements, and handles data conversion, providing code examples and practical insights for mobile developers.

ELab Team
ELab Team
ELab Team
How to Capture Real‑Time Heart Rate Data in a Taro Mini‑Program Using BLE

Background

The author started a weight‑loss plan and bought a heart‑rate strap, aiming to monitor heart rate while using a rowing machine. The strap does not display values directly and requires an app or device connection. The official app only shows real‑time data without charts.

The strap must be connected via an app; it does not show raw numbers.

The official app provides live heart‑rate data but cannot generate statistical charts.

Using the Gudong app, the strap can monitor heart‑rate changes during exercise, but the rowing machine is not a supported activity. The author also created a simple rowing‑machine metronome mini‑program.

Simple Bluetooth Overview

Bluetooth Low Energy (BLE) enables low‑power device communication. The tutorial briefly introduces central (client) and peripheral (server) devices, GAP (Generic Access Profile) for device discovery, and GATT (Generic Attribute Profile) for data exchange.

Central / Peripheral

Client (central): the author's phone. Server (peripheral): the heart‑rate strap, earphones, etc.

BLE

BLE offers lower energy consumption compared to classic Bluetooth. The tutorial focuses on GAP (device discovery) and GATT (service/characteristic interaction).

GAP (Generic Access Profile)

Controls device advertising and connection. Peripherals broadcast information; the central discovers and connects. Connections are exclusive, stopping advertising after establishment. iBeacon devices broadcast without connecting.

GATT (Generic Attribute Profile)

Defines services and characteristics. A service groups one or more characteristics. Characteristics are the smallest data units, with properties such as read, write, notify, indicate. Each has a unique UUID, some predefined by the Bluetooth SIG, others custom.

API Overview

Taro Bluetooth APIs

Taro.openBluetoothAdapter(option)

Initializes the Bluetooth module.

Taro.openBluetoothAdapter({
  success: function (res) {
    console.log("Bluetooth environment started:", res);
    setInitStatus(true);
  },
  fail: function (err) {
    if (err.errMsg.includes("fail already opened")) {
      console.log("Bluetooth environment already started:", err);
      setInitStatus(true);
    } else {
      console.log("Bluetooth environment start failed:", err);
      setInitStatus(false);
    }
  },
});

Taro.getBluetoothDevices(option)

Gets all discovered Bluetooth devices during the module's active period.

discoverInterval = setInterval(() => {
  Taro.getBluetoothDevices({
    success: async function (res) {
      const canConnectDevices = res.devices.filter(
        (item) =>
          item.RSSI > -80 &&
          !["未知设备", "MBeacon"].includes(item.name)
      );
      console.log("Discovered devices:", canConnectDevices);
      setDeviceList(() => canConnectDevices);
    },
  });
}, 2000);

You can also use Taro.onBluetoothDeviceFound(callback) to discover devices.

Taro.createBLEConnection(option)

Connects to a BLE device. If the device was previously discovered, you can pass its deviceId directly.

Taro.createBLEConnection({
  deviceId,
  success: function (res) {
    console.log("Device connected", res);
    setConnectDeviceId(deviceId);
    Taro.onBLEConnectionStateChange(function (res) {
      console.log(`Device ${res.deviceId} connection state changed: ${res.connected}`);
      if (!res.connected) {
        setConnectDeviceId("");
      }
    });
  },
});

Taro.getBLEDeviceServices(option)

Retrieves all services of a connected BLE device.

Taro.getBLEDeviceServices({
  deviceId,
  success: function (res) {
    console.log('device services:', res.services);
  }
});

Taro.getBLEDeviceCharacteristics(option)

Gets all characteristics of a specific service.

Taro.getBLEDeviceCharacteristics({
  deviceId,
  serviceId,
  success: function (res) {
    console.log('device characteristics:', res.characteristics);
  }
});

Taro.readBLECharacteristicValue(option)

Reads the binary value of a characteristic (requires the characteristic to support read).

The read data must be obtained via the onBLECharacteristicValueChange callback.

Taro.notifyBLECharacteristicValueChange(option)

Enables notifications for characteristic value changes (requires notify or indicate support).

Taro.onBLECharacteristicValueChange(callback)

Listens for characteristic value change events; must enable notifications first.

Web Bluetooth API

References to MDN and other resources are provided for further reading.

Device Name Details

After calling getBLEDeviceServices, the service list shows the needed service (0x1800). Using getBLEDeviceCharacteristics, the characteristic 0x2A00 is identified as the device name, which has the read property.

Taro.onBLECharacteristicValueChange(function (characteristic) {
  const buffer = characteristic.value;
  const uint8Array = new Uint8Array(buffer);
  console.log("uint8Array:", uint8Array);
  const encodedString = String.fromCodePoint.apply(null, uint8Array);
  console.log('Device name:', encodedString);
});

Taro.readBLECharacteristicValue({
  deviceId,
  serviceId: DeviceNameService,
  characteristicId: DeviceNameCharacteristics,
});

The output shows the device name (e.g., a specific earphone model).

Heart Rate Measurement Details

The required service and characteristic UUIDs are:

export const HEART_RATE_SERVICE_UUID = "0000180D-0000-1000-8000-00805F9B34FB";
export const HEART_RATE_CHARACTERISTIC_UUID = "00002A37-0000-1000-8000-00805F9B34FB";

After connecting, subscribe to characteristic changes:

export const blueToothGetHeartRate = (deviceId, onHeartRateChange) => {
  Taro.onBLECharacteristicValueChange(function (characteristic) {
    const heartRateValue = getHeartRateValue(characteristic.value);
    onHeartRateChange(heartRateValue);
  });
  Taro.notifyBLECharacteristicValueChange({
    state: true,
    deviceId,
    serviceId: HEART_RATE_SERVICE_UUID,
    characteristicId: HEART_RATE_CHARACTERISTIC_UUID,
  });
};

The heart‑rate data is obtained, but the raw bytes need decoding according to the Bluetooth Heart Rate Service specification.

Flag field (binary 10110) indicates heart‑rate format (uint8), sensor contact status, energy expended presence, and RR‑interval presence. The heart‑rate value (binary 1100101) equals 101 bpm. RR‑interval bytes (0x02, 0x52) decode to 594 ms, matching 60 000 ms / 101 bpm.

Conclusion and Open Questions

The implementation successfully reads heart‑rate data while using a metronome. Remaining questions include:

Why prepend "00" and slice the last two characters when converting a byte to a two‑digit hex string?

How does a phone obtain battery level information from earphones that do not expose the standard Battery Service (0x180F) and Battery Level characteristic (0x2A19) in the discovered services?

The author invites knowledgeable readers to provide answers.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptBluetoothmini-programTaroBLEHeart Rate
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.