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