From 87fe18918572f965ec23f33aeecae68d7439a719 Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 23 Jan 2020 10:39:54 -0800 Subject: [PATCH] sw update kinda works --- TODO.md | 3 ++ .../java/com/geeksville/mesh/MainActivity.kt | 5 ++- .../geeksville/mesh/SoftwareUpdateService.kt | 40 +++++++++++++------ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/TODO.md b/TODO.md index d4355cad..761b7706 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,9 @@ * test reg reading/writing directly via bt to device * fix bluetooth update +* refactor sw update code to share with my other bluetooth service * get signal running under debugger +* handle failures in onCharWrite, instead of logAssert - because they can happen if device goes away * DONE add broadcasters for use by signal (node changes and packet received) * make test implementation of server (doesn't use bluetooth) * make compose based access show mesh state @@ -42,4 +44,5 @@ Don't leave device discoverable. Don't let unpaired users do thing with device * DONE add crash reporting * DONE add analytics (make them optional) * make frontend using https://developer.android.com/jetpack/compose/tutorial +* change bluetooth mtu length to 512 (default is only 20) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index cfcdc555..79f92359 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -105,8 +105,9 @@ class MainActivity : AppCompatActivity(), Logging { Button(text = "Start scan", onClick = { if (bluetoothAdapter != null) { - SoftwareUpdateService.enqueueWork( - this@MainActivity, + // Note: We don't want this service to die just because our activity goes away (because it is doing a software update) + // So we use the application context instead of the activity + SoftwareUpdateService.enqueueWork(applicationContext, SoftwareUpdateService.scanDevicesIntent ) } diff --git a/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt b/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt index b4778007..412b01e7 100644 --- a/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt +++ b/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt @@ -36,22 +36,24 @@ class SoftwareUpdateService : JobIntentService(), Logging { bluetoothManager.adapter!! } + fun startUpdate() { info("starting update") firmwareStream = assets.open("firmware.bin") firmwareCrc.reset() + firmwareNumSent = 0 + firmwareSize = firmwareStream.available() - // Start the update by writing the # of bytes in the image - val numBytes = firmwareStream.available() - logAssert(totalSizeDesc.setValue(numBytes, BluetoothGattCharacteristic.FORMAT_UINT32, 0)) - logAssert(updateGatt.writeCharacteristic(totalSizeDesc)) + // we begin by setting our MTU size as high as it can go + logAssert(updateGatt.requestMtu(512)) } // Send the next block of our file to the device fun sendNextBlock() { - info("sending next block") - if (firmwareStream.available() > 0) { - var blockSize = 512 + + if (firmwareNumSent < firmwareSize) { + info("sending block ${ firmwareNumSent * 100 / firmwareSize }%") + var blockSize = 512-3 // Max size MTU excluding framing if (blockSize > firmwareStream.available()) blockSize = firmwareStream.available() @@ -64,9 +66,12 @@ class SoftwareUpdateService : JobIntentService(), Logging { // updateGatt.beginReliableWrite() dataDesc.value = buffer logAssert(updateGatt.writeCharacteristic(dataDesc)) + firmwareNumSent += blockSize } else { // We have finished sending all our blocks, so post the CRC so our state machine can advance - logAssert(crc32Desc.setValue(firmwareCrc.value.toInt(), BluetoothGattCharacteristic.FORMAT_UINT32, 0)) + val c = firmwareCrc.value + info("Sent all blocks, crc is $c") + logAssert(crc32Desc.setValue(c.toInt(), BluetoothGattCharacteristic.FORMAT_UINT32, 0)) logAssert(updateGatt.writeCharacteristic(crc32Desc)) } } @@ -125,6 +130,15 @@ class SoftwareUpdateService : JobIntentService(), Logging { enqueueWork(this@SoftwareUpdateService, startUpdateIntent) } + override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { + debug("onMtuChanged $mtu") + logAssert(status == BluetoothGatt.GATT_SUCCESS) + + // Start the update by writing the # of bytes in the image + logAssert(totalSizeDesc.setValue(firmwareSize, BluetoothGattCharacteristic.FORMAT_UINT32, 0)) + logAssert(updateGatt.writeCharacteristic(totalSizeDesc)) + } + // Result of a characteristic read operation override fun onCharacteristicRead( gatt: BluetoothGatt, @@ -245,13 +259,11 @@ class SoftwareUpdateService : JobIntentService(), Logging { override fun onHandleWork(intent: Intent) { // We have received work to do. The system or framework is already // holding a wake lock for us at this point, so we can just go. - info("Executing work: $intent") + debug("Executing work: $intent") var label = intent.getStringExtra("label") if (label == null) { label = intent.toString() } - toast("Executing: $label") - when (intent.action) { scanDevicesIntent.action -> connectToTestDevice() // FIXME scanLeDevice(true) startUpdateIntent.action -> startUpdate() @@ -259,14 +271,14 @@ class SoftwareUpdateService : JobIntentService(), Logging { else -> logAssert(false) } - info( + debug( "Completed service @ " + SystemClock.elapsedRealtime() ) } override fun onDestroy() { super.onDestroy() - toast("All work complete") + // toast("All work complete") } val mHandler = Handler() @@ -316,6 +328,8 @@ class SoftwareUpdateService : JobIntentService(), Logging { lateinit var updateResultDesc: BluetoothGattCharacteristic lateinit var firmwareStream: InputStream val firmwareCrc = CRC32() + var firmwareNumSent = 0 + var firmwareSize = 0 /** * Convenience method for enqueuing work in to this service.