From f9c1ac8cd2d139b30fcabea2843ab53fb0b867f5 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 13 Jun 2020 16:21:26 -0700 Subject: [PATCH] Fix back to back writes to not overwrite BLE characterstic. Fixes the "sending while device was asleep bug" --- .../mesh/service/BluetoothInterface.kt | 3 +- .../geeksville/mesh/service/SafeBluetooth.kt | 12 +++- .../mesh/service/SoftwareUpdateService.kt | 67 ++++++++++++++----- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt b/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt index 23195a3f9..c52b0133d 100644 --- a/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt +++ b/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt @@ -217,9 +217,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String // Note: we generate a new characteristic each time, because we are about to // change the data and we want the data stored in the closure val toRadio = getCharacteristic(uuid) - toRadio.value = a - s.asyncWriteCharacteristic(toRadio) { r -> + s.asyncWriteCharacteristic(toRadio, a) { r -> try { r.getOrThrow() debug("write of ${a.size} bytes completed") diff --git a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt index 1ff591646..647ec4a54 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt @@ -622,19 +622,25 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD private fun queueWriteCharacteristic( c: BluetoothGattCharacteristic, + v: ByteArray, cont: Continuation ) = queueWork("writeC ${c.uuid}", cont) { currentReliableWrite = null + c.value = v gatt!!.writeCharacteristic(c) } fun asyncWriteCharacteristic( c: BluetoothGattCharacteristic, + v: ByteArray, cb: (Result) -> Unit - ) = queueWriteCharacteristic(c, CallbackContinuation(cb)) + ) = queueWriteCharacteristic(c, v, CallbackContinuation(cb)) - fun writeCharacteristic(c: BluetoothGattCharacteristic): BluetoothGattCharacteristic = - makeSync { queueWriteCharacteristic(c, it) } + fun writeCharacteristic( + c: BluetoothGattCharacteristic, + v: ByteArray + ): BluetoothGattCharacteristic = + makeSync { queueWriteCharacteristic(c, v, it) } /** Like write, but we use the extra reliable flow documented here: * https://stackoverflow.com/questions/24485536/what-is-reliable-write-in-ble diff --git a/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt b/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt index c10c3914e..6799630a0 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt @@ -13,6 +13,48 @@ import com.geeksville.util.exceptionReporter import java.util.* import java.util.zip.CRC32 +/** + * Move this somewhere as a generic network byte order function + */ +fun toNetworkByteArray(value: Int, formatType: Int): ByteArray { + + val len: Int = 4 // getTypeLen(formatType) + val mValue = ByteArray(len) + + when (formatType) { + /* BluetoothGattCharacteristic.FORMAT_SINT8 -> { + value = intToSignedBits(value, 8) + mValue.get(offset) = (value and 0xFF).toByte() + } + BluetoothGattCharacteristic.FORMAT_UINT8 -> mValue.get(offset) = + (value and 0xFF).toByte() + BluetoothGattCharacteristic.FORMAT_SINT16 -> { + value = intToSignedBits(value, 16) + mValue.get(offset++) = (value and 0xFF).toByte() + mValue.get(offset) = (value shr 8 and 0xFF).toByte() + } + BluetoothGattCharacteristic.FORMAT_UINT16 -> { + mValue.get(offset++) = (value and 0xFF).toByte() + mValue.get(offset) = (value shr 8 and 0xFF).toByte() + } + BluetoothGattCharacteristic.FORMAT_SINT32 -> { + value = intToSignedBits(value, 32) + mValue.get(offset++) = (value and 0xFF).toByte() + mValue.get(offset++) = (value shr 8 and 0xFF).toByte() + mValue.get(offset++) = (value shr 16 and 0xFF).toByte() + mValue.get(offset) = (value shr 24 and 0xFF).toByte() + } */ + BluetoothGattCharacteristic.FORMAT_UINT32 -> { + mValue[0] = (value and 0xFF).toByte() + mValue[1] = (value shr 8 and 0xFF).toByte() + mValue[2] = (value shr 16 and 0xFF).toByte() + mValue[3] = (value shr 24 and 0xFF).toByte() + } + else -> TODO() + } + return mValue +} + /** * typical flow * @@ -211,14 +253,10 @@ class SoftwareUpdateService : JobIntentService(), Logging { val firmwareSize = firmwareStream.available() // Start the update by writing the # of bytes in the image - logAssert( - totalSizeDesc.setValue( - firmwareSize, - BluetoothGattCharacteristic.FORMAT_UINT32, - 0 - ) + sync.writeCharacteristic( + totalSizeDesc, + toNetworkByteArray(firmwareSize, BluetoothGattCharacteristic.FORMAT_UINT32) ) - sync.writeCharacteristic(totalSizeDesc) // Our write completed, queue up a readback val totalSizeReadback = sync.readCharacteristic(totalSizeDesc) @@ -239,23 +277,18 @@ class SoftwareUpdateService : JobIntentService(), Logging { // slightly expensive to keep reallocing this buffer, but whatever logAssert(firmwareStream.read(buffer) == blockSize) firmwareCrc.update(buffer) - - dataDesc.value = buffer - sync.writeCharacteristic(dataDesc) + + sync.writeCharacteristic(dataDesc, buffer) firmwareNumSent += blockSize } // We have finished sending all our blocks, so post the CRC so our state machine can advance val c = firmwareCrc.value info("Sent all blocks, crc is $c") - logAssert( - crc32Desc.setValue( - c.toInt(), - BluetoothGattCharacteristic.FORMAT_UINT32, - 0 - ) + sync.writeCharacteristic( + crc32Desc, + toNetworkByteArray(c.toInt(), BluetoothGattCharacteristic.FORMAT_UINT32) ) - sync.writeCharacteristic(crc32Desc) // we just read the update result if !0 we have an error val updateResult =