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:
- Blutdruckmanschette
- 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:
- Die BLE Gerät eingeschaltet ist, eine Messung wird eingeleitet, auf das Gerät.
- Einmal die Messung ausgeführt wurde, der BLE, versucht das Gerät zu initiieren, die ein BLE-Verbindung.
- 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.)
- Wenn das Letzte Datenpaket, das gesendet wird, ist der BLE-Gerät trennt die Verbindung schnell.
- 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.
- Sehe ich eine Liste von Diensten, wie ich erwarte, einschließlich der Daten-service ich möchte.
- Sehe ich eine Liste von Merkmalen, wie ich Sie erwarten, einschließlich der Daten, die Eigenschaft, die ich will.
- 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:
- Android BLE onCharacteristicChanged nicht genannt
- Android BLE, Lesen und schreiben von Eigenschaften
- Android 4.3 onDescriptorWrite zurückgegebene status 128
Hier ist, warum Sie nicht geben mir, was ich brauche:
- 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.
- 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.
- 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.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Habe ich in der Lage, erfolgreich zu fangen onCharacteristicChanged() mit mehreren Leistungen und Eigenschaften von:
Schreiben Deskriptor-Werte in der broadcastReceiver() in der Hauptschleife nach dem service discovery ist fertig.
und
Durch das hinzufügen einer Verzögerung zwischen dem Deskriptor Wert-Einstellungen
readCharacteristic(...)
undwriteDescriptor(...)
back-to-back-innenonServicesDiscovered(...)
. DiewriteDescriptor(...)
einleiten würde ein schreiben und zurücktrue
, aber es würde nicht durchkommen, und seine callback -onDescriptorWrite(...)
würde nicht ausgelöst. Ich nahm den Anruf anreadCharacteristic(...)
, so dass Sie jetzt nicht back-to-back. JetztwriteDescriptor(...)
durch bekommt, und seine callback feuert auch.Ich Stand vor dem gleichen problem.
das ist, weil, wenn das Gerät sendet den Wert angeben, Ihre Anwendung wird geladen in einen anderen Prozess und das ist, warum Sie nie das zeigen Wert, die die
onCharacteristicChanged
nie ausgelöst.um Ihr problem zu lösen versuchen, alle traitement im service. und nur Funktionen aufrufen, die von Ihrer Aktivität.