Is it possible to set more than one service ?

Posted on
  • Was thinking of having more than one service set with NRF.setService.

    NRF.setServices({
      0x1809 : { // Health Thermometer
        0x2A6E: {  // Temperature
          readable: true,
          value : [E.getTemperature().toFixed(2)]
      }},
      0x180F : { // Battery Service
        0x2A19: {  // Battery Level
          readable: true,
          value : [Puck.getBatteryPercentage()]
    }}});
    
    

    eBeacon or Bluefruit only display the service 0x1809

    Any hints ?

  • NRF.setServices({
      0x1809 : { // Health Thermometer
        0x2A6E: {  // Temperature
          readable: true,
          value : [1]
      }},
      0x180F : { // Battery Service
        0x2A19: {  // Battery Level
          readable: true,
          value : [1]
    }}});
    

    The above works for me - I think it's because you were trying to send a string inside an array as the temperature.

    It should really have errored though - I'm not sure why it didn't

  • only see 0x1809, no 0x180F , which application did you use to check this ?

    Can I use a second puck to check this ?

    • connect
    • query services
    • disconnect
  • Found it all in the documents ....

    >NRF.findDevices(function(devices) {
    :  console.log(devices);
    :}, 1000);
    =undefined
    /*
    [
      BluetoothDevice {
        "id": "f4:38:88:d0:bc:5c random",
        "rssi": -55,
        "services": [  ],
        "data": new ArrayBuffer([2, 1, 5, 13, 9, 80, 117, 99, 107, 46, 106, 115, 32, 98, 99, 53, 99]),
        "name": "Puck.js bc5c"
       }
     ]
    */
    

    looks like the eBeacon and BlueFruit have problems with reading ...

  • I use the nRF connect app - it's been pretty reliable for me.

    NRF.findDevices won't query the services you set with setServices - it's only the publically advertised services (not the ones you get when you connect). What you want is http://www.espruino.com/Reference#l_BluetoothRemoteGATTServer_getPrimaryServices

  • During testing with iPhone and iPad I realized that switching BLE off and on again also helped.

    YES nRF connect works for me too, will stick to that tool !


    1 Attachment

    • FullSizeRender.jpg
  • I imagine some other apps will just cache the BLE services and don't cope well with them changing.

  • Actually I believe that iOS is doing the caching here.
    So turning BLE off/on (or rebooting the whole device if things get worse) is the only thing you can do.
    I have done a BLE app a few years ago and it's astounding what's happening under the hood in iOS.
    I assume most if it is done to preserve battery...

  • Nice idea! It's working for me but i get wrong temperature value (143.85°C instead of 18°C from the console).
    Any suggestion?

  • @Fabio978 I get something similar. I believe it's because toFixed() returns a string and the temperature value is supposed to be 16 bit floating point number. The characters in the string that end up being read as a sint16 and converted to floating point for display just happen to represent 143.85. I tried just rounding the temperature down to an integer but that doesn't work at all. The battery works fine because it's being read as a byte, with a valid range of 0 to 100.

  • I think the problem is that temperature must be specified in 11073-20601 16-bit FLOAT-Type format, but i couldn't find how to do that.

  • I was able to figure this out eventually. Can't believe how much google searching and code searching. Bluetooth specs are specific but not much help to the uninformed.

    Bottom line is that the value sent to the characteristic needs to be a signed 16 bit integer supplied as a mantissa with an 'implied' decimal exponent of -2. All along I was trying to fit a float16 or a half-precision float or a 4 bit exponent float variant into a sint16 and it just doesn't work. Duh.

    This works:

    function tempTo2a6e(temp) {
      var f = parseFloat(temp.toFixed(2)) * 100;
      if (tempTo2a6e.buf === undefined) tempTo2a6e.buf = new Uint8Array(2);
      tempTo2a6e.buf[0] = f & 0xff;
      tempTo2a6e.buf[1] = f >> 8 & 0xff;
      return tempTo2a6e.buf;
    }
    NRF.setServices({
      0x1809 : {
        0x2a6e : {
          readable : true, value : tempTo2a6e(E.getTemperature())
        }
      }
    });
    

    Be sure to disconnect from the puck and then connect using nRF Connect or whatever and look for the Health Thermometer service. The temperature now reads correctly as 18.75'C. If reading the value using another puck you just need to divide by 100 (or multiply by 10^-2).

    Of course this simple example doesn't update the temperature value over time. NRF.updateServices in some sort of a timed loop would do that.

    Writing a string to the characteristic does work (doesn't fail) and if you read back the data and interpet it as a string that will work too. But the spec for the characteristic is sint16. Tools like nRF Connect expect to see an sint16 vs an array of chacters which is why it appeared to display an incorrect value.

  • Great find @dklinkman! I will try this tonight!

  • @dklinkman your code works very well! Thank you!

  • @Gordon

    are you planing functions to convert to the the different value formats for Characteristics ?

  • Afraid not - you can do a lot of value conversion using ArrayBuffers though.

    a = new Uint8Array(2);
    b = new Int16Array(a.buffer);
    b[0] = 12345;
    console.log(a);
    
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Is it possible to set more than one service ?

Posted by Avatar for MaBe @MaBe

Actions