Fix EspFlasher: connection leak (try/finally), progress mapping, use UsbUtils

This commit is contained in:
eddy 2026-07-26 12:51:02 +02:00
commit 2a86fa5d39

View 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) }
}
}