如何使用 RxAndroidBLE 从 GATT 服务器读取块中的 BLE GATT 特性
How to Read BLE GATT characterstics in chunks form GATT server using RxAndroidBLE
我正在使用 RxAndroidBle 库处理 BLE 连接,并从我的 android gatt 客户端应用程序 reading/writing 到 GATT 服务器。我已按照 on github.
提供的示例应用程序进行操作
我面临的问题是我的 GATT 服务器是 运行 在 Intel Edison 上,它只支持 80 的 MTU 大小。它以块的形式发送数据,我应该多次读取特征值直到我遇到了一个特殊字符,比如 '/END' 。我已经尝试过自定义读取操作示例,它应该每 250 毫秒读取 5 次。
private static class CustomReadOperation implements RxBleRadioOperationCustom<byte[]> {
private RxBleConnection connection;
private UUID characteristicUuid;
CustomReadOperation(RxBleConnection connection, UUID characteristicUuid) {
this.connection = connection;
this.characteristicUuid = characteristicUuid;
}
/**
* Reads a characteristic 5 times with a 250ms delay between each. This is easily achieve without
* a custom operation. The gain here is that only one operation goes into the RxBleRadio queue
* eliminating the overhead of going on & out of the operation queue.
*/
@NonNull
@Override
public Observable<byte[]> asObservable(BluetoothGatt bluetoothGatt,
RxBleGattCallback rxBleGattCallback,
Scheduler scheduler) throws Throwable {
return connection.getCharacteristic(characteristicUuid)
.flatMap(characteristic -> readAndObserve(characteristic, bluetoothGatt, rxBleGattCallback))
.subscribeOn(scheduler)
.takeFirst(readResponseForMatchingCharacteristic())
.map(byteAssociation -> byteAssociation.second)
.repeatWhen(notificationHandler -> notificationHandler.take(5).delay(250, TimeUnit.MILLISECONDS));
}
@NonNull
private Observable<ByteAssociation<UUID>> readAndObserve(BluetoothGattCharacteristic characteristic,
BluetoothGatt bluetoothGatt,
RxBleGattCallback rxBleGattCallback) {
Observable<ByteAssociation<UUID>> onCharacteristicRead = rxBleGattCallback.getOnCharacteristicRead();
return Observable.create(emitter -> {
Subscription subscription = onCharacteristicRead.subscribe(emitter);
emitter.setCancellation(subscription::unsubscribe);
try {
final boolean success = bluetoothGatt.readCharacteristic(characteristic);
if (!success) {
throw new BleGattCannotStartException(bluetoothGatt, BleGattOperationType.CHARACTERISTIC_READ);
}
} catch (Throwable throwable) {
emitter.onError(throwable);
}
}, Emitter.BackpressureMode.BUFFER);
}
private Func1<ByteAssociation<UUID>, Boolean> readResponseForMatchingCharacteristic() {
return uuidByteAssociation -> uuidByteAssociation.first.equals(characteristicUuid);
}
}
我是这样称呼它的
public void customRead()
{
if (isConnected()) {
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.queue(new CustomReadOperation(rxBleConnection, UUID_READ_CHARACTERISTIC)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
configureMvpView.showList(bytes);
}, this::onRunCustomFailure);
}
}
而且我没有使用此代码从服务器获取任何数据。
但是,如果我尝试像这样的简单读取操作
public void readInfo() {
if (isConnected()) {
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(UUID_READ_CHARACTERISTIC))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
// parse data
configureMvpView.showWifiList(bytes);
}, this::onReadFailure);
}
}
我得到了第一个数据块,但我需要读取其余数据。
我不是很精通 RxJava。所以可能有一种简单的方法可以做到这一点,但任何建议或帮助都会很好。
这是我的 prepareConnectionObservable
private Observable<RxBleConnection> prepareConnectionObservable() {
return bleDevice
.establishConnection(false)
.takeUntil(disconnectTriggerSubject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnUnsubscribe(this::clearSubscription)
.compose(this.bindToLifecycle())
.compose(new ConnectionSharingAdapter());
}
我打电话
connectionObservable.subscribe(this::onConnectionReceived, this::onConnectionFailure);
我调用 CustomRead 的 onConnectionReceived。
您没有显示 connectionObservable
是如何创建的,我不知道在执行上述代码之前是否对该连接进行了任何其他操作。
我的猜测是,如果您查看应用程序的日志,您会发现无线电处理队列开始执行您的 CustomReadOperation
作为连接后的第一个操作。在您的自定义操作中,您正在调用 RxBleConnection.getCharacteristic(UUID)
尝试执行 .discoverServices()
(在无线电队列中安排 RxBleRadioOperationDiscoverServices
)。问题是无线电队列已经在执行您的 CustomReadOperation
并且在完成之前不会发现服务。
RxBleConnection
未传递给 RxBleRadioOperationCustom.asObservable()
是有原因的 — 届时大部分功能将无法使用。
你可以做的是在安排你的CustomReadOperation
之前执行RxBleConnection.discoverServices()
,并在构造函数中传递从RxBleDeviceServices
检索到的BluetoothGattCharacteristic
。所以不要这样:
public void customRead()
{
if (isConnected()) {
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.queue(new CustomReadOperation(rxBleConnection, UUID_READ_CHARACTERISTIC)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
configureMvpView.showList(bytes);
}, this::onRunCustomFailure);
}
}
你会得到类似的东西:
public void customRead()
{
if (isConnected()) {
connectionObservable
.flatMap(RxBleConnection::discoverServices, (rxBleConnection, services) ->
services.getCharacteristic(UUID_READ_CHARACTERISTIC)
.flatMap(characteristic -> rxBleConnection.queue(new CustomReadOperation(characteristic)))
)
.flatMap(observable -> observable)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
configureMvpView.showList(bytes);
}, this::onRunCustomFailure);
}
}
编辑(澄清):
您的 CustomReadOperation
的构造函数应该如下所示:
CustomReadOperation(BluetoothGattCharacteristic characteristic) {
this.characteristic = characteristic;
}
因此您不必在 CustomReadOperation
中使用 this.rxBleConnection.getCharacteristic(UUID)
并直接使用 bluetoothGatt.readCharacteristic(this.characteristic)
.
编辑 2:
更改这两行:
return connection.getCharacteristic(characteristicUuid)
.flatMap(characteristic -> readAndObserve(characteristic, bluetoothGatt, rxBleGattCallback))
至此(其余同):
return readAndObserve(this.characteristic, bluetoothGatt, rxBleGattCallback)
我正在使用 RxAndroidBle 库处理 BLE 连接,并从我的 android gatt 客户端应用程序 reading/writing 到 GATT 服务器。我已按照 on github.
提供的示例应用程序进行操作我面临的问题是我的 GATT 服务器是 运行 在 Intel Edison 上,它只支持 80 的 MTU 大小。它以块的形式发送数据,我应该多次读取特征值直到我遇到了一个特殊字符,比如 '/END' 。我已经尝试过自定义读取操作示例,它应该每 250 毫秒读取 5 次。
private static class CustomReadOperation implements RxBleRadioOperationCustom<byte[]> {
private RxBleConnection connection;
private UUID characteristicUuid;
CustomReadOperation(RxBleConnection connection, UUID characteristicUuid) {
this.connection = connection;
this.characteristicUuid = characteristicUuid;
}
/**
* Reads a characteristic 5 times with a 250ms delay between each. This is easily achieve without
* a custom operation. The gain here is that only one operation goes into the RxBleRadio queue
* eliminating the overhead of going on & out of the operation queue.
*/
@NonNull
@Override
public Observable<byte[]> asObservable(BluetoothGatt bluetoothGatt,
RxBleGattCallback rxBleGattCallback,
Scheduler scheduler) throws Throwable {
return connection.getCharacteristic(characteristicUuid)
.flatMap(characteristic -> readAndObserve(characteristic, bluetoothGatt, rxBleGattCallback))
.subscribeOn(scheduler)
.takeFirst(readResponseForMatchingCharacteristic())
.map(byteAssociation -> byteAssociation.second)
.repeatWhen(notificationHandler -> notificationHandler.take(5).delay(250, TimeUnit.MILLISECONDS));
}
@NonNull
private Observable<ByteAssociation<UUID>> readAndObserve(BluetoothGattCharacteristic characteristic,
BluetoothGatt bluetoothGatt,
RxBleGattCallback rxBleGattCallback) {
Observable<ByteAssociation<UUID>> onCharacteristicRead = rxBleGattCallback.getOnCharacteristicRead();
return Observable.create(emitter -> {
Subscription subscription = onCharacteristicRead.subscribe(emitter);
emitter.setCancellation(subscription::unsubscribe);
try {
final boolean success = bluetoothGatt.readCharacteristic(characteristic);
if (!success) {
throw new BleGattCannotStartException(bluetoothGatt, BleGattOperationType.CHARACTERISTIC_READ);
}
} catch (Throwable throwable) {
emitter.onError(throwable);
}
}, Emitter.BackpressureMode.BUFFER);
}
private Func1<ByteAssociation<UUID>, Boolean> readResponseForMatchingCharacteristic() {
return uuidByteAssociation -> uuidByteAssociation.first.equals(characteristicUuid);
}
}
我是这样称呼它的
public void customRead()
{
if (isConnected()) {
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.queue(new CustomReadOperation(rxBleConnection, UUID_READ_CHARACTERISTIC)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
configureMvpView.showList(bytes);
}, this::onRunCustomFailure);
}
}
而且我没有使用此代码从服务器获取任何数据。 但是,如果我尝试像这样的简单读取操作
public void readInfo() {
if (isConnected()) {
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(UUID_READ_CHARACTERISTIC))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
// parse data
configureMvpView.showWifiList(bytes);
}, this::onReadFailure);
}
}
我得到了第一个数据块,但我需要读取其余数据。
我不是很精通 RxJava。所以可能有一种简单的方法可以做到这一点,但任何建议或帮助都会很好。
这是我的 prepareConnectionObservable
private Observable<RxBleConnection> prepareConnectionObservable() {
return bleDevice
.establishConnection(false)
.takeUntil(disconnectTriggerSubject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnUnsubscribe(this::clearSubscription)
.compose(this.bindToLifecycle())
.compose(new ConnectionSharingAdapter());
}
我打电话
connectionObservable.subscribe(this::onConnectionReceived, this::onConnectionFailure);
我调用 CustomRead 的 onConnectionReceived。
您没有显示 connectionObservable
是如何创建的,我不知道在执行上述代码之前是否对该连接进行了任何其他操作。
我的猜测是,如果您查看应用程序的日志,您会发现无线电处理队列开始执行您的 CustomReadOperation
作为连接后的第一个操作。在您的自定义操作中,您正在调用 RxBleConnection.getCharacteristic(UUID)
尝试执行 .discoverServices()
(在无线电队列中安排 RxBleRadioOperationDiscoverServices
)。问题是无线电队列已经在执行您的 CustomReadOperation
并且在完成之前不会发现服务。
RxBleConnection
未传递给 RxBleRadioOperationCustom.asObservable()
是有原因的 — 届时大部分功能将无法使用。
你可以做的是在安排你的CustomReadOperation
之前执行RxBleConnection.discoverServices()
,并在构造函数中传递从RxBleDeviceServices
检索到的BluetoothGattCharacteristic
。所以不要这样:
public void customRead()
{
if (isConnected()) {
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.queue(new CustomReadOperation(rxBleConnection, UUID_READ_CHARACTERISTIC)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
configureMvpView.showList(bytes);
}, this::onRunCustomFailure);
}
}
你会得到类似的东西:
public void customRead()
{
if (isConnected()) {
connectionObservable
.flatMap(RxBleConnection::discoverServices, (rxBleConnection, services) ->
services.getCharacteristic(UUID_READ_CHARACTERISTIC)
.flatMap(characteristic -> rxBleConnection.queue(new CustomReadOperation(characteristic)))
)
.flatMap(observable -> observable)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bytes -> {
configureMvpView.showList(bytes);
}, this::onRunCustomFailure);
}
}
编辑(澄清):
您的 CustomReadOperation
的构造函数应该如下所示:
CustomReadOperation(BluetoothGattCharacteristic characteristic) {
this.characteristic = characteristic;
}
因此您不必在 CustomReadOperation
中使用 this.rxBleConnection.getCharacteristic(UUID)
并直接使用 bluetoothGatt.readCharacteristic(this.characteristic)
.
编辑 2: 更改这两行:
return connection.getCharacteristic(characteristicUuid)
.flatMap(characteristic -> readAndObserve(characteristic, bluetoothGatt, rxBleGattCallback))
至此(其余同):
return readAndObserve(this.characteristic, bluetoothGatt, rxBleGattCallback)