Recently I’ve attended a
hackathon powerpointathon which required the teams to build a working prototype sell a load of bullshit. Because our team consisted of actual engineers without a PhD in Bullshitology, we failed miserably at the challenge because we built a working prototype. 😆
We had the chance to work with an Apple Watch, a Polar H10 heart rate sensor, and a Muse brain sensing headband. Our goal was to measure Heart Rate Variability and alpha wave values, which both may (or may not, medical science is very complex) be related to the amount of stress a person is experiencing.
In this post I’ll show you how to accomplish the following with an iOS app:
- Get the most accurate heart rate measurement possible using an Apple Watch and HealthKit
- Get a lot more accurate heart rate measurements using the Polar H10
- Get the alpha wave measurements using the Muse headband
As always all source code is available on my GitHub account.
Reading heart rate data using an Apple Watch is a lot different than reading from conventional bluetooth monitors, because it is not possible to read the bluetooth data directly. Instead all health data, such as the heart rate, must be read with the HealthKit APIs.
Before you can read the heart rate from the Apple Watch, you must ask the user permission to access this data. First you need to specify the reason you wish to access this data inside the Info.plist file of the iPhone app (not the Watch app or extension). Add the following key and a description:
You can now request read access to the heart rate data when the Watch app has finished launching using the
requestAuthorization method of your
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1 2 3 4 5 6 7 8 9 10 11
Requesting access will display an short explanation on the Watch screen and will trigger an alert on the iPhone.
Once access is granted we can start measuring the heart rate. The Apple Watch continuously monitors the users heart rate, but only once every ~10 minutes when not in motion. To get a more frequent reading we need to start an
HKWorkoutSession. If you take off the Apple Watch and take a look at the bottom of the watch when starting a session, you will see the green LED lights of the heart rate monitor. Measuring heart rate with these lights is called photoplethysmography. Learn to pronounce this word correctly and then casually mention it in a conversation to impress friends and colleagues with your expert medical knowledge. 😉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
As you can see the workout session uses a delegate for its callbacks. Once the session is running the Apple Watch will automatically start transmitting health data to the iPhone, but it is not possible to read this data directly. Instead we need to query the
HKHealthStore for the stored heart rate data. To do this we use an
HKAnchoredObjectQuery which has an
updateHandler callback which continuously returns new and deleted values for the query.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
updateHeartRate method receives all heart rate updates while the workout session is running. It also calculates the difference in time between each update, and as you can see when running this code you’ll notice the Apple Watch will send a heart rate value each ~5 seconds.
Because we are interested in calculating the Heart Rate Variability this is not accurate enough at all, since this method requires that we know the amount of milliseconds between each heart beat, also known as the RR interval. With the introduction of HealthKit the RR interval was mentioned as part of the available data, but unfortunately it didn’t make it into the released version of HealthKit on our devices.
Which brings us to the next part of this post, measuring the heart rate and RR intervals with a Polar H10 heart rate monitor.
The Polar H10 is a bluetooth heart rate monitor which can be connected to using CoreBluetooth. It’s the same process as described in my previous post about measuring heart rates with the Mio Alpha 2 watch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
Once the Polar H10 is connected it automatically starts transmitting the heart rate. To read this data we can enable notifications for the
hrCharacteristic which was found while connecting to the device.
1 2 3 4 5 6 7 8
The heart rate data is transmitted as raw bytes according to the Bluetooth Heart Rate Measurement specification.
This specification tells us the first byte contains the
Flags, which will tell us how to read the remaining bytes we received. Reading the first byte with
(rxData as NSData).getBytes(&flags, length: 1) returns
16 to the 8 bits in this byte gives us
00010000. Using these bit values we can determine how to interpret the rest of the received data. The meaning of these bits (from right to left):
0- 1 bit for Heart Rate Value Format => 0: Heart Rate Value Format is set to UINT8. Units: beats per minute (bpm).
00- 2 bits for Sensor Contact Status => 0: Sensor Contact feature is not supported in the current connection.
0- 1 bit for Energy Expended Status => 0: Energy Expended field is not present.
1- 1 bit for RR-Interval => 1: One or more RR-Interval values are present. Yay! 🎉
So, we have received the heart rate value as UINT8, and one or more RR interval values as UINT16. This means the next byte contains the heart rate value, and every 2 subsequent bytes contain an RR interval value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
This is great, the Polar H10 is sending us the RR interval for every heart beat. This means we don’t even need the BPM value which is sent as well because once we have the RR interval we can easily calculate the current BPM which is
60 / (rr / 1000).
The next step is actually calculating the Heart Rate Variability. This turned out to be much more complicated than we thought and it wasn’t possible to do this within 24 hours we had for this hackathon. Sorry! I did find an interesting open source Heart Rate Monitor for macOS which also worked properly with the Polar H10 and does a lot of different HRV calculations.
The final topic of this post is measuring brain waves with the Muse headband. This is also a bluetooth device like the Apple Watch and Polar H10 and I included it in this post because this device has yet another way of connecting to it. Both HealthKit and a regular bluetooth connection are not available. I tried connecting to the device using CoreBluetooth and it does report several available characteristics, but none of them seem to broadcast any data once connected. I don’t know how they did it but it’s probably because they offer an SDK and want to keep implementation details private. If you know more perhaps you can leave a comment on this post. 😊
The Muse SDK is an Objective-C framework (at the time of writing), so adding this framework to our Swift project requires that we create an Objective-C bridging header in the project. The easiest way to do this is to create a new file in the Xcode project, select
Objective-C File and enter a random filename with file type
Empty File. When you do this Xcode will ask if you wish to configure an Objective-C bridging header. Click the
Create Bridging Header button and afterwards delete the Objective-C file you just created.
Now all you have to do is open the bridging header file and import the Muse framework. Once this is done you will be able to magically call all Objective-C methods from the framework in Swift style. 😎
Connecting to the Muse and receiving the data is pretty straight forward and documented in the SDK. You can view my code in the GitHub repository which contains all code mentioned in this post.
Thanks for reading this post! Here’s a short clip of the prototype we made. You can see the Alpha waves measured at 4 different points of my head and my heart rate as measured by the Polar H10. As you can see, I was not experiencing any stress at all. 🙄