espruino / BangleApps

Bangle.js App Loader (and Apps)
https://banglejs.com/apps
MIT License
478 stars 1.14k forks source link

Heart rate way off #1908

Open Yenya opened 2 years ago

Yenya commented 2 years ago

Hello,

on my Bangle.js 2, the heart rate measurement is way off. I do cycling and sometimes borrow my wife's Garmin Vivoactive watch for it - it displays my heart rate quite reliably, even though I have the wrist strap quite loosely tightened. FWIW, depending on the effort I usually have about 120-175 bpm when cycling and about 80 bpm when idle. This is according to Vivoactive, and it sounds believable. Bangle.js 2 usually displays something around 40 bpm, which occasionally increases to 70. When cycling, I have never seen a measurement above 100 bpm.

How is the heart rate sensor implemented? Is it somewhere in the firmware? If so, how can I build a custom firmware and try to tinker with the implementation?

Thanks!

-Yenya

nxdefiant commented 2 years ago

see http://forum.espruino.com/conversations/373033 for a discussion on the problem. You can use Bangle.on('HRM-raw', (hrm) => {...}); to get the raw sensor value. So no need to modify the firmware for experimentation. In the long run implementing the algorithm in the firmware in C might be more efficient... Current implementation is https://github.com/espruino/Espruino/blob/master/libs/misc/heartrate.c

A paper that might get you started could be Pollreisz, D., TaheriNejad, N. Detection and Removal of Motion Artifacts in PPG Signals. Mobile Netw Appl (2019).

gfwilliams commented 2 years ago

Hi - thanks - as mentioned there are some attempts to improve this (above).

One issue you may be hitting is that the initial filter is designed for 50Hz but the default Bangle.js 2 sample rate is 25Hz, so it'll be filtering out the wrong frequencies - so a quick fix is just to raise the sample rate to 50Hz with http://www.espruino.com/Reference#l_Bangle_setOptions and that may help you somewhat.

But what we really need is some recorded data, and then hopefully we can start to make some more scientific improvements

Yenya commented 2 years ago

Hi, thanks for your help. I am however not sure what to do. When I connect to my bangle.js v2 with the web ide, and run Bangle.getOptions(), I see hrmPollInterval: 20 - I guess these are miliseconds, so it means it already uses 50 Hz sample rate. Am I wrong?

nxdefiant commented 2 years ago

Looks like the interval was reduces from 50Hz to 25Hz quite recently. hrmPollInterval on mine says 40.

@Yenya What firmware version are you running?

gfwilliams commented 2 years ago

Looks like the interval was reduces from 50Hz to 25Hz quite recently

The reported interval was, and on Bangle.js 2 kickstarter version it was dropped. All Bangles since the KickStarter have been 25Hz though.

@Yenya What firmware version are you running?

This - older firmwares reported the poll rate wrong (and also didn't allow changing it). You'd actually need to be on a 'cutting edge' build to be able to change the poll rate.

Yenya commented 2 years ago

It says "2v13". When I click to "Firmware update" at banglejs.com/apps, it says "Your current firmware is 2v13 and bootloader is unknown (CRC 4056371285)".

I flashed a newer firmware 2v13.136. After reboot, the hrmPollInterval value is 40. I tried to set it to 20 - now I see 40 to 60 bpm with Confidence around 60-70 % (there is no way for me to have 40 bpm heart rate).

What debugging data should I provide? I can for example record the heart rate with Garmin Vivoactive together with Bangle.js v2. What is the best way to record the raw data? And how much data do you need? (sorry, I am pretty new to this :-).

nxdefiant commented 2 years ago

Please follow the discussion in the forum: http://forum.espruino.com/conversations/372681/?offset=50 there is a hrmaccevents app in the apploader.

gfwilliams commented 2 years ago

I tried to set it to 20 - now I see 40 to 60 bpm with Confidence around 60-70 % (there is no way for me to have 40 bpm heart rate)

What was the heart rate you got reported before you changed the option? Is it the same?

You have to have set hrmPollInterval before the HRM is turned on - not sure if that helps at all?

What debugging data should I provide?

As @nxdefiant says. However...

I was kind of hoping one of the people on the forum would set something up, but it looks like it's not the case. I've just got back from a week off and I'm still working through emails, but I'll try and set up a test harness in a similar way to what I've done for the step counter, hopefully today.

But to really help, ideally we need an external bluetooth heart rate monitor as well - then we have some proper data to compare to

Yenya commented 2 years ago

OK, let's move to the forum.

However, in order to not leave the above questions unanswered:

You are right, I had HRM turned on all the time. When I turned it off, and only then set hrmPollInterval to 20, it now shows higher values than before. Yesterday I recorded two hours worth of data (of which the middle 1:20h is me cycling). Looking at the values, I think the highest values from any 10 or 20 surrounding ones show real data. I expect to have about 70 bpm when idle, 140-175 during the cycling workout, then quickly return to about 90-100 bpm, and then slowly decrease to the idle value. recorder9.csv recorder9

gfwilliams commented 2 years ago

Test harness now at https://github.com/gfwilliams/EspruinoHRMTestHarness

More info in http://forum.espruino.com/conversations/373033/#comment16554946

luc-spec commented 6 months ago

Is there a way that I might be able to help here? I am in the process of ordering a Polar H10 to get something resembling ground truth. I would love to contribute to this awesome project if I can.

thyttan commented 6 months ago

@luc-spec I think one thing is to contribute more data into the "data" folder on the test harness repo @gfwilliams linked to above. I meant to do that as well but haven't yet...

Edit: You can read threads on hrm on the forum, e.g. https://forum.espruino.com/comments/16454552/

Edit2: Also this thread: https://forum.espruino.com/conversations/385922/

gfwilliams commented 6 months ago

I should add that since this the last post on this bug report I've completely changed the HRM algorithm to a binary blob one supplied by the manufacturer of the sensor, which is significantly better. That happened in 2v18/Mar 2023 or before.

So personally I'd think maybe this issue could be closed...

Obviously more data for comparison is good (someone thought their sensor was always 10% on their watch but I wasn't able to verify that here), but we're not using the algorithm from https://github.com/gfwilliams/EspruinoHRMTestHarness now unless someone explicitly enables it in their build, and I think to switch back to that from the binary blob, the algorithm would need totally rewriting.

Moini commented 2 months ago

@gfwilliams Since we have that binary blob, the heart rate sensor for me no longer notices when the watch is not worn. I take it off at night, and it basically just reports random values that look almost the same as during the day. It's like it's just a blinking light with a RNG attached to it...

issue_screenshot

Moini commented 2 months ago

(thanks for normalizing the activity level, btw. - huge improvement!)

Moini commented 2 months ago

I just had a thought. My wristband is red. I'll invert the wristband tonight.

gfwilliams commented 2 months ago

When exactly was it working? Because the binary blob was added a year ago.

The HRM detects wear by whether it's getting any reflection from the IR LEDs or not - it's usually pretty good. But if you put the watch face-down then it's going to get a reflection from whatever you put it face down on.

I guess we could add something specific for "not moving and facing pretty much face down" that disabled the HRM in those cases though...

Moini commented 2 months ago

When exactly was it working? Because the binary blob was added a year ago.

Over night: never since the binary blob update (but before, it worked) - but I've only changed to not wearing the watch at night a couple months after that.

It seemed plausible during the day, though, and got a reading faster than before. I've not compared with the BT belt I've since acquired yet, though (nor did I remember to invert the wrist band last night).

It lies on the side, though, not face down, when I've taken it off. I'm afraid of scratching the screen....

Moini commented 2 months ago

So, it looks like if there is anything that reflects the light (beige bathroom tiles, red wristband), it will invent values, but it will stop recording any if there's nothing close by that could throw the light back.

Edit: Interestingly, also the Activity data gets flatter when the heart rate sensor has noticed that there's no pulse. It seems there's some creative guessing going on with that, too.

gfwilliams commented 2 months ago

The code that checks is at https://github.com/espruino/Espruino/blob/master/libs/misc/hrm_vc31.c#L376-L412

You could check your psValue/envValues from HRM-RAW events and see if they can be tweaked, but the values I used are the ones the manufacturer suggests. If you have dark skin I guess there may be less reflectance, and we don't want it to accidentally turn off for some people

Moini commented 2 months ago

No idea how to do that, @gfwilliams ... I've been told that I become invisible if standing in front of a white wall... :joy: