Trouble with: setAdvertising heart_rate

Posted on
  • I'm trying to make a little game with the puck, and the first part of my plan is to advertise the puck as a heart_rate monitor (service: 0x180D).

    When I check the puck after a deploy with the Android nRF Connect app, it doesn't list heart_rate as a service.

    When I compare to my Pebble, I can see it explicitly listed under flags & services.

    The odd thing is I can advertise as a temperature gauge - but it's entirely possible I'm making mistakes.

    This is my code:

    NRF.setAdvertising({
      0x180D: [ 99 ],
      0x1809: [ 30 ],
    },
    {
      name: "Puck <3", // The name of the device
      showName: true, // include full name, or nothing
      discoverable: true, // general discoverable, or limited - default is limited
      interval: 600, // Advertising interval in msec, between 20 and 10000
    });
    

    Temperate correctly shows as 30c (in nRF connect), but the heart_rate is missing.

    Any ideas/help?

    Thanks!

  • I have been trying to advertise as various things from the Puck, but the only things I can get to show up as being described properly in nRF are the examples, Temperature and Battery Life. I can't get blood pressure or environmental sensing to show up. I think you have to provide a byte array as data which has some required parameters for some services, but I don't know how to tell what data it needs or how to give it to it! The eddystone example works too, and that has a load of bytes in an array.

    NRF.setAdvertising([0x03,  // Length of Service List
      0x03,  // Param: Service List
      0xAA, 0xFE,  // Eddystone ID
      0x13,  // Length of Service Data
      0x16,  // Service Data
      0xAA, 0xFE, // Eddystone ID
      0x10,  // Frame type: URL
      0xF8, // Power
      0x03, // https://
      'g','o','o','.','g','l','/','B','3','J','0','O','c'],
        {interval:100});
    

    I don't really understand the gatt spec website either.

  • If I just do:

    NRF.setAdvertising({
      0x180D: [ 99 ],
      0x1809: [ 30 ],
    });
    

    Then when I disconnect and re-scan in nRF connect I see:

    Service Data: UUID: 0x180D Data: 0x63
    Temperature: 30 C
    

    And if I do:

    NRF.setAdvertising({
      0x180F : [ 95 ],
      0x1809: [ 30 ],
    });
    

    I see:

    Battery Level: 95%
    Temperature: 30 C
    

    So Puck.js seems to be advertising everything just fine. Is it possible that the heart rate service isn't meant to be an advertisement, but is meant to be a service that you add with NRF.setServices?

    • Services: only available when you connect
    • Advertisements: broadcast to the world when you're not connected

    It seems more likely it's a service, since you maybe don't want to broadcast your heartrate to anyone that's listening?

    You can always click RAW in nRF Connect and can see the complete advertising data if you want to compare your Pebble's advertising data with Puck.js.

    @gomako: I'm pretty sure that blood pressure will be a service, not advertising.

    In setAdvertising if you supply a raw array, it just advertises that data - for the format you need to delve into the Bluetooth spec. Normally you'd supply an object though as I have above, and Puck.js creates that array for you automatically.

  • I've been reading through an older thread on BT on this forum and I've come to the same thought, that heart_rate should be a service not an advertisement.

    I'm going to give that a try now. I took that route to start with, but didn't have much luck…that said, it was 18 hours ago, so maybe my brain has soaked up more of the GATT spec!

    Will report back.

  • Aside, @Gordon do you have any tips on debugging? Because when I apply the new service, it blows away the Nordic UART so I can't redeploy without pulling the battery and resetting.

    If I use setService and add the option uart: true should I still be able to write?

  • Quick update:

    1. Using setService doesn't blow away UART (I think this puck was just taking some time to pick up).
    2. I've got a heart_rate advertising, but…it's not quite there.

    Note that the value is totally wrong. I'm also trying to connect from a webpage, which doesn't discover that this device has heart_rate (I think it's filtering on the service), but baby steps!

  • You'll need to use the format of data that's specified here: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml

    Looks a bit cryptic, so it might be easier to copy some data values from someone else's example code?

  • @Gordon yeah, I've been reading through the specs (and parsing existing data).

    I know (hmm "know", not sure that's accurate!) that sending a value of 0x06, 0x2c should send the right flags to say the heart_rate monitor is connected and the heart rate is 44bpm.

    I was thinking something like this:

    NRF.setServices({
      0x180D: { // heart_rate
        0x2A37: { // heart_rate_measurement
          broadcast: true,
          readable: true,
          notify: true,
          indicate: true,
          value : 0x2C06,
        }
      }
    })
    

    Which does kinda work, but I also believe the device needs to advertise it has heart_rate services - which this doesn't seem to be doing, otherwise filtering for the service doesn't seem to work (or certainly the demo page I'm working from doesn't see it)

  • Oh, also note that I've tried sending the following - none really work:

    • value: 0x2C06
    • value: [0x06, 0x2C]
    • value: 0x062C

    …which does suggest I'm basically making it up as I go along!

  • @Gordon I think I've found an adafruit example that I might be able to use as documentation, except I think I need to know where the bit flags for the setServices properties are set.

    For example, if I have broadcast: true, notify: true, where are those flags in C? What's the value of the GATT property? (I think…)

    I've been poking around the C files, but I'm not familiar with the code at all.

  • When you send value : 0x2C06, Espruino doesn't know it's supposed to be sending 16 bits - it just sends it as an 8 bit value.

    I think you probably want: value : [0x2C, 0x06] and that'll fix it.

    Those flags are for the characteristic itself (rather than the data) - I really don't think you need to delve that deeply - where's the adafruit example code?

  • @Gordon Ah right, yeah I see the distinction between advertising and service now. Will follow this thread with interest, as I think this has such a lot of potential. If you do manage to decode the GATT specs @remy it would be good to hear about it!

  • Looking at this:

    AT+GATTADDCHAR=UUID=0x2A37, PROPERTIES=0x10, MIN_LEN=2, MAX_LEN=3, VALUE=00-40
    

    I'm not 100% sure how to represent the value 00-40 in a way that the puck will understand. Would I give it hex? Is this something else?

    I'm guessing that properties is one of the flags like readable, notify, etc - but not sure which. Is this the same as [0x00, 0x40]? Any ideas?

  • I'm guessing that properties is one of the flags like readable, notify, etc - but not sure which.

    I'm sure that'd be in Adafruit's docs somewhere? It's not something you should really try and figure out from Puck.js's source.

    And yes, 00-40 sure sounds like it should be [0x00, 0x40] to me.

    That also fits with the spec I posted earlier. It'd mean:

    • Heart Rate Value Format is set to UINT8. Units: beats per minute (bpm)
    • Sensor Contact feature is not supported in the current connection
    • Energy Expended field is not present
    • RR-Interval values are not present.

    And 0x40 is 64. So a heart rate of 64 BPM

  • Quick update, I'm able to read heart rate of 40bpm (with contact detected) and body sensor location: wrist on nRF.

    But it's not advertising (yet).

    Here's the code (because I'm not tracking changes!):

    NRF.setServices({
      0x180D: { // heart_rate
        0x2A37: { // heart_rate_measurement
          notify: true,
          readable: true,
          broadcast: true,
          value : [0x06, 0x40],
        },
        0x2A38: {
          readable: true,
          value: [0x02]
        }
      }
    });
    
    • 0x06 in the heart_rate_measurement is the flags and says that it's got contact.
    • 0x40 is the heart rate
    • 0x02 is the sensor location value
  • Quick update, I've got the heart_rate service advertising and "working". Note that I lifted the setAdvertising values from here and just copied the GAPSETADVDATA value into a hex array, but I really don't understand why it works.

    var hr = 65;
    
    function update() {
      hr++;
      if (hr > 120) {
        hr = 65;
      }
      
      NRF.updateServices({
        0x180D: { // heart_rate
          0x2A37: { // heart_rate_measurement
            notify: true,
            value : [0x06, hr],
          }
        }
      });
      
      setTimeout(update, 1000);
    }
    
    NRF.setServices({
      0x180D: { // heart_rate
        0x2A37: { // heart_rate_measurement
          notify: true,
          value : [0x06, hr],
        },
        0x2A38: {
          readable: true,
          value: [0x02]
        }
      }
    });
    
    
    NRF.setAdvertising([
      0x02,
      0x01,
      0x06,
      0x05,
      0x02,
      0x0d,
      0x18,
      0x0a,
      0x18
    ], {
      name: "puck heart", // this doesn't stick at all, no idea why
      showName: true,
      discoverable: true,
      interval: 600
    });
    
    // if I update too early, it seems like GATT is still trying to do the previous updates
    setTimeout(update, 2000);
    
  • Ahh, right. So you have 3 specific things:

    • Advertising data - data that's broadcast to everyone
    • Advertised services - broadcasting 'I can do X', but not containing data
    • Actual services - data once connected

    Unfortunately Puck.js doesn't let you define advertised services right now - but you managed to find a way around it by explicitly setting the raw advertising data, so it looks like you're sorted :)

    I've filed a bug and I'll try and add the ability to set advertised services at some point in the future.

  • One really weird thing I do see, is if I redeploy the code, the service no longer advertises correctly (it's like it completely resets).

    I'm having to do a hard reset on the puck to get back to advertising.

  • You'd definitely have to disconnect and reconnect for it to take effect (since Espruino would think the services have changed and the only way to change the services on nRF52 is for Espruino to totally reset the Bluetooth stack, which it can only do when nothing is connected).

    It might be that the automatic BLE reset means that the data set in setAdvertising gets lost.

    Can you try sticking NRF.setAdvertising inside update() for now? If so I can see if I can change the software a bit so it remembers.

  • Yeah, putting the setAdvertising in the update did seem to do the trick.

    Still a bit weird that the name isn't set, though I can see it on the nRF Connect app if I explicitly read the value, but my browser (via webBT) is still reading the old "Pluck 1234" value. Not a biggie though.

    Now seeing if I can add some more services to this poor little puck!

  • Ok, thanks - I'll add that to my To Do list then.

    You're not seeing the name because you're specifying raw BLE advertising data which doesn't have the name specified in it. If you added the right sequence of bytes to specify a name then you'd be able to do that as well.

    My guess is anything that's still showing a name just has it cached.

  • Ok, this is now done and will be in 1v92. You can do:

    NRF.setServices({
      0x180D: { // heart_rate
        0x2A37: { // heart_rate_measurement
          notify: true,
          value : [0x06, hr],
        },
        0x2A38: {
          readable: true,
          value: [0x02]
        }
      }
    }, {advertise:[0x180D]});
    

    to start advertising the heart rate service.

  • very cool, thanks! Looking forward to 1v92 for my puck :)

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

Trouble with: setAdvertising heart_rate

Posted by Avatar for remy @remy

Actions