Fix EspFlasher: connection leak (try/finally), progress mapping, use UsbUtils
This commit is contained in:
parent
2e08f9a340
commit
2a86fa5d39
1 changed files with 134 additions and 0 deletions
134
android/app/src/main/java/com/eddy/atomrelay/EspFlasher.kt
Normal file
134
android/app/src/main/java/com/eddy/atomrelay/EspFlasher.kt
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package com.eddy.atomrelay
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.usb.UsbConstants
|
||||
import android.hardware.usb.UsbDevice
|
||||
import android.hardware.usb.UsbManager
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import org.dkaukov.esp32.core.EspFlasherApi
|
||||
import org.dkaukov.esp32.io.ProgressCallback
|
||||
import java.io.IOException
|
||||
|
||||
class EspFlasher(private val context: Context) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "EspFlasher"
|
||||
}
|
||||
|
||||
interface FlashCallback {
|
||||
fun onProgress(percent: Int, message: String)
|
||||
fun onComplete()
|
||||
fun onError(message: String)
|
||||
}
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
fun flashDevice(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) {
|
||||
Thread {
|
||||
try {
|
||||
doFlash(usbManager, device, callback)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Flash failed", e)
|
||||
handler.post { callback.onError(e.message ?: "Onbekende fout") }
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private class FlashStep(
|
||||
val name: String,
|
||||
val address: Int,
|
||||
val data: ByteArray,
|
||||
val baseProgress: Int,
|
||||
val range: Int,
|
||||
val compress: Boolean
|
||||
)
|
||||
|
||||
private fun doFlash(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) {
|
||||
Thread.sleep(500)
|
||||
|
||||
val connection = usbManager.openDevice(device)
|
||||
?: throw IOException("Kan USB-apparaat niet openen")
|
||||
|
||||
try {
|
||||
val intf = UsbUtils.findCdcDataInterface(device)
|
||||
?: throw IOException("Geen CDC data interface gevonden")
|
||||
|
||||
if (!connection.claimInterface(intf, true)) {
|
||||
throw IOException("Kan USB interface niet claimen")
|
||||
}
|
||||
|
||||
val epOut = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_OUT)
|
||||
?: throw IOException("Geen USB OUT endpoint gevonden")
|
||||
val epIn = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_IN)
|
||||
?: throw IOException("Geen USB IN endpoint gevonden")
|
||||
|
||||
postProgress(callback, 0, "Verbinden met ESP bootloader...")
|
||||
|
||||
val transport = UsbSerialTransport(connection, epOut, epIn)
|
||||
|
||||
val assets = context.assets
|
||||
val bootloader = assets.open("firmware/bootloader.bin").readBytes()
|
||||
val partitions = assets.open("firmware/partitions.bin").readBytes()
|
||||
val bootApp0 = assets.open("firmware/boot_app0.bin").readBytes()
|
||||
val firmware = assets.open("firmware/firmware.bin").readBytes()
|
||||
|
||||
val totalSize = bootloader.size + partitions.size + bootApp0.size + firmware.size
|
||||
|
||||
val steps = listOf(
|
||||
FlashStep("Bootloader", 0x0, bootloader, 30, 10, false),
|
||||
FlashStep("Partitie tabel", 0x8000, partitions, 40, 10, false),
|
||||
FlashStep("Boot app0", 0xe000, bootApp0, 50, 10, false),
|
||||
FlashStep("Firmware", 0x10000, firmware, 60, 35, true)
|
||||
)
|
||||
|
||||
postProgress(callback, 5, "Sync met bootloader...")
|
||||
val api = EspFlasherApi.connect(transport)
|
||||
.withCallBack(object : ProgressCallback {
|
||||
override fun onProgress(pct: Float) {
|
||||
Log.d(TAG, "Lib progress: $pct%")
|
||||
}
|
||||
override fun onInfo(value: String) {
|
||||
Log.d(TAG, "Info: $value")
|
||||
}
|
||||
})
|
||||
|
||||
postProgress(callback, 10, "Chip detecteren...")
|
||||
val detected = api.chipDetect()
|
||||
val chipId = detected.getChipId()
|
||||
Log.i(TAG, "Detected chip: $chipId")
|
||||
postProgress(callback, 15, "Chip: ${chipId.readableName}")
|
||||
|
||||
postProgress(callback, 20, "Stub laden...")
|
||||
val stub = detected.loadStub()
|
||||
|
||||
postProgress(callback, 25, "Flash wissen...")
|
||||
stub.eraseFlash()
|
||||
|
||||
for (step in steps) {
|
||||
val sizeStr = if (step.data.size > 1024) "${step.data.size / 1024}KB" else "${step.data.size} bytes"
|
||||
postProgress(callback, step.baseProgress, "${step.name} schrijven ($sizeStr)...")
|
||||
|
||||
val writeStub = if (step.compress) stub.withCompression(true) else stub.withCompression(false)
|
||||
writeStub.writeFlash(step.address, step.data)
|
||||
|
||||
val endProgress = step.baseProgress + step.range
|
||||
postProgress(callback, endProgress.coerceAtMost(95), "${step.name} geschreven")
|
||||
}
|
||||
|
||||
postProgress(callback, 95, "ESP resetten...")
|
||||
stub.reset()
|
||||
|
||||
postProgress(callback, 100, "Firmware geflashed!")
|
||||
handler.post { callback.onComplete() }
|
||||
} finally {
|
||||
connection.close()
|
||||
}
|
||||
}
|
||||
|
||||
private fun postProgress(callback: FlashCallback, percent: Int, message: String) {
|
||||
Log.d(TAG, "Progress $percent%: $message")
|
||||
handler.post { callback.onProgress(percent, message) }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue