kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
support spiffs updates over OTA (not yet tested)
for https://github.com/meshtastic/Meshtastic-device/issues/496pull/203/head
rodzic
b854c57aa4
commit
31e0136b2a
|
@ -1,5 +1,8 @@
|
|||
package com.geeksville.mesh.service
|
||||
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
open class BLEException(msg: String) : IOException(msg)
|
||||
open class BLEException(msg: String) : IOException(msg)
|
||||
|
||||
open class BLECharacteristicNotFoundException(uuid: UUID) : BLEException("Can't get characteristic $uuid")
|
|
@ -489,6 +489,6 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
|||
* Get a chracteristic, but in a safe manner because some buggy BLE implementations might return null
|
||||
*/
|
||||
private fun getCharacteristic(uuid: UUID) =
|
||||
bservice.getCharacteristic(uuid) ?: throw BLEException("Can't get characteristic $uuid")
|
||||
bservice.getCharacteristic(uuid) ?: throw BLECharacteristicNotFoundException(uuid)
|
||||
|
||||
}
|
||||
|
|
|
@ -1334,7 +1334,7 @@ class MeshService : Service(), Logging {
|
|||
return 0 // We don't have mynodeinfo yet, so just let the radio eventually assign an ID
|
||||
}
|
||||
|
||||
var firmwareUpdateFilename: String? = null
|
||||
var firmwareUpdateFilename: UpdateFilenames? = null
|
||||
|
||||
/***
|
||||
* Return the filename we will install on the device
|
||||
|
|
|
@ -18,7 +18,12 @@ import java.util.zip.CRC32
|
|||
*/
|
||||
fun toNetworkByteArray(value: Int, formatType: Int): ByteArray {
|
||||
|
||||
val len: Int = 4 // getTypeLen(formatType)
|
||||
val len = when (formatType) {
|
||||
BluetoothGattCharacteristic.FORMAT_UINT8 -> 1
|
||||
BluetoothGattCharacteristic.FORMAT_UINT32 -> 4
|
||||
else -> TODO()
|
||||
}
|
||||
|
||||
val mValue = ByteArray(len)
|
||||
|
||||
when (formatType) {
|
||||
|
@ -44,6 +49,9 @@ fun toNetworkByteArray(value: Int, formatType: Int): ByteArray {
|
|||
mValue.get(offset++) = (value shr 16 and 0xFF).toByte()
|
||||
mValue.get(offset) = (value shr 24 and 0xFF).toByte()
|
||||
} */
|
||||
BluetoothGattCharacteristic.FORMAT_UINT8 ->
|
||||
mValue[0] = (value and 0xFF).toByte()
|
||||
|
||||
BluetoothGattCharacteristic.FORMAT_UINT32 -> {
|
||||
mValue[0] = (value and 0xFF).toByte()
|
||||
mValue[1] = (value shr 8 and 0xFF).toByte()
|
||||
|
@ -55,6 +63,9 @@ fun toNetworkByteArray(value: Int, formatType: Int): ByteArray {
|
|||
return mValue
|
||||
}
|
||||
|
||||
|
||||
data class UpdateFilenames(val appLoad: String?, val spiffs: String?)
|
||||
|
||||
/**
|
||||
* typical flow
|
||||
*
|
||||
|
@ -152,6 +163,8 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
UUID.fromString("4826129c-c22a-43a3-b066-ce8f0d5bacc6") // write crc32, write last - writing this will complete the OTA operation, now you can read result
|
||||
private val SW_UPDATE_RESULT_CHARACTER =
|
||||
UUID.fromString("5e134862-7411-4424-ac4a-210937432c77") // read|notify result code, readable but will notify when the OTA operation completes
|
||||
private val SW_UPDATE_REGION_CHARACTER =
|
||||
UUID.fromString("5e134862-7411-4424-ac4a-210937432c67") // write - used to set the region we are setting (appload vs spiffs)
|
||||
|
||||
private val SW_VERSION_CHARACTER = longBLEUUID("2a28")
|
||||
private val MANUFACTURE_CHARACTER = longBLEUUID("2a29")
|
||||
|
@ -210,28 +223,36 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
false // If we fail parsing our update info
|
||||
}
|
||||
|
||||
/** Return the filename this device needs to use as an update (or null if no update needed)
|
||||
/** Return a Pair of apploadfilename, spiffs filename this device needs to use as an update (or null if no update needed)
|
||||
*/
|
||||
fun getUpdateFilename(
|
||||
context: Context,
|
||||
mfg: String
|
||||
): String? {
|
||||
): UpdateFilenames {
|
||||
val curver = context.getString(R.string.cur_firmware_version)
|
||||
|
||||
val base = "firmware-$mfg-$curver.bin"
|
||||
|
||||
// Check to see if the file exists (some builds might not include update files for size reasons)
|
||||
val firmwareFiles = context.assets.list("firmware") ?: arrayOf()
|
||||
return if (firmwareFiles.contains(base))
|
||||
"firmware/$base"
|
||||
else
|
||||
null
|
||||
|
||||
val appLoad = "firmware-$mfg-$curver.bin"
|
||||
val spiffs = "spiffs-$curver.bin"
|
||||
|
||||
return UpdateFilenames(
|
||||
if (firmwareFiles.contains(appLoad))
|
||||
"firmware/$appLoad"
|
||||
else
|
||||
null,
|
||||
if (firmwareFiles.contains(spiffs))
|
||||
"firmware/$spiffs"
|
||||
else
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
/** Return the filename this device needs to use as an update (or null if no update needed)
|
||||
* No longer used, because we get update info inband from our radio API
|
||||
*/
|
||||
fun getUpdateFilename(context: Context, sync: SafeBluetooth): String? {
|
||||
fun getUpdateFilename(context: Context, sync: SafeBluetooth): UpdateFilenames? {
|
||||
val service = sync.gatt!!.services.find { it.uuid == SW_UPDATE_UUID }!!
|
||||
|
||||
//val hwVerDesc = service.getCharacteristic(HW_VERSION_CHARACTER)
|
||||
|
@ -248,19 +269,66 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
* A public function so that if you have your own SafeBluetooth connection already open
|
||||
* you can use it for the software update.
|
||||
*/
|
||||
fun doUpdate(context: Context, sync: SafeBluetooth, assetName: String) {
|
||||
fun doUpdate(context: Context, sync: SafeBluetooth, assets: UpdateFilenames) {
|
||||
// we must attempt spiffs first, because if we update the appload the device will reboot afterwards
|
||||
try {
|
||||
assets.spiffs?.let { doUpdate(context, sync, it, FLASH_REGION_SPIFFS) }
|
||||
}
|
||||
catch(_: BLECharacteristicNotFoundException) {
|
||||
// If we can't update spiffs (because not supported by target), do not fail
|
||||
errormsg("Ignoring failure to update spiffs on old appload")
|
||||
}
|
||||
assets.appLoad?.let { doUpdate(context, sync, it, FLASH_REGION_APPLOAD) }
|
||||
progress = -1 // success
|
||||
}
|
||||
|
||||
// writable region codes in the ESP32 update code
|
||||
private val FLASH_REGION_APPLOAD = 0
|
||||
private val FLASH_REGION_SPIFFS = 100
|
||||
|
||||
/**
|
||||
* A public function so that if you have your own SafeBluetooth connection already open
|
||||
* you can use it for the software update.
|
||||
*/
|
||||
private fun doUpdate(context: Context, sync: SafeBluetooth, assetName: String, flashRegion: Int = FLASH_REGION_APPLOAD) {
|
||||
try {
|
||||
val g = sync.gatt!!
|
||||
val service = g.services.find { it.uuid == SW_UPDATE_UUID }
|
||||
?: throw BLEException("Couldn't find update service")
|
||||
|
||||
info("Starting firmware update for $assetName")
|
||||
/**
|
||||
* Get a chracteristic, but in a safe manner because some buggy BLE implementations might return null
|
||||
*/
|
||||
fun getCharacteristic(uuid: UUID) =
|
||||
service.getCharacteristic(uuid)
|
||||
?: throw BLECharacteristicNotFoundException(uuid)
|
||||
|
||||
info("Starting firmware update for $assetName, flash region $flashRegion")
|
||||
|
||||
progress = 0
|
||||
val totalSizeDesc = service.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)
|
||||
val dataDesc = service.getCharacteristic(SW_UPDATE_DATA_CHARACTER)
|
||||
val crc32Desc = service.getCharacteristic(SW_UPDATE_CRC32_CHARACTER)
|
||||
val updateResultDesc = service.getCharacteristic(SW_UPDATE_RESULT_CHARACTER)
|
||||
val totalSizeDesc = getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)
|
||||
val dataDesc = getCharacteristic(SW_UPDATE_DATA_CHARACTER)
|
||||
val crc32Desc = getCharacteristic(SW_UPDATE_CRC32_CHARACTER)
|
||||
val updateResultDesc = getCharacteristic(SW_UPDATE_RESULT_CHARACTER)
|
||||
|
||||
/// Try to set the destination region for programming (spiffs vs appload etc)
|
||||
/// Old apploads don't have this feature, but we only fail if the user was trying to set a
|
||||
/// spiffs - otherwise we assume appload.
|
||||
try {
|
||||
val updateRegionDesc = getCharacteristic(SW_UPDATE_REGION_CHARACTER)
|
||||
sync.writeCharacteristic(
|
||||
updateRegionDesc,
|
||||
toNetworkByteArray(flashRegion, BluetoothGattCharacteristic.FORMAT_UINT8)
|
||||
)
|
||||
}
|
||||
catch(ex: BLECharacteristicNotFoundException) {
|
||||
errormsg("Can't set flash programming region (old appload?")
|
||||
if(flashRegion != FLASH_REGION_APPLOAD) {
|
||||
errormsg("Can't set flash programming region (old appload?)")
|
||||
throw ex
|
||||
}
|
||||
warn("Ignoring setting appload flashRegion (old appload?")
|
||||
}
|
||||
|
||||
context.assets.open(assetName).use { firmwareStream ->
|
||||
val firmwareCrc = CRC32()
|
||||
|
@ -320,8 +388,6 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
// We might get SyncContinuation timeout on the final write, assume the device simply rebooted to run the new load and we missed it
|
||||
errormsg("Assuming successful update", ex)
|
||||
}
|
||||
|
||||
progress = -1 // success
|
||||
}
|
||||
} catch (ex: BLEException) {
|
||||
progress = -3
|
||||
|
|
Ładowanie…
Reference in New Issue