11
votes

I am trying to enable notifications for more than one BLE characteristic using Xamarin/Android but seem unable to do so. The app seems to stop receiving any BLE events if I try and enable more than one at a time.

Can anyone confirm whether this is possible using Tamarin/Android. We have a native iOS app that works just fine with multiple notifications enabled. The basic steps we use are as follows:

  1. Scan for device
  2. Connect to device
  3. Discover services
  4. For each discovered service iterate through characteristics and enable the ones that are required
  5. Process each asynchronous callback event in the BLE callback

Any time we try and enable notifications on more than one characteristic we no longer receive any events.

I have also been unable to find any examples where more than one characteristic is being enabled.

I hope I have simply missed something fundamental about using the Xamarin/Android APIs here.

public override void OnServicesDiscovered (BluetoothGatt gatt, GattStatus status)
{
    base.OnServicesDiscovered (gatt, status);
    foreach (BluetoothGattService service in gatt.Services) {
        string uuid = service.Uuid.ToString ().ToUpper();
        if (uuid.Equals (BLEServices.HRService.ToUpper())) {
            _Adap.LogMessage ("HRService discovered");
            foreach(BluetoothGattCharacteristic characteristic in service.Characteristics) {
                string c_uuid = characteristic.Uuid.ToString ().ToUpper ();
                _Adap.LogMessage (" HRCharacteristic: " + c_uuid);

                if (c_uuid.Equals(_Adap.useCharacteristic.ToUpper())) {
                    _Adap.LogMessage ("  enabling HRCharacteristic");
                    gatt.SetCharacteristicNotification(characteristic, true);
                    BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor (Java.Util.UUID.FromString (BLEServices.CLIENT_CHARACTERISTIC_CONFIG), GattDescriptorPermission.Write | GattDescriptorPermission.Read);
                    characteristic.AddDescriptor (descriptor);
                    descriptor.SetValue (BluetoothGattDescriptor.EnableNotificationValue.ToArray ());
                    gatt.WriteDescriptor (descriptor);
                    _Adap.StartTimer ();
                }
            }

        } else if (uuid.Equals (BLEServices.BatteryService.ToUpper())) {
            _Adap.LogMessage ("BatteryService discovered");
            foreach (BluetoothGattCharacteristic characteristic in service.Characteristics) {
                string c_uuid = characteristic.Uuid.ToString ().ToUpper ();
                _Adap.LogMessage (" BatteryService: " + c_uuid);

                if (c_uuid.Equals (_Adap.useCharacteristic.ToUpper ())) {
                    _Adap.LogMessage ("  reading batteryCharacteristic");
                    // This may only be reported when the battery level changes so get the level first by doing a read
                    gatt.ReadCharacteristic (characteristic);

                    //gatt.SetCharacteristicNotification (characteristic, true);
                    //BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor (Java.Util.UUID.FromString (BLEServices.CLIENT_CHARACTERISTIC_CONFIG), GattDescriptorPermission.Write | GattDescriptorPermission.Read);
                    //characteristic.AddDescriptor (descriptor);
                    //descriptor.SetValue (BluetoothGattDescriptor.EnableNotificationValue.ToArray ());
                    //gatt.WriteDescriptor (descriptor);
                }
            }
        } else if (uuid.Equals (BLEServices.DeviceInfoService.ToUpper())) {
            _Adap.LogMessage ("DeviceInfoService discovered");
            foreach (BluetoothGattCharacteristic characteristic in service.Characteristics) {
                string c_uuid = characteristic.Uuid.ToString ().ToUpper ();
                _Adap.LogMessage (" DeviceInfoService: " + c_uuid);
                if (c_uuid.Equals (BLEServices.kModelNumberCharacteristicUuidString.ToUpper ())) {
                    //gatt.ReadCharacteristic (characteristic);
                }
            }
        } else if (uuid.Equals (BLEServices.kHxM2CustomServiceUuidString.ToUpper())) {
            _Adap.LogMessage ("HxM2CustomService discovered");
            foreach (BluetoothGattCharacteristic characteristic in service.Characteristics) {
                string c_uuid = characteristic.Uuid.ToString ().ToUpper ();
                _Adap.LogMessage (" HxM2CustomCharacteristic: " + c_uuid);

                if (c_uuid.Equals (_Adap.useCharacteristic.ToUpper ())) {
                    _Adap.LogMessage ("  enabling HxM2 characteristic: "+_Adap.useCharacteristic);
                    gatt.SetCharacteristicNotification (characteristic, true);
                    BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor (Java.Util.UUID.FromString (BLEServices.CLIENT_CHARACTERISTIC_CONFIG), GattDescriptorPermission.Write | GattDescriptorPermission.Read);
                    characteristic.AddDescriptor (descriptor);
                    descriptor.SetValue (BluetoothGattDescriptor.EnableNotificationValue.ToArray ());
                    gatt.WriteDescriptor (descriptor);
                    // Start a timer to make sure that we can recover if we never receive any data from the device
                    _Adap.StartTimer ();
                }

            }
        } else {
            _Adap.LogMessage ("Unknown Service "+uuid+" discovered");
        }
    }
}

Can anyone explain what the following lines are for

BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor (Java.Util.UUID.FromString (BLEServices.CLIENT_CHARACTERISTIC_CONFIG), GattDescriptorPermission.Write | GattDescriptorPermission.Read);
characteristic.AddDescriptor (descriptor);
descriptor.SetValue (BluetoothGattDescriptor.EnableNotificationValue.ToArray ());
gatt.WriteDescriptor (descriptor);
2

2 Answers

4
votes

Beside your found solution: Be aware, that you can't listen to an unlimited number of characteristics. The maximum is limited hardcoded in the android source to BTA_GATTC_NOTIF_REG_MAX.

So your app should not rely on more than the maximum number of notifying characteristics of your minimum supported android version.

0
votes

there is number of option re availble but i used to below logic to implement for this characteristics and it will realy working please try it:

 public ConnectThread(BluetoothDevice device, BluetoothDeviceConnector deviceConnector)
            {
                _mmDevice = device;
                BluetoothSocket tmp = null;
                _deviceConnector = deviceConnector;

                Log.Info(Tag, "calling device.createRfcommSocket with channel 1 ...");
                try
                {
                    //tmp = device.CreateRfcommSocketToServiceRecord(UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"));
                    //tmp = device.CreateRfcommSocketToServiceRecord(device.GetUuids()[0].Uuid);
                    deviceConnector.BluetoothAdapter.CancelDiscovery();

                    var createRfcommSocket = JNIEnv.GetMethodID(device.Class.Handle, "createInsecureRfcommSocket", "(I)Landroid/bluetooth/BluetoothSocket;");
                    var socket = JNIEnv.CallObjectMethod(device.Handle, createRfcommSocket, new JValue(1));
                    tmp = GetObject<BluetoothSocket>(socket, JniHandleOwnership.TransferLocalRef);

                    var uuidList = device.GetUuids();
                    if (uuidList != null)
                    {
                        foreach (var uuid in uuidList)
                        {
                            try
                            {
                                tmp = device.CreateInsecureRfcommSocketToServiceRecord(uuid.Uuid);
                                tmp.Connect();
                                if (tmp.IsConnected)
                                    break;
                            }
                            catch (Exception)
                            {
                                // ignored
                            }
                        }
                    }

                    Log.Info(Tag, "setting socket to result of createRfcommSocket");
                }
                catch (Exception e)
                {
                    Log.Error(Tag, e.Message, e);
                }
                _mmSocket = tmp;
            }