Filtering getCharacteristics()

Posted on
  • So I'm trying to interface a puck.js with an existing BLE product, and it has a service with two characteristics with the same UUID, but different properties and uses. One is read/notify and is output from the device, and the other is "write without response", and is input to the device. There are also another two characteristics in this service that I'm not interested in, but also have a read/notify and "write without response" pair.

    Because they have the same UUID, I believe it's not possible(Or at least reliable) for me to use getCharacteristic() to get a handle for both of them, and it becomes necessary to use getCharacteristics() and iterate through them to find the correct ones.

    However, it appears that getCharacteristics() in Espruino does not take a UUID argument as a filter, so I end up also getting the two characteristics I'm not interested in in the array of results.

    In Web Bluetooth, it looks like BluetoothRemoteGATTCharacteristic has a UUID property, but this property does not appear to exist and/or work in Espruino. Attempting to print the UUIDs of the characteristics always results in the value 0x0000.

    So how do I go about getting handles for the correct characteristics?

    Attached is a screenshot of the service/characteristics, lightly anonymized.


    1 Attachment

    • Screenshot_20210220-193236-anonymized.png
  • Sun 2021.02.21

    'so I end up also getting the two characteristics I'm not interested in in the array of results.'

    Has searching any of the existing forum threads helped in learning what others have done?

    Google: getCharacteristic ble site:forum.espruino.com

    This thread has a few good examples:

    http://forum.espruino.com/conversations/­304661/

    Edit: fanoush just posted this reference table:

    http://forum.espruino.com/comments/15836­108/

  • This is a specific case which does not seem to be covered by other threads. I have two characteristics with the same UUID, and I need to get handles for both of them. getCharacteristic does not allow me to do this. getCharacteristics does not filter by UUID, so I get unrelated characteristics from that service as well. The characteristic does not have a working UUID property, so I cannot filter them myself.

  • var my_device;
    var cmd_rx_characteristic;
    var cmd_tx_characteristic;
    
    function handleCmdRx(event) {
    
    }
    
    function onDisconnect(event) {
        const device = event.target;
        if (cmd_rx_characteristic) {
            cmd_rx_characteristic.stopNotifications(­)
            .then(_ => {
              log('Notifications stopped');
              cmd_rx_characteristic.removeEventListene­r('characteristicvaluechanged',
                handleCmdRx);
            })
            .catch(error => {
              log('Argh! ' + error);
            });
          }
    }
    
    function getSupportedProperties(characteristic) {
        let supportedProperties = [];
        for (const p in characteristic.properties) {
          if (characteristic.properties[p] === true) {
            supportedProperties.push(p.toUpperCase()­);
          }
        }
        return '[' + supportedProperties.join(', ') + ']';
      }
    
    
    //Find the device and rx/tx characterstics...
    
    NRF.requestDevice({ filters: [{ services: ['DE3A0001-7100-57EF-9190-F1BE84232730']­ }] })
    .then(device => {
        my_device = device;
        device.on('gattserverdisconnected', onDisconnect);
        return device.gatt.connect();
    })
    .then(server => server.getPrimaryService('DE3A0001-7100-­57EF-9190-F1BE84232730'))
    .then(service => service.getCharacteristics('803C3B1F-D30­0-1120-0530-33A62B7838C9'))
    .then(characteristics => {
        console.log(characteristics.length + ' characteristics');
        characteristics.forEach(characteristic => {
            console.log(getSupportedProperties(chara­cteristic));
            console.log(characteristic.uuid);
            if(characteristic.uuid == "803C3B1F-D300-1120-0530-33A62B7838C9") {
                if(characteristic.properties.read) {
                    console.log('Found cmd_rx characteristic');
                    cmd_rx_characteristic = characteristic;
                    characteristic.startNotifications()
                    .then(characteristic => {
                        characteristic.on('characteristicvaluech­anged',
                            handleCmdRx);
                        console.log('Notifications started.');
                    });
                } else if(characteristic.properties.writeWithou­tResponse) {
                    console.log('Found cmd_tx characteristic');
                    cmd_tx_characteristic = characteristic;
                }
            }
        });
    });
    
    

    Here's the code I'm trying to use. Line 44 should filter it down to two characteristics, but it returns 4. Line 49 tries to print the UUID of the characteristic, but always prints 0x0000.

  • Sun 2021.02.21

    @user125533, to speed our analysis and to assist us in a visualization of what that code block outputs, would you mind posting that content please.

  • >
     ____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v08 (c) 2019 G.Williams
    >
    4 characteristics
    [READ, NOTIFY]
    0x0000[vendor]
    [WRITEWITHOUTRESPONSE]
    0x0000[vendor]
    [READ, NOTIFY]
    0x0000[vendor]
    [WRITEWITHOUTRESPONSE]
    0x0000[vendor]
    > 
    
  • For further reference, here is the results of the equivalent code under Chrome's Web Bluetooth.

    2 characteristics
    [READ, NOTIFY]
    803c3b1f-d300-1120-0530-33a62b7838c9
    Found cmd_rx characteristic
    Notifications started.
    [WRITEWITHOUTRESPONSE]
    803c3b1f-d300-1120-0530-33a62b7838c9
    Found cmd_tx characteristic
    
    
  • Wow, having identical UUIDs on the same service is something I haven't come across at all! The use of differing 128 bit UUIDs is a bit dodgy too.

    I just tried some simple code here and the UUID gets reported properly in my case:

    var gatt;
    NRF.connect("f0:c6:4a:e8:d2:50 random").then(function(g) {
      gatt = g;
      return gatt.getPrimaryService("6e400001-b5a3-f3­93-e0a9-e50e24dcca9e");
    }).then(function(service) {
      return service.getCharacteristics();
    }).then(function(c) {
      chars=c;
      print(JSON.stringify(c,null,2));
    }).then(function() {
      console.log("Done!");
    });
    /*[
      {
        "uuid": "6e400003-b5a3-f393-e0a9-e50e24dcca9e",
        "handle_value": 13,
        "handle_decl": 12,
        "properties": {
          "broadcast": false,
          "read": false,
          "writeWithoutResponse": false,
          "write": false,
          "notify": true,
          "indicate": false,
          "authenticatedSignedWrites": false
         }
       },
      {
        "uuid": "6e400002-b5a3-f393-e0a9-e50e24dcca9e",
        "handle_value": 16,
        "handle_decl": 15,
        "properties": {
          "broadcast": false,
          "read": false,
          "writeWithoutResponse": true,
          "write": true,
          "notify": false,
          "indicate": false,
          "authenticatedSignedWrites": false
         }
       }
     ]*/
    

    So I wonder whether you're hitting an issue with how 128 bit UUIDs are handled. Basically I think they're transmitted as 16 bit UUIDs plus a '128 bit ID index'. Espruino doesn't explicitly request the full 128 bit UUIDs (I'm not even sure how you do this) and expects you to supply them.

    You may find that just poking Espruino with the ID you're interested in is enough:

    .then(server => server.getPrimaryService('DE3A0001-7100-­57EF-9190-F1BE84232730'))
    .then(s => {service=s;return service.getCharacteristic('803C3B1F-D300­-1120-0530-33A62B7838C9')}) // <--------
    .then(service => service.getCharacteristics())
    

    So you request the characteristic, but don't use it. You might now see UUIDs reported properly.

    But as you say, Espruino doesn't support filtering by UUID with getCharacteristics() though.

    Honestly, I'd have thought that since the order of characteristics won't change, you could probably just use a simple getCharacteristics() and then reference the characteristics by their position in the returned array

  • Had to tweak it a bit to get it to work correctly(Needed to save the service, and make the next .then use the saved service instead of the argument), but requesting with getCharacteristic() and throwing it away /does/ get the UUID field to populate in getCharacteristics(). Fun stuff. Thanks for the help!

    var device_service;
    
    .then(server => server.getPrimaryService('DE3A0001-7100-­57EF-9190-F1BE84232730'))
    .then(s => {
      device_service=s;
      return device_service.getCharacteristic('803C3B­1F-D300-1120-0530-33A62B7838C9');
    })
    .then(_ => device_service.getCharacteristics('803C3­B1F-D300-1120-0530-33A62B7838C9'))
    

    Now on to figuring out how to actually write commands to it, haha. :)

  • Mon 2021.02.22

    'Now on to figuring out how to actually write commands to it, haha. :)'

    Not sure if you are being facetious here as a fair amount of coding has been done, so the assumption is that you are well on your way, but for those needing that detail:

    https://www.espruino.com/BLE+Communicati­ons

    https://www.espruino.com/About+Bluetooth­+LE

    http://www.espruino.com/Reference#l_NRF_­setAdvertising

    http://www.espruino.com/Reference#l_Blue­toothRemoteGATTCharacteristic_writeValue­

  • Sorry, yes, I was joking. Thank you for trying to be helpful, though!

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

Filtering getCharacteristics()

Posted by Avatar for user125533 @user125533

Actions