Bangle.js 2 Cutting Edge - new heart rate algorithm

Posted on
of 6
  • Either you managed to scratch the glass or the protector is still on. If I remember correctly it had no tab or something like that to pull it off. On my first Bangle I thought I had received a scratched one and only a few days later I discovered it to be a protector. It was a tiny bit "frosted". Without the protector you should have a mirror finish just like the display glass has.

    Edit: @adjtm hehe, you ninjaed my post very closely :D

  • Here is video recorded, exactly 60 seconds, reported HR is 57-61 but if you count all the beats drawn it is actually 70

    Also I tried Cardiograph android app­ls? on my phone and it matches what my other watches/fitness trackers show. I disabled network before starting it as some people in reviews claim that showing ads make it less relible. For me it worked pretty well with no ads shown.

  • I just tried, and they are identical for me - more or less. The Bangle.js one moves around a bit more (I guess it'd not filtered) but they were within 1 bpm after a while.

    is it possible that the proprietary firmware for VC31 and VC31B should be different?

    I don't believe so, no... the distribution I had seems to deal with both models.

    I'm not really sure what to suggest... I mean you could try setting the heart rate period down so it's at 12.5hz with Bangle.setOptions - but it seems really strange to me that it seems to work well for me and others but not you.

    You don't have any apps installed that might be adjusting the time on your Bangle.js while the HRM is running do you?

  • OK, good that it works for you, did you try same HR monitoring app - the one that draws continuously (if that could perhaps slow it down)?

    I have nothing special there, the clock showing time is OK, it does not go faster or slower. Will try to remove all apps.

  • you could try setting the heart rate period down so it's at 12.5hz with Bangle.setOptions

    How would I do that? That is not described here­gle_setOptions
    my hrmPollInterval option is 40

    I set it to 2o and the monitor was drawing much faster but the HR is still off by same fraction
    Also I updated to latest 2.17.118, no change.

    Anyway, if it is not a common issue that others see too then don't bother.

    I did factory reset, then installed just heart rate monitor app on top of that and it behaves still the same.

  • Funny thing, I downgraded to 2.17 release to get back the older open source HR code and that one is correct! When confidence is 100 it basically shows same numbers as my other watches.

  • Hmm, well that's interesting then - I guess at least we know it's not hardware then.

    I know you tried hrmPollInterval with 20 - did you try it with 80? The HRM algorithm expects data to come in more slowly and I wonder if sending it data too quickly is actually impacting its accuracy (although as I say, it seemed to work ok for me and at least also when those graphs were made).

    I'm not actually 100% sure if I used the HRM app or not. As I understand the HRM should be done in an interrupt so it wouldn't matter, but I guess you could try using a Clock face with ClockInfo and then switching to the HRM clockinfo.

    ... or just reading the value in code with Bangle.on('HRM',print);Bangle.setHRMPowe­r(1)

  • I know you tried hrmPollInterval with 20 - did you try it with 80?

    With 80 the confidence almost never reach 100% and is overall much worse so the heart rate is more random and it does not look like it has any effect on this. With this I've seen it even 'stabilize' on numbers over 110 while fitness tracker was showing below 90 but I also saw cases when it gave similar numbers like with poll interval 40 (too low)


    I tried this with hrmPollInterval back to 40 and 2v17.120 and it makes no difference when compared to running Heart Rate Monitor (v0.11), still about 8-12 less than what my fitness tracker says or what can I count myself on the visible data coming from Heart Rate Monitor (v0.11) when I start it.

    I just retried again to switch between 80 and 40 with logging in IDE, see attached file. According to my fitness tracker my HR was about 76

    1 Attachment

  • I have tried comparing the new algorithm with my BT belt during a few hours of hiking. There are still differences, but I think that is at least partly to be expected. It is a massive improvement over the old one which would show everything from 40 to 90 on similar levels of exertion. The y-axis does not start at 0 to show the differences a bit more clearly. So it looks worse than it is :)

    1 Attachment

    • hrm.png
  • When looking at it maybe you could also replicate my experience that the BPM is about 10 lower? Most of your graph looks like that. Maybe you could also record part when you are sitting still so that it is less random?

  • Yes, sure seems that way. This was sitting and snacking. So not completely without movement but close. Kickstarter version Bangle.

    1 Attachment

    • hrm.png
  • Hmm - that's interesting then - so it looks like maybe you have a similar issue to @fanoush with the HRM differences. @fanoush yours is one of the earlier watches too? eg not with the VC31B

    I wonder whether maybe for some reason we're getting duplicate readings - maybe the VC31 code still forwards HRM data when there is nothing. @fanoush if you look at HRM-raw can you see duplicate entries?

    I just tried with an original bangle here:

    var last = {};
    function r(h) {
      print(h.vcPPG==last.vcPPG,  h.vcPPG);
      last = h;

    And I see it returning false reliably - but if it was reporting true 1 in 10 times then I guess we might expect that would be causing a lower HRM to be reported?

    But even so I don't really see it. hrm_new gets called whenever there is a new bit if data, and it keeps track of the amount of time that has passed between each call and feeds it directly into the algorithm. It feels like the only way it could be 10% off is if the clock was running 10% fast!

  • And I see it returning false reliably

    for me there is true but very rarely. in 10 out of 2749 samples.
    mine is possibly one of the earliest ones, the one you sent me when figuring out the HW pinout

  • Attached is output of your code with getTime() added print(getTime(),h.vcPPG==last.vcPPG, h.vcPPG);
    can the timestamp tell you if it is too fast or too slow?

    1 Attachment

  • Ok, thanks - yes you'd expect a few duplicates just because there's always a chance the value really will be the same two times in a row.

    So on yours, even with the old algorithm it was still out? Because I could imagine the new algorithm might be expecting the time difference between samples to stay constant rather than varying slightly per sample, and that might throw it off - but the old one should have been fine

  • the old open source one worked fine when sitting still, mostly same value as other devices

  • I could imagine the new algorithm might be expecting the time difference between samples to stay constant rather than varying slightly per sample, and that might throw it off

    And from the log I attached you see this is the case? You see it is better on newer watches?

  • Ahh ok, so it may well be an issue with the algorithm expecting almost identical time differences - in their example the HRM is calibrated to give readings at the right rate, not the other way around.

    Looking at you file there is some variation - but worryingly I actually see some much bigger gaps every so often - like one sample is being missed which I guess is just due to a difference in frequency between the HRM and the bangle:

    0.067596435546875 !!!
    0.069732666015625 !!!

    But maybe that one long sample throws the algorithm off (when the original one would cope with it).

    You see it is better on newer watches?

    Well, when I tested the HRM measurements I got against a Bangle running old firmware, they were almost identical.

    Maybe you could try - in heartrate_vc31_binary.c - changing:

    Algo_Input(&inputData, timeDiff, hrmInfo.sportMode, 0/*surfaceRecogMode*/,0/*opticalAidMode*­/);


    if (timeDiff*2 > hrmPollInterval*3){ // > 1.5x
      Algo_Input(&inputData, timeDiff, hrmInfo.sportMode, 0/*surfaceRecogMode*/,0/*opticalAidMode*­/);
    Algo_Input(&inputData, timeDiff, hrmInfo.sportMode, 0/*surfaceRecogMode*/,0/*opticalAidMode*­/);

    So it effectively just splits the double-length gap into 2

  • FYI: just checked here with kickstarter bangle2: HR is 10bpm(+-2) below chest belt in rest
    with Firmware 17.93 (already binary blob, right?) and current stable 18.00.
    With older firmware it was pretty exact at rest. So looks like this is a reproducible general effect.
    Glad you already have an idea about the cause.

  • Maybe you could try - in heartrate_vc31_binary.c - changing

    unfortunately no change with that, still ~10BPS below
    I even added jsiConsolePrintf inside the if like

      if (timeDiff*2 > hrmPollInterval*3){ // > 1.5x
      jsiConsolePrintf("Compensating HRM timediff %d\n",timeDiff);

    and it shows approx. every second when starting the HRM monitoring app

     2v18.25 (c) 2021 G.Williams
    Compensating HRM timediff 338
    Compensating HRM timediff 136
    >Compensating HRM timediff 135
    Compensating HRM timediff 136
    Compensating HRM timediff 136
    Compensating HRM timediff 136
    Compensating HRM timediff 136
    Compensating HRM timediff 136
    Compensating HRM timediff 136
    Compensating HRM timediff 135
    Compensating HRM timediff 136
    Compensating HRM timediff 68
    Compensating HRM timediff 67
    Compensating HRM timediff 68
    Compensating HRM timediff 68
    Compensating HRM timediff 67
    Compensating HRM timediff 68
    Compensating HRM timediff 68
    Compensating HRM timediff 67
    Compensating HRM timediff 68
    Compensating HRM timediff 67
    Compensating HRM timediff 67
    Compensating HRM timediff 67
    Compensating HRM timediff 67
    Compensating HRM timediff 68
    Compensating HRM timediff 67
  • It is not so regular, I added time in ms

      jsiConsolePrintf("%f Compensating HRM timediff %d\n",jshGetMillisecondsFromTime(time),t­imeDiff);

    and it looks like this, it is a lot from the start, then once or few times per second and then there are long periods where it does not happen at all and in these HRM is wrong too

     ____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
     2v18.25 (c) 2021 G.Williams
    1686403741632.9345703125 Compensating HRM timediff 338
    1686403741769.25659179687 Compensating HRM timediff 136
    >1686403741905.57861328125 Compensating HRM timediff 136
    1686403742041.96166992187 Compensating HRM timediff 136
    1686403742178.37524414062 Compensating HRM timediff 136
    1686403742314.72778320312 Compensating HRM timediff 136
    1686403742450.86669921875 Compensating HRM timediff 136
    1686403742587.18872070312 Compensating HRM timediff 136
    1686403742723.57177734375 Compensating HRM timediff 136
    1686403742859.86328125 Compensating HRM timediff 136
    1686403742999.35913085937 Compensating HRM timediff 139
    1686403744631.28662109375 Compensating HRM timediff 68
    1686403746232.02514648437 Compensating HRM timediff 68
    1686403746470.5810546875 Compensating HRM timediff 67
    1686403748071.31958007812 Compensating HRM timediff 68
    1686403749672.21069335937 Compensating HRM timediff 68
    1686403749910.55297851562 Compensating HRM timediff 68
    1686403751511.53564453125 Compensating HRM timediff 68
    1686403751749.66430664062 Compensating HRM timediff 68
    1686403753350.12817382812 Compensating HRM timediff 68
    1686403754950.83618164062 Compensating HRM timediff 68
    1686403755189.208984375 Compensating HRM timediff 68
    1686403756789.85595703125 Compensating HRM timediff 68
    1686403758390.22827148437 Compensating HRM timediff 67
    1686403758628.72314453125 Compensating HRM timediff 68
    1686403760229.1259765625 Compensating HRM timediff 68
    1686403761829.7119140625 Compensating HRM timediff 68
    1686403762068.359375 Compensating HRM timediff 68
    1686403763668.48754882812 Compensating HRM timediff 68
    1686403765268.6767578125 Compensating HRM timediff 68
    1686403765506.98852539062 Compensating HRM timediff 67
    1686403766868.95751953125 Compensating HRM timediff 68
    1686403767107.23876953125 Compensating HRM timediff 68
    1686403768707.6416015625 Compensating HRM timediff 68
    1686403770307.58666992187 Compensating HRM timediff 67
    1686403771907.74536132812 Compensating HRM timediff 68
    1686403772146.02661132812 Compensating HRM timediff 68
    1686403773507.96508789062 Compensating HRM timediff 68
    1686403773746.12426757812 Compensating HRM timediff 68
    1686403775107.72705078125 Compensating HRM timediff 67
    1686403775346.58813476562 Compensating HRM timediff 68
    1686403776945.98388671875 Compensating HRM timediff 68
    1686403778545.8984375 Compensating HRM timediff 68
    1686403780145.90454101562 Compensating HRM timediff 68
    1686403781745.54443359375 Compensating HRM timediff 67
    1686403783345.3369140625 Compensating HRM timediff 68
    1686403784945.15991210937 Compensating HRM timediff 67
    1686403786545.07446289062 Compensating HRM timediff 68
    1686403788144.62280273437 Compensating HRM timediff 68
    1686403789744.384765625 Compensating HRM timediff 68
    1686403791344.54345703125 Compensating HRM timediff 68
    1686403799988.12866210937 Compensating HRM timediff 68
    1686403801587.5244140625 Compensating HRM timediff 68
    1686403803187.01171875 Compensating HRM timediff 68
    1686403804786.2548828125 Compensating HRM timediff 68
    1686403832786.4990234375 Compensating HRM timediff 67
    1686403834385.22338867187 Compensating HRM timediff 68
    1686403852786.9873046875 Compensating HRM timediff 68
    1686403854385.65063476562 Compensating HRM timediff 68
    1686403871185.63842773437 Compensating HRM timediff 67
    1686403884787.07885742187 Compensating HRM timediff 68
    1686403886385.10131835937 Compensating HRM timediff 67
    1686403899984.64965820312 Compensating HRM timediff 67
    1686403911985.01586914062 Compensating HRM timediff 67
    1686403923984.95483398437 Compensating HRM timediff 68
    1686403934385.28442382812 Compensating HRM timediff 67
    1686403944785.24780273437 Compensating HRM timediff 67
    Button held down - interrupting JS execution...
    Execution Interrupted
    >1686403955218.65844726562 Compensating HRM timediff 101
    1686403956034.423828125 Compensating HRM timediff 67


  • Ok, thanks for trying that out! that's a pain - I was really hopeful that might have been the problem!

    So when you did the compensation you did call Algo_input twice?

    Although if you get the problem even when the HRM readings are regular I guess that doesn't help

  • So when you did the compensation you did call Algo_input twice?

    yes, copy paste of your code, timediff divided by two and then Algo_input twice

  • anyone who does care can still build their own firmwares with the open one with relative ease.

    Is this the only thing I have to do to build with the open algorithm?:­pare/master...thyttan:Espruino:espruino-­hrm-open

    I've flashed a build with that and it seems to work on the watch at least.

    Just recently I got a chest strap and wanted to add data to­MTestHarness. But I can't seem to get HRM Accelerometer event recorder (v0.03) to work on my laptop nor phone. Nothing happens on the bangle or in the browser when I click 'start'.

  • Yes, that diff looks exactly what you need!

    So you're seeing this issue with the offset HRM value as well?

    I'm not really sure about the event recorder - maybe @halemmerich has some ideas?

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Bangle.js 2 Cutting Edge - new heart rate algorithm

Posted by Avatar for Gordon @Gordon