BLE Heartrate monitor

Be aware a bug was fixed in 1.17.1. Release 1.17.1 · esphome/esphome · GitHub

good Info, but same behavior with 1.17.1

also same issue with 1.17.2 any idea whats wrong?

ok, additional Info:
I used another BLE sensor, which is a Home Trainer with a power meter.
It has very similar behavior:

[19:20:32][W][ble_sensor:082]: Error reading char at handle 49, status=2

all the static numbers work (like manufacturer)
what am I missing here?

Hi guys,
still the same issue with 1.18.0-dev. Any tips where would be the right place to ask for assistance?

Sorry, would love to help but I didn’t get any further with it either. None of the UUIDs related to anything useful so I’ve stopped using mine.

@jonnyrider that’s such a bummer, would have been a really good addition!
so all UUIDs are working for you? and it’s only the issue that the numbers make no sense?

Yeah, most of the UUIDs bring back figures.

The problem is that the current implementation of the BLE client in ESPHome only implements a sensor and a switch, and the former interprets the characteristic as a float number. For the battery service, this works, as the Battery Level characteristic just returns a number.

But if you read the specification of the Heart Rate Service, you’ll see that the Heart Rate Measurement characteristic is more complex than just a number. There’s a byte with some flags, the next one or two bytes is the heart rate, and then come some other bytes. For instance, here’s the relevant code in a non-ESPHome project I made that reads the heart rate value from a heart rate sensor:

To be able to read these values in ESPHome, the BLE client in ESPHome should be extended with another sensor component that reads the raw bytes from the characteristic and returns these as an array, comparable to what the BLE tracker component is doing now. Then you could add a lambda which gets the heart rate value in x[1], as it’s the second byte in the array.

@koan thank you very much for the explanation and the comprehensive documentation.

Just pulled the spec for cycling power (the other sensor I tried) and it is the same story there…

The ble_client component is designed to be pretty generic and extendable.

The initial inclusion of the sensor reads pure numeric values, though extending it to heart rate sensors and other non numeric formats seems easy.

However, given that heart rate sensors have standard uuids, it seems worthwhile to create a new ble_heartrate component that only needs to have a ble_client reference.
The same goes for other standard BLE types.

2 Likes

thanks for your assessment on this! As I am very much a beginner in programming I tried to look trough the files:
ble_client.cpp and ble_client.h those would be the ones to adapt, correct?
@buxtronix If I am trying to work on this a pull request would be the way to go?

@jeti your best bet is to look through the BLE sensor code, it fetches numerical values, and you can adapt to what’s returned by your device.

You can also look at Support for AM43 BLE blind motors by buxtronix · Pull Request #1744 · esphome/esphome · GitHub for another ble client based component as an example.

1 Like

Now that pull request #1851 (Add optional lambda to BLESensor for raw data parsing) has been merged in esphome:dev, you can do this to monitor the heart rate if you install the esphome development version (pip3 install https://github.com/esphome/esphome/archive/dev.zip):

esp32_ble_tracker:

ble_client:
  - mac_address: AA:BB:CC:DD:EE:FF
    id: heart_rate_monitor

sensor:
  - platform: ble_client
    ble_client_id: heart_rate_monitor
    id: heart_rate_measurement
    name: "Heart rate measurement"
    service_uuid: '180d'
    characteristic_uuid: '2a37'
    notify: true
    lambda: |-
      uint16_t heart_rate_measurement = x[1];
      if (x[0] & 1) {
          heart_rate_measurement += (x[2] << 8);
      }
      return (float)heart_rate_measurement;
    icon: 'mdi:heart'
    unit_of_measurement: 'bpm'

I just tested this on my fitness tracker, and this works nicely, updating my heart rate every second.

Unfortunately trying to show the information on a display isn’t possible yet, as the BLE and display components together use too much memory (see issues #1336, #1731 and #2045). I tried this both on an M5Stack Core and a LilyGO TTGO T-Display, and both fail to get working Wi-Fi and end up in a boot loop. Otherwise it would be a really cool and much more maintainable solution than my Arduino code to create a heart rate monitor!

4 Likes

So I just created this project to display the heart rate on an M5Stack Core or a LilyGO TTGO T-Display ESP32:

It works if I disable Wi-Fi, and I don’t mind for this purpose. The result looks like this:

Or this:

1 Like

this works like a charm! thanks for pointing this out!

Could you elaborate on what the code in your lambda is doing? I’ve tried sorting it out but I’m just not sure what it’s doing. I have a project I’m working on that deals with raw data processing on a BLE device and depending on what your code is doing, it could be very useful to my project.

heart_rate_measurement
    name: "tickr.heartrate"
    service_uuid: '180D'
    characteristic_uuid: '2A37'
    notify: true
    lambda: |-
      uint16_t heart_rate_measurement = x[1];
      if (x[0] & 1) {
          heart_rate_measurement += (x[2] << 8);
      }
      return (float)heart_rate_measurement;
    icon: 'mdi:heart'
    unit_of_measurement: 'bpm'
  - platform: ble_client
    ble_client_id: tickr_fit
    name: "tickr.battery"
    service_uuid: '180F'
    characteristic_uuid: '2A19'
    
  - platform: ble_rssi
    mac_address: F0:13:C3:B1:AC:67
    name: "tickr.RSSI"
    
ble_client:
  - mac_address: F0:13:C3:B1:AC:67
    id: tickr
1 Like

My puzzle is “what does this do”