Android BLE: onCharacteristicChanged nie ausgelöst

Ich versuche zu schreiben, ein Android-app imitiert die Funktionalität bereits in einer iOS-app, die ich schrieb. Ich bin eine Schnittstelle mit 2 verschiedenen BLE-Geräte:

  1. Blutdruckmanschette
  2. Waage

Auf iOS, ich habe beide Geräte arbeiten gut und reporting-Daten. Auf Android, die ich nicht bekommen kann es arbeiten. Nach Stunden der Forschung und Tests, ich denke, die grundlegende Frage, die ich zu lösen versuche, ist dies:

Auf iOS, ich nenne Sie den folgenden code zu ermöglichen, die BLE-Gerät zu Benachrichtigen meinem iOS-Gerät, wenn es Daten zu melden:

#pragma mark - CBPeripheralDelegate Protocol methods
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    for (CBCharacteristic *characteristic in [service characteristics]) {
        [peripheral setNotifyValue:YES forCharacteristic:characteristic];
    }
}

Das ist es. Die Noten für diese Methode in iOS Folgendes sagen:

Wenn das angegebene Merkmal konfiguriert ist, dass Meldungen und Hinweise, hat das aufrufen dieser Methode können Benachrichtigungen nur.

Basierend auf das (und die Tatsache, dass es funktioniert in iOS), ich bin herauszufinden, dass der configuration-descriptor für das charakteristische, für die ich Benachrichtigungen konfiguriert werden sollte, wie diese:

descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);

In diesem Sinne, meine BLEDevice - Klasse sieht wie folgt aus:

public abstract class BLEDevice {
    protected BluetoothAdapter.LeScanCallback   mLeScanCallback;
    protected BluetoothGattCallback             mBluetoothGattCallback;
    protected byte[]                            mBytes;
    protected Context                           mContext;
    protected GotReadingCallback                mGotReadingCallback;
    protected String                            mDeviceName;
    public final static UUID                    UUID_WEIGHT_SCALE_SERVICE                       
            = UUID.fromString(GattAttributes.WEIGHT_SCALE_SERVICE);
    public final static UUID                    UUID_WEIGHT_SCALE_READING_CHARACTERISTIC        
            = UUID.fromString(GattAttributes.WEIGHT_SCALE_READING_CHARACTERISTIC);
    public final static UUID                    UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC  
            = UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
    public final static UUID                    UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR      
            = UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);

    abstract void processReading();

    interface GotReadingCallback {
        void gotReading(Object reading);
    }

    public BLEDevice(Context context, String deviceName, GotReadingCallback gotReadingCallback) {
        mContext                    = context;
        BluetoothManager btManager  = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
        final BluetoothAdapter btAdapter  = btManager.getAdapter();
        if (btAdapter != null && !btAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            mContext.startActivity(enableIntent);
        }
        mDeviceName = deviceName;
        mBluetoothGattCallback = new BluetoothGattCallback() {
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
                byte[] data = characteristic.getValue();
                mBytes = data;
                Log.d("BluetoothGattCallback.onCharacteristicChanged", "data: " + data.toString());
            }
            @Override
            public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
                //this will get called when a device connects or disconnects
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    gatt.discoverServices();
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    if (mBytes != null) {
                        processReading();
                    }
                }
            }
            @Override
            public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                super.onDescriptorWrite(gatt, descriptor, status);
                Log.d("onDescriptorWrite", "descriptor: " + descriptor.getUuid() + ". characteristic: " + descriptor.getCharacteristic().getUuid() + ". status: " + status);
            }
            @Override
            public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
                //this will get called after the client initiates a BluetoothGatt.discoverServices() call
                BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
                if (service != null) {
                    BluetoothGattCharacteristic characteristic;
                    characteristic                              = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
                    if (characteristic != null) {
                        gatt.setCharacteristicNotification(characteristic, true);
                    }
                    characteristic                              = service.getCharacteristic(UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
                    if (characteristic != null) {
                        BluetoothGattDescriptor descriptor      = characteristic.getDescriptor(UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
                        if (descriptor != null) {
                            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                            gatt.writeDescriptor(descriptor);
                        }
                    }
                }
            }
        };
        mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
                Log.d("LeScanCallback", device.toString());
                if (device.getName().contains("{Device Name}")) {
                    BluetoothGatt bluetoothGatt = device.connectGatt(mContext, false, mBluetoothGattCallback);
                    btAdapter.stopLeScan(mLeScanCallback);
                }
            }
        };
        btAdapter.startLeScan(mLeScanCallback);
    }
}

HINWEIS: Es könnte wichtig sein zu wissen, dass diese 2 Geräte-Funktion in der folgenden Weise:

  1. Die BLE Gerät eingeschaltet ist, eine Messung wird eingeleitet, auf das Gerät.
  2. Einmal die Messung ausgeführt wurde, der BLE, versucht das Gerät zu initiieren, die ein BLE-Verbindung.
  3. Einmal die BLE-Verbindung hergestellt ist, wird das Gerät ziemlich viel sofort sendet die Daten, manchmal schicke ein paar Daten-Pakete. (Wenn die vorherigen Daten der Messungen noch nicht erfolgreich gesendet wurde, über BLE, es hält Sie im Speicher und sendet alle von Ihnen, so dass ich mich nur wirklich um das Letzte Datenpaket.)
  4. Wenn das Letzte Datenpaket, das gesendet wird, ist der BLE-Gerät trennt die Verbindung schnell.
  5. Wenn die BLE-Gerät ausfällt, um Daten zu senden (wie es derzeit geschieht, wird auf der Android-app), die BLE-Gerät die Verbindung trennt sich ziemlich schnell.

In meinem LogCat, sehe ich eine Menge von Ausgabe genau, wie ich es erwarten würde.

  1. Sehe ich eine Liste von Diensten, wie ich erwarte, einschließlich der Daten-service ich möchte.
  2. Sehe ich eine Liste von Merkmalen, wie ich Sie erwarten, einschließlich der Daten, die Eigenschaft, die ich will.
  3. Sehe ich eine Liste von Deskriptoren, wie ich erwarten, einschließlich der "Konfiguration" (0x2902) Deskriptor.

Letzten Ausfall ich bin erfahren ist ein status von "128" berichtet wird, die in onCharacteristicWrite. Die Kommentare zu Frage #3 (unten) scheinen zu zeigen, dies ist ein Ressourcen-Problem.

Ist, habe ich mich auf die folgenden Fragen:

  1. Android BLE onCharacteristicChanged nicht genannt
  2. Android BLE, Lesen und schreiben von Eigenschaften
  3. Android 4.3 onDescriptorWrite zurückgegebene status 128

Hier ist, warum Sie nicht geben mir, was ich brauche:

  1. Dieser Frage wurde die Antwort nicht zu Lesen der Deskriptor Wert. Ich bin nicht so machen, so, dass nicht sein kann, was sich in den Weg.
  2. Dies ist im Grunde ein überblick über die verschiedenen Methoden, die verfügbar sind, was ich glaube, ich verstehe jetzt. Die großen Schlüssel in dieser Frage/Antwort nicht zu schreiben mehrfach an verschiedene Deskriptoren, aber ich bin auch nicht zu tun. Ich habe nur die Sorge um die ein Merkmal.
  3. Diese Frage/Antwort scheint zu sein, in Bezug auf BLE und die Ressourcenknappheit, aber ich glaube nicht, dass dies zutrifft. Ich bin nur die Verbindung dieses eine Gerät, und ich bin versucht zu tun, ein sehr, sehr einfaches Daten übertragen. Ich glaube nicht, ich bin schlagen Ressource decken.

Ich habe versucht eine Reihe von Beispielen und tutorials, einschließlich Google Android-Beispiel-code. Keiner von Ihnen scheinen zu ermöglichen, die BLE-Gerät zu Benachrichtigen meinem Android-Gerät von Daten-updates. Es ist offensichtlich nicht das Gerät, da die iOS-version funktioniert. Also, was ist die iOS-code zu tun im hintergrund, um die Benachrichtigungen zu arbeiten und was-code auf der Android-Seite wird imitieren die Funktionalität?


BEARBEITEN/AKTUALISIEREN

Basierend auf @yonran Kommentare, ich aktualisiert mein code durch ändern der onServicesDiscovered Umsetzung dieser:

@Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
    //this will get called after the client initiates a BluetoothGatt.discoverServices() call
    BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
    if (service != null) {
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
        if (characteristic != null) {
            if (gatt.setCharacteristicNotification(characteristic, true) == true) {
                Log.d("gatt.setCharacteristicNotification", "SUCCESS!");
            } else {
                Log.d("gatt.setCharacteristicNotification", "FAILURE!");
            }
            BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
            if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
                //It's an indicate characteristic
                Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is INDICATE");
                if (descriptor != null) {
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                    gatt.writeDescriptor(descriptor);
                }
            } else {
                //It's a notify characteristic
                Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is NOTIFY");
                if (descriptor != null) {
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    gatt.writeDescriptor(descriptor);
                }
            }
        }
    }
}

Dass hat zu haben scheinen ein paar Dinge geändert, ein wenig. Hier der aktuelle Logcat, folgenden code ändern:

D/BluetoothGatt setCharacteristicNotification() - uuid: <UUID> enable: true
D/gatt.setCharacteristicNotification SUCCESS!
D/onServicesDiscovered Characteristic (<UUID>) is INDICATE
D/BluetoothGatt writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
D/BluetoothGatt onDescriptorWrite() - Device=D0:5F:B8:01:6C:9E UUID=<UUID>
D/onDescriptorWrite descriptor: 00002902-0000-1000-8000-00805f9b34fb. characteristic: <UUID>. status: 0
D/BluetoothGatt onClientConnectionState() - status=0 clientIf=6 device=D0:5F:B8:01:6C:9E

So, es scheint, dass ich jetzt alles richtig (da setCharacteristicNotification zurück true und die onDescriptorWrite status 0). Allerdings onCharacteristicChanged noch nie ausgelöst.


  • Würden Sie bitte die überprüfung der charakteristischen Eigenschaften zu sehen, die unterstützt wird (informieren vs. angeben)? 0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)? iOS automatisch tut dies für Sie.
  • Ich würde es lieben! Tun wird, wenn ich zurück zu meinem Büro. Vielen Dank für die mir eine Idee. Sie denken, ich könnte brauchen, um es anders???
  • Der folgende code true zurückgibt (Ausführung geht es in der "if" - Anweisung): if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
  • Warten Sie, sieht aus wie Sie nennen setCharacteristicNotification auf eine andere Charakteristik als die, an deren client charakteristisch Konfiguration Aktivierung der Anzeige. Verwenden Sie das gleiche Merkmal.
  • Ja, ich gefangen, und es geändert. Ich bin zu nach einem update.
  • Ok. Code update gepostet. Vielen Dank für Ihre Hilfe. Ich glaube, ich bin näher an eine Lösung jetzt. Immer noch nicht dort, zwar.
  • Ich weiß nicht Recht jetzt. Vielleicht eine Paketverfolgung wird zeigen, ob das Gerät tatsächlich gab Anzeichen? Siehe nowsecure.com/blog/2014/02/07/bluetooth-packet-capture-android
  • Ich habe die btsnoop Datei von meinem Gerät, und ich bin mit der Analyse in Wireshark. Ich sehe sehr viel traffic hin und her zwischen meinem Handy und dem Maßstab (BLE-Gerät), aber es erscheint meist das Gespräch, das Auftritt, während discoverServices(). Irgendwelche Gedanken auf, was ich eingrenzen? Wie würde ich es erkennen, eine Meldung/Hinweis?
  • Lassen Sie uns weiter, diese Diskussion im chat.
  • haben Sie die Lösung. Ich habe das gleiche problem
  • Leider war die Lösung zu bekommen ein neueres Gerät. Ich habe ein LG G3 und habe es aktualisiert, um Android Lollipop 5.0.1. Danach funktionierte alles gut. Was meine Forschung hat mir gesagt, die BLE-stack war super instabil, bis Android 4.4.4 (mein altes LG Optimus G Pro wurde ausgeführt 4.4.3; go figure). Auf meinem LG G3 läuft 5.0.1, der code, den ich geschrieben habe funktioniert wie ein Charme! Ich weiß, das ist eine schlechte "Lösung", aber es funktioniert tatsächlich zuverlässig, also vielleicht ist das etwas hilfreich.

InformationsquelleAutor mbm29414 | 2015-02-18
Schreibe einen Kommentar