Bluetooth Heart Monitor
I recently purchased a CooSpo Chest Strap to measure my heart rate. I mostly got this for the purpose of getting some more data on my running and seeing what sort of ranges I was in while out on a run. When it arrived I excitedly unboxed it and tested it out and I was pleased to see it worked pretty well with my running app, as well as a simple heart rate monitor app on my phone.
Having been getting more interested in speedrunning lately and even doing some of my own speedruns on my stream, I noticed that one of my favourite runners to watch darbian has a heart rate display while he is doing runs. Personally I really like this feature, it's interesting to see, especially when the player has a calm demeanour that behind the scenes they're freaking out at being on PB pace. It gives me even more respect for their skills and makes the stream more entertaining to watch.
Armed with this knowledge, I thought to myself now that I have a heart rate monitor, it would be cool to incorporate it into my streams, let's take a look at what is out there right now.
Surveying the Scene - Heart Rate Monitoring on Linux
I began this search with high hopes. Since moving to Linux I've been incredibly impressed with the quantity and calibre of the apps available in the FOSS community. (Yes Calibre was sort of a pun there, as that is a fantastic example). Rarely do I have to do more than a quick google seach, peruse of GitHub or `yay -S` and I find myself with high quality software for free for almost any task. This time however it was not so easy. A hilarious side note, it seems I'm not the only one who's had this issue. While looking wherever I could I found a hilarious post on reddit from 2018 entitled: Are all Linux users slobs? The state of Fitness software for Linux. While this post was pretty funny, it did however confirm my doubts that good free software to connect to health peripherals were few and far between, so it looked like I was probably going to have to build something myself.
This decision led me down a bit of a rabbit hole, but I certainly learned a lot along the way, which is what it's all really about. So let's dive in, and learn a bit about Bluetooth.
Bluetooth
I pretty quickly stumbled onto some great blog posts about connecting to the Polar devices which are the most prominent bluteooth HR devices on the market. These were helpful as from their methodolgies and tools I could start cobbling together something that would be able to do what I wanted.
After reading a little about this I realised that the way this and other similar devices work is via the GATT (Generic Attribute Profile) protocol which is based on the ATT (Attribute Protocol). Therefore I need to use some blutooth library to discover 'services' available on my HR sensor. One of these services was the battery, and another, more helpfully was the HR measurement. According to the bluetooth spec, the measurement for HR was a 'characterstic' which could be read. After a lot of fiddling and trying different tools I managed to read the data with bluetoothctl. However this was only one part of my problem solved.
I realised that although I could read the values it would be much better to have a stream of values, so problem one was to be able to poll/listen to the changes in heart rate. The second problem I had was that the values were just hex at this point, and it wasn't quite as simple as just converting it to decimal. But we'll come back to that in a second.
The polling problem turned out to be a very easy one to solve, bluetoothctl allows you to use a 'notify on' command instead of read and then it will report the value each time it is received from the heart rate monitor. This was perfect, and things were starting to come together well now.
[COOSPO H6 0017925]# select-attribute /org/bluez/hci0/dev_EB_48_6B_02_72_8B/service0009/char000a
[COOSPO H6 0017925:/service0009/char000a]# notify on
For the other issue I had to consult the bluetooth spec sheet where I read that the values returned as the heart rate measurement was constructed like so:
So it was simply a matter of extracting the relavant hex data and then converting that to decimal, taking note of the bit that configured how large the value was.
Display
Having solved most of the problems I was feeling pretty happy with how things were going and wrote a script in python that utilised the python bluetooth 'gatt' library to read the bluetooth attributes. I could now print the readings to console and it was satisfying seeing them change as I did jumping jacks or lay down. For a while I fiddled around with creating a GUI or using websocketd to create a cool UI for this little script but in the end I decided it was more hassle than it was worth and settled for clearing the screen, printing a heart emoji and then the reading next to it each time a new value came through. I was very pleased with the end result, and it looks pretty good on my stream too, so mission accomplished!
References
These resources were invaluable in working this all out and will help a lot if you are ever interested in doing anything similar:
iOS Tutorial to Connect to Heart Rate Monitor
Blog Post about BLE
Blog Post connecting to Polar H10
Bluetooth Spec - GATT Characteristics
Bluetooth Spec - GATT Services
The code for my script
- etopiei (26/08/2020)