Search code examples
androidkotlinbluetooth-lowenergyandroid-blerxandroidble

How do I format a byte array to be sent to a characteristic on a BLE device?


I connect to my device and attempt to write to its characteristic:

scanSubscription = rxBleClient.scanBleDevices(
  ScanSettings.Builder()
  // .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // change if needed
  // .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
  .build()
  // add filters if needed
 )
 .filter {
  scanResult -> scanResult.bleDevice.name == "MyDevice"
 } // Filter for devices named MyDevice
 .subscribe({
  scanResult ->
  // Print list of available devices
  println("Scan Result: ${scanResult.bleDevice.bluetoothDevice.name}")
  val charUUID = UUID.fromString("49535343-1e4d-4bd9-ba61-23c647249616")
  println("UUID: $charUUID")

  // Connect to MyDevice
  val macAddress = scanResult.bleDevice.bluetoothDevice.address //34:81:F4:55:04:9A
  println("MAC Address: $macAddress")
  val rxBleDevice = rxBleClient.getBleDevice(macAddress)

  val charset = Charsets.UTF_8
  val bytesToWrite = "cmdl000".toByteArray(charset)
  println("Bytes: $bytesToWrite")

  rxBleDevice.establishConnection(false)
  .flatMapSingle {
   rxBleConnection -> rxBleConnection.writeCharacteristic(charUUID, bytesToWrite)
  }

 }, {
  throwable ->
  // Handle an error here.
  println("Scan Error: $throwable")
 })

Output:

I/System.out: Scan Result: MyDevice
    UUID: 49535343-1e4d-4bd9-ba61-23c647249616
    MAC Address: 34:81:F4:55:04:9A
I/System.out: Bytes: [B@4973078

I'm not sure if the byte array that I'm sending to my characteristic is in the right format. When I print it I get the following and my device does not respond as I intend it to. Is this correct?


Solution

  • Judging from the code above the problem you encounter is pretty trivial. All Observable objects need to be subscribed to be actually executed. You have correctly subscribed to the scanning flow but not for connecting.

      rxBleDevice.establishConnection(false)
        .flatMapSingle {
          rxBleConnection -> rxBleConnection.writeCharacteristic(charUUID, bytesToWrite)
        }
        .subscribe(
          { /* done */ },
          { /* encountered error */ }
        )
    

    You could wire-up the whole flow (scanning, connecting, writing characteristic) with a single .subscribe(). It could look like:

    val charUUID = UUID.fromString("49535343-1e4d-4bd9-ba61-23c647249616")
    val bytesToWrite = "cmdl000".toByteArray(charset)
    
    subscription = rxBleClient.scanBleDevices(
        ScanSettings.Builder().build(),
        ScanFilter.Builder().setDeviceName("MyDevice").build() // Filter for devices named MyDevice
    )
        .take(1) // stop the scan when a matching device will be scanned for the first time
        .flatMap {
            val device = it.bleDevice
            println("MAC Address: ${device.macAddress}")
            device.establishConnection(false)
                .flatMapSingle { rxBleConnection -> rxBleConnection.writeCharacteristic(charUUID, bytesToWrite) }
                .take(1) // disconnect after the write
        }
        .subscribe(
            { /* written */ },
            { throwable ->
                // Handle an error here.
                println("Scan Error: $throwable")
            }
        )