GATT connection timeout?

Posted on
Page
of 2
/ 2
Next
  • Hi!
    I am playing with GATT with Puck.js being a central device :

    var serviceUUID="2fc3af42-7a4a-1431-d1f1-fs5­51e3bf022";
    var characteristicUUID="32160fb9-5337-4e71-b­0f8-ff412e3ae078";
    var MessageToSend=[0x00,0x01,0x03];
    
    NRF.requestDevice({ filters: [{ name: Devname }] }).then(function(device) {
          return device.gatt.connect();
        }).then(function(g) {
          gatt = g;
          console.log(gatt);
          console.log(gatt);
          console.log(gatt);
          console.log(gatt);
          return gatt.getPrimaryService(serviceUUID);
        }).then(function(service) {
          return service.getCharacteristic(characteristic­UUID);
        }).then(function(characteristic) {
          return characteristic.writeValue(MessageToSend)­;
        }).then(function() {
          gatt.disconnect();
          console.log("Done!");
        });
    
    

    and in the console I get :

    =undefined
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -57,
        "data": new Uint8Array([2, 1, 26, 10, 255, 76, 0, 16, 5, 1, 16, 139, 101, 67]).buffer,
        "name": "AABBCCDD",
        "gatt":  ...
       },
      "connected": true }
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -57,
        "data": new Uint8Array([2, 1, 26, 10, 255, 76, 0, 16, 5, 1, 16, 139, 101, 67]).buffer,
        "name": "AABBCCDD",
        "gatt":  ...
       },
      "connected": true }
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -57,
        "data": new Uint8Array([2, 1, 26, 10, 255, 76, 0, 16, 5, 1, 16, 139, 101, 67]).buffer,
        "name": "AABBCCDD",
        "gatt":  ...
       },
      "connected": false }
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -57,
        "data": new Uint8Array([2, 1, 26, 10, 255, 76, 0, 16, 5, 1, 16, 139, 101, 67]).buffer,
        "name": "AABBCCDD",
        "gatt":  ...
       },
      "connected": false }
    Uncaught Error: Unhandled promise rejection: Not connected
    > 
    

    having a look at the BluetoothRemoteGATTServer .connected state, is shows true, true,false,false. Why such a behaviour? is there really a timeout anywhere?

  • Does it say timeout anywhere?

    What peripheral are you trying to connect to? I think what's happening most likely is the peripheral might be bonded to some other device already? It connects, realises that it doesn't have any shared keys with Puck.js during the negotiation procedure, and then disconnects immediately.

    It all happens in the background using interrupts, so it's not a big surprise that the state suddenly changes.

    Does this work with a different peripheral device?

  • No it doesn't say timeout anywhere, it is just a potential cause that I suspected.
    The peripheral is an actuator. Basically I have to send 3 bytes to a specific characteristic in order to control the actuator.
    I try to bond it with this method

    function Bond(){
      NRF.requestDevice({ filters: [{ name: Devname }] }).then(function(device) {
        console.log("found device");
        return device.gatt.connect();
      }).then(function(g) {
        gatt = g;
        console.log("connected");
        return gatt.startBonding();
      }).then(function() {
        console.log("bonded", gatt.getSecurityStatus());
        gatt.disconnect();
      }).catch(function(e) {
        console.log("ERROR",e);
      });
    }
    

    This process seems to be OK. It returns

    bonded { "connected": true, "encrypted": false, "mitm_protected": false, "bonded": true }
    

    Then I try to write to the specific characteristic of the specific service and again, I got error messages like following:

    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "gatt":  ...
       },
      "connected": true }
    Uncaught Error: Unhandled promise rejection: Disconnected
    

    I don't have any other BTLE device here to test. Even my phone doesn't seem to accept being a GATT server with NRFConnect....

  • That's a strange one - can you connect to the actuator with your phone, and control it from that?

    It's possible there's something more subtle going on (like the actuator checks whether it has an encrypted connection or not).

  • Yes I am able to connect to the actuator with my phone, using NRFConnect. I don't know about security. At least, my phone did not need anything else before interracting with the actuator

  • At least, my phone did not need anything else before interracting with the actuator

    So you managed that fine without even bonding?

    Please can you post up the complete code you used to try and change the actuator's value with? I might be able to try it with a fake device here.

  • Here it is:
    Please note DevName,serviceUUID,characteristicUUID and MessageToSend have been replaced in the code below because of confidentiallity agreement.

    var devices;
    var gatt;
    var DevName="MyActuator";
    
    var serviceUUID="2fc3af42-7a4a-1431-d1f1-fs5­51e3bf022";
    var characteristicUUID="32160fb9-5337-4e71-b­0f8-ff412e3ae078";
    var MessageToSend=[0x00,0x01,0x03];
    
    
    function scan(){
      NRF.findDevices(function(d) {
        devices = d;
        console.log(devices);
      }, 5000);
    }
    
    function SendCommandToActuator() {
      NRF.requestDevice({ filters: [{ name: DevName }] }).then(function(device) {
        return device.gatt.connect();
      }).then(function(g) {
        gatt = g;
        console.log(gatt);
        return gatt.getPrimaryService(serviceUUID);
      }).then(function(service) {
        return service.getCharacteristic(characteristic­UUID);
      }).then(function(characteristic) {
        return characteristic.writeValue(MessageToSend)­;
      }).then(function() {
        gatt.disconnect();
        console.log("Done!");
      });
    }
    
    function Bond(){
      NRF.requestDevice({ filters: [{ name: DevName }] }).then(function(device) {
        console.log("found device");
        return device.gatt.connect();
      }).then(function(g) {
        gatt = g;
        console.log("connected");
        return gatt.startBonding();
      }).then(function() {
        console.log("bonded", gatt.getSecurityStatus());
        gatt.disconnect();
      }).catch(function(e) {
        console.log("ERROR",e);
      });
    }
    

    When I load this script and I call SendCommandToActuator(), I get this log:

    	
    >
     _____                 _
    |   __|___ ___ ___ _ _|_|___ ___
    |   __|_ -| . |  _| | | |   | . |
    |_____|___|  _|_| |___|_|_|_|___|
              |_| http://espruino.com
     1v94 Copyright 2016 G.Williams
    >
    =undefined
    >SendCommandToActuator()
    =undefined
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -61,
        "services": [  ],
        "data": new Uint8Array([2, 1, 4, 12, 9, 79, 75, 73, 78, 45, 54, 50, 54, 48, 49, 57]).buffer,
        "name": "MyActuator",
        "gatt":  ...
       },
      "connected": true }
    Uncaught Error: Unhandled promise rejection: Disconnected
    
    
  • That all looks fine... I just tried it between two Pucks using their UUIDs:

    var DevName="Puck.js b0ca";
    var serviceUUID="6e400001-b5a3-f393-e0a9-e50­e24dcca9e";
    var characteristicUUID="6e400002-b5a3-f393-e­0a9-e50e24dcca9e";
    var MessageToSend="LED1.set()\n";
    
    function SendCommandToActuator() {
      NRF.requestDevice({ filters: [{ name: DevName }] }).then(function(device) {
        return device.gatt.connect();
      }).then(function(g) {
        gatt = g;
        console.log(gatt);
        return gatt.getPrimaryService(serviceUUID);
      }).then(function(service) {
        return service.getCharacteristic(characteristic­UUID);
      }).then(function(characteristic) {
        return characteristic.writeValue(MessageToSend)­;
      }).then(function() {
        gatt.disconnect();
        console.log("Done!");
      });
    }
    

    ... and it works fine, even with the Bonding.

    Do you know where the error occurs? At getPrimaryService? I guess you could try delaying the call to getPrimaryService and seeing if that helps?

    Also perhaps you could try NRF.on('disconnect', function(reason) { print("Disconnected",reason) });

    It might help to understand why the connection broke down.

    Otherwise I'm not sure what to suggest - if somehow I can get one of the magic devices here then I might be able to figure out why it's not working.

    Otherwise if you have an nRF51DK then you can use Nordic's tools to sniff all the BLE packets - which might help to figure out why things aren't working.

  • I will probably try to sniff traffic but I need to order this DK before trying.

    I just added NRF.on('disconnect', function(reason) { print("Disconnected",reason);});
    and guess what? never called... However the gatt.connectedis set tofalse after having called SendCommandToActuator...

  • Am I misunderstanding anything with gatt structure? I ever thought that the gatt is pretty much like a socket (network), it represent an active connection. If this is true, the value gatt.connected should represent the status of the connection in such a way that if a disconnection event is seen, the gatt.connected should be set to false?!?

  • the value gatt.connected should represent the status of the connection in such a way that if a disconnection event is seen, the gatt.connectedshould be set to false

    Yes - however the disconnected event executes in the main thread, while connected is set to false in the Interrupt, so the two can be out of sync.

    But actually the problem in this case is my advice. What you actually want is:

      gatt.device.on('gattserverdisconnected',­ function(reason) {
        console.log("Disconnected ",reason);
      });
    

    The event you hooked into is for when the PC disconnects from the Puck, not when the Puck disconnects from some other device

  • I replaced the event callback and I just get Disconnected 62 which seems to mean "BLE_HCI_CONN_FAILED_TO_BE_ESTABLISHED 0x3E". This explains why the gatt is connected only for a very short time.
    Any hint on the reason for a connection that cannot be established?

  • This is the first time I've come across that - I'd imagine that the actuator you're trying to connect to maybe has some kind of whitelist of devices that it allows to connect - or maybe it's to do with the bonding somehow.

    The strange thing is it hangs on long enough to be able to complete the bonding...

    Perhaps you could try some of the stuff I mentioned previously:

    • Finding which bit of the promise actually throws the error
    • Put a delay between the connection and getPrimaryService - it's possible that the connection event gets fired too soon, and the act of trying to get the service messes up the rest of the connection procedure
  • Put a delay between the connection and getPrimaryService

    I am sorry for asking such a thing, but.... Could you please indicate how to add a delay within a Promise?

  • No problem, I was trying to avoid writing code because I'd have to look it up too :)

      NRF.requestDevice({ filters: [{ name: DevName }] }).then(function(device) {
        return device.gatt.connect();
      }).then(function(g) {
        gatt = g;
        console.log(gatt);
        return new Promise(function(resolve) { 
           setTimeout(resolve, 1000);
        });
      }).then(function() {
        return gatt.getPrimaryService(serviceUUID);
      }).then(function(service) {
    
  • Well, nice try but no difference, gatt is disconnected after the 1000ms delay.

    function Connect(dev){
      NRF.requestDevice({ filters: [{ name: Devname }] }).then(function(device) {
        return device.gatt.connect();
      }).then(function(g) {
        gatt = g;
        console.log(gatt);
        return new Promise(function(resolve) {
          setTimeout(resolve, 1000);
          console.log("delayed for 1000ms...");
        });
      }).then(function() {
          console.log("end delay");
        return gatt.getPrimaryService(serviceUUID);
      }).then(function(service) {
        return service.getCharacteristic(characteristic­UUID);
      }).then(function(characteristic) {
        return characteristic.writeValue(MessageToSend)­;
      }).then(function() {
        console.log("Done!");
      });
    }
    

    Which, when executed, gives:

    >Connect(MyDev)
    =undefined
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -64,
        "services": [  ],
        "data": new Uint8Array([2, 1, 4, 12, 9, 72, 75, 73, 18, 45, 54, 30, 54, 48, 49, 57]).buffer,
        "name": "MyActuator",
        "gatt":  ...
       },
      "connected": true }
    delayed for 1000ms...
    end delay
    Uncaught Error: Unhandled promise rejection: Not connected
    > 
    

    I really can't understand why it connects THEN disconnects quickly.

  • Can you try:

    function Connect(dev){
      NRF.requestDevice({ filters: [{ name: Devname }] }).then(function(device) {
        return device.gatt.connect();
      }).then(function(g) {
        gatt = g;
        console.log(gatt);
        return new Promise(function(resolve) {
          setTimeout(resolve, 1000);
          console.log("delayed for 1000ms...");
        });
      }).then(function() {
        console.log(gatt);
        console.log("end delay - getPrimaryService");
        return gatt.getPrimaryService(serviceUUID);
      }).then(function(service) {
        console.log("getCharacteristic");
        return service.getCharacteristic(characteristic­UUID);
      }).then(function(characteristic) {
        console.log("writeValue");
        return characteristic.writeValue(MessageToSend)­;
      }).then(function() {
        console.log("Done!");
      });
    }
    

    just to be sure that the connection really did drop during that 1 sec?

    But if it's disconnected at that point then in a way it tells us the problem. The connection is failing before you're even doing anything, so it's some problem during the negotiation.

    Googling BLE_HCI_CONN_FAILED_TO_BE_ESTABLISHED seems to imply that it's likely to be an issue with whitelisting/bonding information on the peripheral you're connecting to. Are you in contact with the developers of it? They might have come across this behaviour before.

    Out of interest, can you still connect with a second phone? It might be the actuator only holds bonding information for a single device at a time.

  • Just trying your code, it gives me this log:

    >Connect2(MyDev)
    =undefined
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -54,
        "services": [  ],
        "data": new Uint8Array([2, 1, 4, 12, 9, 72, 75, 73, 18, 45, 54, 30, 54, 48, 49, 57]).buffer,
        "name": "MyActuator",
        "gatt":  ...
       },
      "connected": true }
    delayed for 1000ms...
    BluetoothRemoteGATTServer {
      "device": BluetoothDevice {
        "id": "96:05:05:f7:ac:1c random",
        "rssi": -54,
        "services": [  ],
        "data": new Uint8Array([2, 1, 4, 12, 9, 72, 75, 73, 18, 45, 54, 30, 54, 48, 49, 57]).buffer,
        "name": "MyActuator",
        "gatt":  ...
       },
      "connected": false }
    end delay - getPrimaryService
    Uncaught Error: Unhandled promise rejection: Not connected
    > 
    

    I just tried with the smartphone of a colleague (Samsung galaxy S5), he just can't connect to it. Therefore, I will investigate the whitelisting/bonding topic.
    It's a shame I cannot use the nrf sniffer (PCA100028) that I just received, it seem to have a problem with the Segger driver version, that seems to be too recent :-/

  • OK I have good news: I can open and maintain a connection with the actuator !
    Once again, you were right @Gordon, it was due to a refused bonding. When I discovered the param forceRePair of the startBonding() method I immediately tried to set it to 1 and ... it worked.

    Now I cannot read any value , including mandatory fields such as DeviceInfo.ModelNumber (0x180A-->0x2A24). However I am strictly following the example given in the doc http://www.espruino.com/Reference#l_Blue­toothRemoteGATTCharacteristic_readValue :

    var device;
    NRF.connect(device_address).then(functio­n(d) {
      device = d;
      return d.getPrimaryService("service_uuid");
    }).then(function(s) {
      console.log("Service ",s);
      return s.getCharacteristic("characteristic_uuid­");
    }).then(function(c) {
      return c.readValue();
    }).then(function(d) {
      console.log("Got:", JSON.stringify(d.buffer));
      device.disconnect();
    }).catch(function() {
      console.log("Something's broken.");
    });
    

    I always receive an empty buffer . I tried to retrieve this value with NrfConnect and this works well.

    By the way, I am experiencing disconnections between the IDE and the puck sometimes (I would say every 4-5 minutes after having reseted the puck), even if I don't download any script in it. Is it a feature to keep the consumption low, or an issue in my setup?

  • Good news about the connection...

    About the characteristics, again, I'm not sure - that code would work on a normal device. Just to rule anything out, could you try calling startBonding after connect, and then try and read the information after that?

    Between your Puck and PC, have you checked the Puck's battery voltage? It shouldn't auto disconnect at all, so there's something a bit iffy there. What OS are you using to connect with?

  • Just to rule anything out, could you try calling startBonding after connect, and then try and read the information after that?

    IMO it is not necessary, I am quite sure it is bonded now. Furthermore, I can discover services with

    function Discover(){
        gatt.getPrimaryServices().then(function(­services) {
          console.log("Services:",services);
        }).catch(function(e){console.log("error:­ ",e);});
    }
    

    with the corresponding log:

    Services: [
      BluetoothRemoteGATTService {
        "uuid": "0x1800",
        "isPrimary": true, "start_handle": 1, "end_handle": 7 },
      BluetoothRemoteGATTService {
        "uuid": "0x1801",
        "isPrimary": true, "start_handle": 8, "end_handle": 11 },
      BluetoothRemoteGATTService {
        "uuid": "0x0000[vendor]",
        "isPrimary": true, "start_handle": 12, "end_handle": 21 },
      BluetoothRemoteGATTService {
        "uuid": "0x180a",
        "isPrimary": true, "start_handle": 22, "end_handle": 34 },
      BluetoothRemoteGATTService {
        "uuid": "0x0000[vendor]",
        "isPrimary": true, "start_handle": 35, "end_handle": 44 },
      BluetoothRemoteGATTService {
        "uuid": "0x0000[vendor]",
        "isPrimary": true, "start_handle": 45, "end_handle": 65535 }
     ]
    

    Actually 3 services with proprietary UUIDs are listed with 0x0000[vendor]. I suspect that it is the reason why I can't exchange with those services...

    I just made a READ request on the "Generic Access" service (0x1800), characteristic "Device Name" (0x2A00) and I am able to retrieve the correct device name.

  • That is a strange one. If I could reproduce here then I might be able to figure out why it's displaying 0x0000[vendor].

    However you could always get a BluetoothRemoteGATTCharacteristic for another characteristic, could fudge the handle in it to a different value, and could try using that

  • Interesting - thanks! Now I just have to figure out which bulb it was!

  • Interesting... It's reported wrong, but it looks like it's forBLE_UUID_TYPE_UNKNOWN, which is Invalid UUID type. But that's coming right out of the Bluetooth stack so honestly I have no idea what's going on there.

    I'd try fudging the BLE handle and see if that will do it.

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

GATT connection timeout?

Posted by Avatar for Jean-Philippe_Rey @Jean-Philippe_Rey

Actions