Я пытаюсь написать приложение для Android, которое имитирует функциональность, уже присутствующую в написанном мной приложении для iOS. Я взаимодействую с двумя разными устройствами BLE:
- Манжета для измерения артериального давления
- Весовая шкала
На iOS у меня оба устройства работают нормально и предоставляют данные. На Android не могу заставить его работать. После нескольких часов исследований и испытаний я думаю, что основная проблема, которую я пытаюсь решить, заключается в следующем:
В iOS я вызываю следующий код, чтобы позволить устройству BLE уведомлять мое устройство iOS, когда у него есть данные для отчета:
#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];
}
}
Вот и все. Примечания для этого метода в iOS говорят следующее:
Если указанная характеристика настроена так, чтобы разрешать и уведомления, и индикации, вызов этого метода разрешает только уведомления.
Исходя из этого (и того факта, что он работает в iOS), я полагаю, что дескриптор конфигурации для характеристики, для которой я хочу получать уведомления, должен быть настроен следующим образом:
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
Имея это в виду, мой класс BLEDevice
выглядит так:
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);
}
}
ПРИМЕЧАНИЕ.. Возможно, важно знать, что эти 2 устройства работают следующим образом:
- Устройство BLE включается, и на устройстве запускается измерение.
- После выполнения измерения устройство BLE пытается инициировать соединение BLE.
- Как только соединение BLE установлено, устройство практически сразу же отправляет данные, иногда отправляя пару пакетов данных. (Если предыдущие измерения данных не были успешно отправлены через BLE, он сохраняет их в памяти и отправляет их все, поэтому меня действительно волнует только окончательный пакет данных.)
- После отправки последнего пакета данных устройство BLE быстро отключается.
- Если устройству BLE не удается отправить данные (как это сейчас происходит в приложении Android), устройство BLE отключается довольно быстро.
В моем LogCat я вижу много результатов, которые соответствуют моим ожиданиям.
- Я вижу список ожидаемых служб, включая нужную мне службу передачи данных.
- Я вижу список ожидаемых характеристик, включая те характеристики данных, которые мне нужны.
- Я вижу список дескрипторов, как и ожидал, включая дескриптор «конфигурации» (0x2902).
Самая последняя ошибка, с которой я столкнулся, - это статус "128", сообщаемый в onCharacteristicWrite
. Комментарии к вопросу № 3 (ниже), похоже, указывают на то, что это проблема с ресурсами.
Я рассмотрел следующие вопросы:
- Android BLE onCharacteristicChanged не вызывается
- Android BLE, характеристики чтения и записи
- Android 4.3 onDescriptorWrite возвращает статус 128
Вот почему мне не дают то, что мне нужно:
- Ответ на этот вопрос заключался в том, чтобы не читать значение дескриптора. Я этого не делаю, так что это не может быть то, что мешает.
- Это в основном обзор различных доступных методов, которые, как мне кажется, я теперь понимаю. Главный ключ в этом вопросе / ответе - не писать несколько раз в разные дескрипторы, но я тоже этого не делаю. Меня волнует только одна характеристика.
- Этот вопрос / ответ, похоже, связан с ограничениями ресурсов BLE, но я не думаю, что это применимо. Я подключаю только это одно устройство и пытаюсь выполнить очень и очень простую передачу данных. Я не думаю, что достигаю потолка ресурсов.
Я перепробовал кучу примеров и руководств, включая образец кода Google для Android. Ни один из них, похоже, не позволяет устройству BLE уведомлять мое устройство Android об обновлениях данных. Дело явно не в девайсе, так как версия для iOS работает. Итак, что код iOS делает в фоновом режиме, чтобы уведомления работали, и какой код на стороне Android имитирует эту функциональность?
ИЗМЕНИТЬ / ОБНОВИТЬ
Основываясь на комментариях @yonran, я обновил свой код, изменив реализацию onServicesDiscovered
на это:
@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);
}
}
}
}
}
Это действительно, похоже, немного изменило некоторые вещи. Вот текущий Logcat после этого изменения кода:
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
Итак, похоже, что теперь я настраиваю все правильно (поскольку setCharacteristicNotification
возвращает true
, а статус onDescriptorWrite
равен 0
). Однако onCharacteristicChanged
по-прежнему не срабатывает.
0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)
? iOS автоматически сделает это за вас. - person yonran   schedule 18.02.2015if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
- person mbm29414   schedule 19.02.2015discoverServices()
. Есть какие-нибудь мысли о том, на что я должен обратить внимание? Как распознать уведомление / указание? - person mbm29414   schedule 19.02.2015