Fix back to back writes to not overwrite BLE characterstic. Fixes

the "sending while device was asleep bug"
This commit is contained in:
geeksville
2020-06-13 16:21:26 -07:00
parent 7ee4aff64c
commit f9c1ac8cd2
3 changed files with 60 additions and 22 deletions

View File

@@ -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")

View File

@@ -622,19 +622,25 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private fun queueWriteCharacteristic(
c: BluetoothGattCharacteristic,
v: ByteArray,
cont: Continuation<BluetoothGattCharacteristic>
) = queueWork("writeC ${c.uuid}", cont) {
currentReliableWrite = null
c.value = v
gatt!!.writeCharacteristic(c)
}
fun asyncWriteCharacteristic(
c: BluetoothGattCharacteristic,
v: ByteArray,
cb: (Result<BluetoothGattCharacteristic>) -> 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

View File

@@ -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 =