• I'm writing a port of the Bose AR Web SDK to Bangle.js. Right now I'm able to connect to the BLE device, get the service/characteristics and read/write values, but I get a CCCD Handle not found error when I run characteristic.startNotifications() on the characteristics with the notify property.

    I looked it up and it seems to look for a handle_cccd property on the characteristic so I looked it up and found the cccd 0x1 and assigned it to the characteristics' characteristic.handle_cccd property, and the error stopped. However I still don't get any notifications when adding an eventListener via characteristic.on('characteristicvaluech­anged', callback).

  • Wow, that's a strange one - and you're sure you were calling startNotifications on the right characteristic? Could you post up your code?

    CCCD Handle not found normally gets called when it *can't*find a handle_cccd property so I'm surprised one was actually set...

  • There was no handle_cccd property before, so I tried to set it manually via characteristic.hande_cccd = 0x1.

    const BoseAR = {
      SERVICE_UUID: "0000fdd2-0000-1000-8000-00805f9b34fb",
      
      CHARACTERISTIC_UUIDs: {
        GESTURE_INFORMATION: "a0384f52-f95a-4bcd-b898-7b9ceec92dad",
        GESTURE_CONFIGURATION: "21e550af-f780-477b-9334-1f983296f1d7",
        GESTURE_DATA: "9014dd4e-79ba-4802-a275-894d3b85ac74",
        
        SENSOR_INFOMRATION: "855cb3e7-98ff-42a6-80fc-40b32a2221c1",
        SENSOR_CONFIGURATION: "5af38af6-000e-404b-9b46-07f77580890b",
        SENSOR_DATA: "56a72ab8-4988-4cc8-a752-fbd1d54a953d",
      },
      
      GESTURE_IDs: {
          129 : 'double tap',
          130 : 'head nod',
          131 : 'head shake',
    
          192 : 'unknown gesture 192',
          193 : 'unknown gesture 193',
          194 : 'unknown gesture 194',
      },
      getGestureNameByGestureId: function (GESTURE_ID) {
        return this.GESTURE_IDs[GESTURE_ID];
      },
      getGestureIdByGestureName: function(GESTURE_NAME) {
        return Object.keys(this.GESTURE_IDs).find(GESTU­RE_ID => {
          return this.GESTURE_IDs[GESTURE_ID] == GESTURE_NAME;
        });
      },
      
      _ableGesture : function(gestureName, enabled) {
        const _gestureId = this.getGestureIdByGestureName(gestureNa­me);
        if(this.isConnected()) {
          return this.characteristics.GESTURE_CONFIGURATI­ON.readValue().then(dataView => {
            for(let offset = 0; offset < dataView.byteLength; offset+=2) {
              const gestureId = dataView.getUint8(offset);
              if(gestureId == _gestureId) {
                dataView.setUint8(offset+1, enabled);
              }
            }
            return this.characteristics.GESTURE_CONFIGURATI­ON.writeValue(dataView.buffer);
          });
        }
      },
      enableGesture : function(gestureName) {
        return this._ableGesture(gestureName, true);
      },
      disableGesture : function(gestureName) {
        return this._ableGesture(gestureName, false);
      },
      
      SENSOR_NAMEs: [
        'accelerometer',
        'gyroscope',
        'rotation',
        'game rotation'
      ],
      SENSOR_SAMPLE_PERIODs: [
        320,
        160,
        80,
        40,
        20
      ],
      ACCURACY_NAMEs: [
        'unreliable',
        'low',
        'medium',
        'high'
      ],
      
      connect: function() {
        return NRF.requestDevice({
          filters: [{services: [this.SERVICE_UUID]}],
        }).then(device => {
          this.device = device;
          return device.gatt.connect();
        }).then(server => {
          this.server = server;
          return server.startBonding();
        }).then(() => {
          return this.server.getPrimaryService(this.SERVI­CE_UUID);
        }).then(service => {
          this.service = service;
          this.characteristics = {};
          return Object.keys(this.CHARACTERISTIC_UUIDs).r­educe((promise, CHARACTERISTIC_NAME) => {
            const CHARACTERISTIC_UUID = this.CHARACTERISTIC_UUIDs[CHARACTERISTIC­_NAME];
            return promise.then(() => {
              return this.service.getCharacteristic(CHARACTER­ISTIC_UUID).then(characteristic => {
                console.log(CHARACTERISTIC_NAME);
                
                this.characteristics[CHARACTERISTIC_NAME­] = characteristic;
                switch(CHARACTERISTIC_NAME) {
                    
                  case 'GESTURE_INFORMATION':
                    break;
                  case 'GESTURE_CONFIGURATION':
                    break;
                  case 'GESTURE_DATA':
                    break;
                    
                  case 'SENSOR_INFOMRATION':
                    break;
                  case 'SENSOR_CONFIGURATION':
                    break;
                  case 'SENSOR_DATA':
                    break;
                    
                  default:
                    break;
                }
                
                if(characteristic.properties.notify) {
                  //characteristic.handle_cccd = 0x1;
                  characteristic.on('characteristicvaluech­anged', event => {
                    console.log(event);
                  });
                  
                  return characteristic.startNotifications();
                }
              });
            });
          }, Promise.resolve([]));
        }).then(() => {
          console.log("FINISHED");
        });
      },
      isConnected : function() {
        return this.device && this.device.gatt && this.device.gatt.connected;
      },
      disconnect : function() {
        if(this.isConnected()) {
          this.device.gatt.disconnect();
        }
      },
    };
    
    
    BoseAR.connect();
    
  • Ahh - thanks for posting up the code. That looks pretty good.

    I guess my thought is maybe the CCCD isn't at handle 1? Usually the CCCD is next to the characteristic that it is for. Perhaps you could try setting it with something like the nRF Connect app, and then look at the log?

  • It turns out the CCCD value for all the Notify characteristics are 0, which triggers the "CCCD Handle Not Found" error. I'm not sure if that's an invalid CCCD or it is but it triggers the error because 0 is considered a false value.

  • Wow, thanks for tracking that down! I bet 0 is valid. I'll try and get a fix into Espruino for that today - I'll post up when done

  • Hmm... from the Nordic SDK:

    /**@brief Invalid Attribute Handle. */
    [#define](http://forum.espruino.com/sear­ch/?q=%23define) BLE_GATT_HANDLE_INVALID            0x0000
    
    /**@brief First Attribute Handle. */
    [#define](http://forum.espruino.com/sear­ch/?q=%23define) BLE_GATT_HANDLE_START              0x0001
    
    /**@brief Last Attribute Handle. */
    [#define](http://forum.espruino.com/sear­ch/?q=%23define) BLE_GATT_HANDLE_END                0xFFFF
    

    So it looks a lot like the Bose device is actually using an out of spec handle. It does seem amazingly unlikely that anything would have a 0xFFFF handle, but then if we used that as an invalid handle we'd end up potentially breaking something that was ok.

    What I've done for now is to modify the code so that if you manually set handle_cccd=0 as you were doing, startNotifications should then work.

    So if you try a 'cutting edge' build now and you can get it working, let me know and i'll figure out another way of handling the CCCD discovery that will deal with a CCCD handle of 0.

  • I flashed the latest build and now I don't get errors when I set characteristic.handle_cccd = 0;, but I still don't get any notifications.

    And thank you so much for taking the time to help out so far

  • Thanks for letting me know - I'd be pretty sure it is setting the CCCD characteristic now, so I wonder whether there's something else that's not quite normal in the Bose bluetooth setup

  • The weirdest thing happened - when I enabled the sensors from my desktop browser (which triggers the device to stream sensor/gesture data), the CCCD's changed to 0x0100 (some are still 0, but the ones I need to be notified of changed to 0x0100).

    I'm still not getting notifications, but I'll keep playing with it.

  • Are there any tools besides the nRF Connect app I can use to debug this further?

    Also is there anyone I can pay to figure this out?

  • All I could really suggest is https://www.nordicsemi.com/Software-and-­tools/Development-Tools/nRF-Sniffer-for-­Bluetooth-LE for sniffing the actual bluetooth traffic.

    However if bonding is required then it likely wouldn't be much use as the connection would be encrypted.

    Just a thought but is is possible that the device is expecting a number to be entered during the bonding process, and so the bonding actually fails? What does server.getSecurityStatus say after bonding completes? http://www.espruino.com/Reference#l_Blue­toothRemoteGATTServer_getSecurityStatus

    Also is there anyone I can pay to figure this out?

    You could post on http://forum.espruino.com/microcosms/120­2/ and see if anyone replies?

    I can do consultancy work as well if needed?

  • Hi,

    I have the same error as the topic starter, not sure if my problem is related, I'm rather new to Espruino/BLE so it may be a "user error".

    Here's my code, and the output it produces. It seems to run OK until the point when Notifications are enabled:

    var d;
    var out="";
    
    function getData(){
      print("Getting data now");
      g.drawString("Getting data now",0,60);
        NRF.connect("A4:C1:38:4E:75:A0 public").then(function(device) {
          print("Device connected:",device.device.id);
          out=device.device.id;
          g.drawString(out,0,80);
          d=device;
          return d.getPrimaryService("EBE0CCB0-7A0A-4B0C-­8A1A-6FF2997DA3A6");
        }).then(function(s) {
          out=s.uuid;
          g.drawString(out,0,105);
          if(s) { print("Service:",s.uuid); }
          else { print("S-error"); }
          return s.getCharacteristic("EBE0CCC1-7A0A-4B0C-­8A1A-6FF2997DA3A6");
        }).then(function(c) {
          print("Characteristic:",c.uuid);
          print("C:",c);
          out=c.uuid;
          g.drawString(out,0,130);
          c.on('characteristicvaluechanged', function(event) {
            buf =E.toString(event.target.value.buffer);
            g.drawString(buf,0,155);
            print(buf);
            print(event.target.value.buffer);
            // For 26.0 degrees and 54% is like : new Uint8Array([47, 10, 54, 98, 10]).buffer
            b=event.target.value.buffer;
            temp=b[1]*256+b[0];
            hum=b[2];
            print("temp:",temp, " Hum:",hum);
          }); // c.on
          return c.startNotifications();
        }).then(function() {
        console.log("Done!");
    
      });
    } // end getData function
    
    
    
    setWatch(function(){
      print("Bangle setwatch on TOP button activated");
      g.clear();
      g.setFont("Vector",15);
      getData();
    },BTN ,{ repeat:true,debounce:25 }
    );
    
    /*
    WEBIDE OUTPUT
    
                     _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v06 (c) 2019 G.Williams
    >
    Bangle setwatch on TOP button activated
    Getting data now
    Device connected: A4:C1:38:4E:75:A0 public
    Service: ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6
    Characteristic: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6
    C: BluetoothRemoteGATTCharacteristic: {
      "uuid": "ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6",
      "handle_value": 54, "handle_decl": 53,
      "properties": { "broadcast": false, "read": true, "writeWithoutResponse": false, "write": false,
        "notify": true, "indicate": false, "authenticatedSignedWrites": false }
     }
    Uncaught Error: Unhandled promise rejection: CCCD Handle not found
    > 
    
    */
    

    The NrfConnect output for the Notification is below.

    I know the handle for the notification is 0x0038. When I connect to the device using RPI-gatttool using this handle it starts Notifications immediately.:

    pi@raspberrypi3:~/Mydata/python/Xiaomi $ gatttool -b a4:c1:38:4e:75:a0 --char-write-req --handle='0x0038' --value="0100" --listen
    Characteristic value was written successfully
    Notification handle = 0x0036 value: c5 09 3a 4e 0a 
    Notification handle = 0x0036 value: c4 09 39 4e 0a 
    

    Do I need to specify this handle specifically before using startNotifcations, and if so, how do I do that?

    Any help is welcome.


    1 Attachment

    • Schermafbeelding 2020-08-21 om 20.47.13.png
  • BTW : My device is a Xiaomi BLE Thermometer type LYWSD03MMC


    1 Attachment

    • Schermafbeelding 2020-08-21 om 21.11.09.png
  • Update : working now!

    Reading the code from @Zakaton and @Gordon's response I added a characteristic.handle_cccd = 0x0038; statement.

          c.handle_cccd = 0x0038;
          return c.startNotifications();
    

    Now the Notifications are coming through.

    Device connected: A4:C1:38:4E:75:A0 public
    Service: ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6
    Characteristic: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6
    C: BluetoothRemoteGATTCharacteristic: {
      "uuid": "ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6",
      "handle_value": 54, "handle_decl": 53,
      "properties": { "broadcast": false, "read": true, "writeWithoutResponse": false, "write": false,
        "notify": true, "indicate": false, "authenticatedSignedWrites": false }
     }
    Done!
    temp: 2484  Hum: 53
    temp: 2487  Hum: 53
    temp: 2486  Hum: 53
    

    Thanks for the inspiration.

  • Great! I'd love to figure out why this isn't going though - it should really have been able to figure it out automatically.

  • I always think it's ME, not this time ;-)

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

"CCCD Handle not found" error from characteristic.startNotifications()

Posted by Avatar for Zakaton @Zakaton

Actions