• Bangle.setOptions({
      hrmGreenAdjust: false

    Adjust 15, to change intensity.

    Regarding the data, I agree and have same results. I assume the old way of read pin directly with analogRead produced better results for raw ppg manipulation. You would have to uncomment:

    # Standard open-source heart rate algorithm:
    # 'SOURCES += libs/misc/heartrate.c',

    Line 74 of boards/BANGLEJS2.py, and recomment the current one.

    Then it doesn't read from Register 0x80 via I2C (HEARTRATE_PIN_SDA) (hrm_vc31.c @vc31b_readfifo) , but instead HEARTRATE_PIN_ANALOG 29 (hrm_analog.c @hrm_sensor_on).

    That is as much as I know atm. However the actual readings of BPM are considered quite good even though its routed through the binary blob, but not sure why it forces the raw values to be bad.

    EDIT: HEARTRATE_PIN_ANALOG 29 is for Bangle.js 1 only!!

  • Thanks - I hadn't realised I could alter the LED intensity without rebuilding the firmware - I'll try that first, then have a look at using the analogue output as you suggest.

  • This code can be used to view the data, might be useful. It is using analogRead from old method of reading pin directly instead of talk through i2c. Could be interesting to compare outputs. Pass hrm.raw into instead of val to c.onHRM to see difference. (You have to adjust graph constraints too, hrm.raw are huge numbers between 0 - 10000 ).
    EDIT: It is using wrong pin (29), its not hrm data

    var c = E.compiledC(`
      // void onHRM(int,int,int)
      const unsigned char windowSize = 25;
      typedef struct inputs {
        short * vals;
      } inputs_t;
      void onHRM(unsigned int &tick,int val,inputs_t *argss){
        argss->vals[tick % windowSize] = val;
    let newUint32FlatArray = len => {
      let fs = E.toFlatString((new Uint32Array(len)).buffer);
      if ( !fs ) throw new Error("Required to be a flatstring");
      let obj = new Uint32Array(E.toArrayBuffer(fs));
      return {"obj" : obj, "ref" : E.getAddressOf(obj,true) };
    let newInt16FlatArray = len => {
      let fs = E.toFlatString((new Int16Array(len)).buffer);
      if ( !fs ) throw new Error("Required to be a flatstring");
      let obj = new Int16Array(E.toArrayBuffer(fs));
      return {"obj" :  obj, "ref" : E.getAddressOf(obj,true) };
      hrmGreenAdjust: false
    let windowSize = 25;
    var vals = newInt16FlatArray(windowSize);
    var inputs = newUint32FlatArray(6);
    inputs.obj[0] = vals.ref;
    let tickFlat = newUint32FlatArray(1);
    tickFlat.obj[0] = 0;
    let tickCountOld = 0;
    //This is called by idleLoop. Not an interrupt.
    Bangle.on('HRM-raw', function(hrm) {
      let val = Math.round(analogRead(29)* 16383);
    //25Hz, Bangle.js 2
    //50Hz, Bangle.js 1
    setInterval(function hmm(){
        let tick = tickFlat.obj[0];
        tickCountOld = tick;
        let nonCircVals = [];
        //circular to non-circular.
        for ( let i = 0; i < windowSize; i++ ) {
          nonCircVals.push(vals.obj[(tick + 1 + i) % windowSize]);
        require("graph").drawLine(g, nonCircVals,{axes:false,miny:-50,maxy:50­,gridy:25});