RxAndroidBle 在 Kotlin 中写入(不是长写入)时等待外设的响应

问题描述 投票:0回答:1

我正在尝试使用 RxAndroidBleAndroid Kotlin 中写入外围设备。应用程序写入外设,如果此写入请求成功,则外设会做出响应,即 根据对发送给外设的信息的评估,如果是期望的信息,则外设向应用程序发送响应,如果不是期望的信息,则外设以不同的响应进行响应;总而言之,这是一个非常类似于通过 POST 的 HTTP 请求的场景,发送信息,如果信息满足要求,服务器会返回状态。我已经成功连接并通过以下方式从外围设备读取信息:

override fun connectDeviceToGetInfoHardwareByBle(mac: String): Observable<Resource<HardwareInfoResponse>> {
        val device: RxBleDevice = bleClient.getBleDevice(mac)
        return Observable.defer {
            device.bluetoothDevice.createBond()// it is a blocking function
            device.establishConnection(false) // return Observable<RxBleConnection>
        }
        .delay(5, TimeUnit.SECONDS)
        .flatMapSingle { connection ->
            connection.requestMtu(515)
            .flatMap {
                Single.just(connection)
            }
        }
        .flatMapSingle {
            it.readCharacteristic(UUID.fromString(GET_HARDWARE_INFORMATION_CHARACTERISTIC))
            .map { byteArray ->
                evaluateHardwareInfoResponse(byteArray = byteArray)
            }
        }
        .map {
            Resource.success(data = it)
        }
        .take(1)
        .onErrorReturn {
            Timber.i("Rointe Ble* Error getting ble information. {$it}")
            Resource.error(data = null, message = it.message.toString())
        }
        .doOnError {
            Timber.i("Rointe Ble*","Error getting ble information."+it)
        }
        .subscribeOn(ioScheduler)
        .observeOn(uiScheduler)
    }

如您所见,外设需要MTU,它满足了我的需要。在该响应之后,我关闭该 BLE 连接,并且应用程序在网络 (HTTP) 上执行另一项独立工作。然后需要再次连接,但这一次需要将 JSON 信息写入外设,设备会分析该 JSON 并给出一些我需要的答案作为返回;如何实现等待外设响应的写入?由于我在连接上分配了 MTU,是否有必要对 JSON 进行长写入?我正在 Kotlin 中的存储库模式下开发它。

发送的JSON是这样的:

{
"data": {

"id_hardware": "[ID_HARDWARE]",
"product_brand": <value>,
"product_type": <value>,
"product_model": <value>,
"nominal_power": <value>,
"industrialization_process_date": <value>,
"platform_api_path": "[Host_API_REST]",
"platform_streaming_path": "[Host_STREAMING]",
"updates_main_path": "[Host_UPDATES]",
"updates_alternative_path": "[Host_ALTERNATIVE_UPDATES]",
"check_updates_time": <value>,
"check_updates_day": <value>,
"auth_main_path": "[Host_AUTHORIZATION]",
"auth_alternative_path": "[Host_BACKUP_AUTHORIZATION]",
"analytics_path": "[Host_ANALYTICS]",
"idToken": "[ID_TOKEN]",
"refreshToken": "[REFRESH_TOKEN]",
"expiresIn": "3600",
"apiKey": "[API_KEY]",
"factory_wifi_ssid": "[FACTORY_WIFI_SSID]",
"factory_wifi_security_type": "[FACTORY_WIFI_TYPE]",
"factory_wifi_passphrase": "[FACTORY_WIFI_PASS]",
"factory_wifi_dhcp": 1,
"factory_wifi_device_ip": "[IPv4]",
"factory_wifi_subnet_mask": "[SubNetMask_IPv4]",
"factory_wifi_gateway": "[IPv4]"

},
"factory_version": 1,
"crc": ""
}

外围设备分析该 JSON 并根据发送的 JSON 给我一些答案。

现在,我尝试进行写入并期待响应的方式是这样的:

private fun setupNotifications(connection: RxBleConnection): Observable<Observable<ByteArray>> =
            connection.setupNotification(UUID.fromString(SET_FACTORY_SETTINGS_CHARACTERISTIC))

    private fun performWrite(connection: RxBleConnection, notifications: Observable<ByteArray>, data: ByteArray): Observable<ByteArray> {
        return connection.writeCharacteristic(UUID.fromString(SET_FACTORY_SETTINGS_CHARACTERISTIC), data).toObservable()
    }

    override fun connectDeviceToWriteFactorySettingsByBle(mac: String, data: ByteArray): Observable<Resource<HardwareInfoResponse>> {
        val device: RxBleDevice = bleClient.getBleDevice(mac)
        return Observable.defer {
            //device.bluetoothDevice.createBond()// it is a blocking function
            device.establishConnection(false) // return Observable<RxBleConnection>
        }
        .delay(5, TimeUnit.SECONDS)
        .flatMapSingle { connection ->
            connection.requestMtu(515)
                .flatMap {
                    Single.just(connection)
                }
        }
        .flatMap(
            { connection -> setupNotifications(connection).delay(5, TimeUnit.SECONDS) },
            { connection, deviceCallbacks -> performWrite(connection, deviceCallbacks, data) }
        )
        .flatMap {
            it
        }
        //.take(1) // after the successful write we are no longer interested in the connection so it will be released
        .map {
            Timber.i("Rointe Ble: Result write: ok ->{${it.toHex()}}")
            Resource.success(data = evaluateHardwareInfoResponse(it))
        }
        //.take(1)
        .onErrorReturn {
            Timber.i("Rointe Ble: Result write: failed ->{${it.message.toString()}}")
            Resource.error(data = HardwareInfoResponse.NULL_HARDWARE_INFO_RESPONSE, message = "Error write on device.")
        }
        .doOnError {
            Timber.i("Rointe Ble*","Error getting ble information."+it)
        }
        //.subscribeOn(ioScheduler)
        .observeOn(uiScheduler)
    }

可以看到,MTU 已协商为最大值,并且发送了单个数据包(显示的 json 文件)。

当我运行代码时,它会连接但显示此错误:

com.polidea.rxandroidble2.exceptions.BleCannotSetCharacteristicNotificationException: 找不到客户端特征配置描述符(代码 2) 特征 UUID 4f4a4554-4520-4341-4c4f-520001000002

对 Kotlin 有帮助吗?

android kotlin bluetooth-lowenergy rx-android rxandroidble
1个回答
1
投票

当我运行代码时,它会连接但显示此错误:

com.polidea.rxandroidble2.exceptions.BleCannotSetCharacteristicNotificationException: 找不到客户端特征配置描述符(代码 2) 特征 UUID 4f4a4554-4520-4341-4c4f-520001000002

您可以通过两种方式解决此问题:

  1. 更改您的外设代码,以在您想要使用通知的特征上包含客户端特征配置描述符 - 这是首选方法,因为它将使外设符合蓝牙规范
  2. 设置通知时使用
    COMPAT
    模式,根本不设置CCCD值

如何清理UUID的特征缓存?发生的情况是库在缓存中记住了可能是最后注册的 UUID。我如何清理这个缓存?

可以通过 使用

BluetoothGatt#refresh
并随后获取 新的一组服务来清除缓存,这将允许绕过库
UUID
帮助程序 — 您需要使用接受
BluetoothGattCharacteristic
而不是
UUID 的函数
.

刷新代码

BluetoothGatt
:

RxBleCustomOperation<Void> bluetoothGattRefreshCustomOp = (bluetoothGatt, rxBleGattCallback, scheduler) -> {
    try {
        Method bluetoothGattRefreshFunction = bluetoothGatt.getClass().getMethod("refresh");
        boolean success = (Boolean) bluetoothGattRefreshFunction.invoke(bluetoothGatt);
        if (!success) return Observable.error(new RuntimeException("BluetoothGatt.refresh() returned false"));
        return Observable.<Void>empty().delay(500, TimeUnit.MILLISECONDS);
    } catch (NoSuchMethodException e) {
        return Observable.error(e);
    } catch (IllegalAccessException e) {
        return Observable.error(e);
    } catch (InvocationTargetException e) {
        return Observable.error(e);
    }
};

发现绕过库缓存的服务的代码:

RxBleCustomOperation<List<BluetoothGattService>> discoverServicesCustomOp = (bluetoothGatt, rxBleGattCallback, scheduler) -> {
    boolean success = bluetoothGatt.discoverServices();
    if (!success) return Observable.error(new RuntimeException("BluetoothGatt.discoverServices() returned false"));
    return rxBleGattCallback.getOnServicesDiscovered()
            .take(1) // so this RxBleCustomOperation will complete after the first result from BluetoothGattCallback.onServicesDiscovered()
            .map(RxBleDeviceServices::getBluetoothGattServices);
};
© www.soinside.com 2019 - 2024. All rights reserved.