簡體   English   中英

RxAndroidBle 在 Kotlin 中寫入時等待外圍設備的響應(不是長寫入)

[英]RxAndroidBle waiting for response from peripheral when write on it (not long write) in Kotlin

我正在嘗試使用 RxAndroidBle 寫入 Android 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 連接,應用程序在 .network (HTTP) 上執行另一項獨立工作。 然后需要再次連接,但這次需要向外設寫入 JSON 信息,設備會分析 JSON 並給出我需要的一些答案作為回報; 如何實現寫入等待外圍設備的響應? 是否有必要為 JSON 進行長寫,因為我在連接上分配了 MTU? 我在 Kotlin 中在 Repository 模式下開發這個。

發送的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:找不到具有特征 UUID 4f4a4554-4520-4341-4c4f-520001000002 的客戶端特征配置描述符(代碼 2)

Kotlin 有幫助嗎?

非常感謝!!

當我運行我的代碼時,它連接但顯示此錯誤:

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

您可以通過兩種方式解決此問題:

  1. 更改您的外設代碼以包含關於您要使用通知的特性的客戶端特性配置描述符——這是首選方法,因為它會使外設符合藍牙規范
  2. 設置完全不設置 CCCD 值的通知時使用COMPAT模式

如何清理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);
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM