param( [Parameter(Mandatory=$true)][string]$Port, [Parameter(Mandatory=$true)][string]$AppBin ) $ErrorActionPreference = 'Stop' $partitionCsv = (Resolve-Path (Join-Path $PSScriptRoot '..\partitions.csv')).Path $rows = @(Get-Content -LiteralPath $partitionCsv -Encoding UTF8 | Where-Object { $_.Trim() -and -not $_.TrimStart().StartsWith('#') } | ConvertFrom-Csv -Header Name,Type,SubType,Offset,Size,Flags | Where-Object { $_.Name.Trim() -eq 'ota_0' -and $_.Type.Trim() -eq 'app' -and $_.SubType.Trim() -eq 'ota_0' }) if ($rows.Count -ne 1 -or $rows[0].Offset.Trim() -ne '0x70000' -or $rows[0].Size.Trim() -ne '10M') { throw 'Expected exactly one ota_0 app partition at 0x70000, size 10M in partitions.csv.' } $app = (Resolve-Path -LiteralPath $AppBin).Path $length = (Get-Item -LiteralPath $app).Length if ($length -le 0 -or $length -gt 10MB) { throw "Image length $length is outside ota_0 partition." } $image = [System.IO.File]::ReadAllBytes($app) if ($image[0] -ne 0xe9) { throw 'Not an ESP application binary (image magic mismatch).' } $idf = if ($env:IDF_PATH) { $env:IDF_PATH } else { 'E:\Espressif\frameworks\esp-idf-v5.5.4' } $py = if ($env:IDF_PYTHON_ENV_PATH) { Join-Path $env:IDF_PYTHON_ENV_PATH 'Scripts\python.exe' } else { 'E:\Espressif\python_env\idf5.5_py3.11_env\Scripts\python.exe' } if (-not (Test-Path -LiteralPath $py)) { throw "ESP-IDF Python not found: $py" } $esptool = Join-Path $idf 'components\esptool_py\esptool\esptool.py' if (-not (Test-Path -LiteralPath $esptool)) { throw "esptool.py not found: $esptool" } # USB-Serial/JTAG: idf.py app-flash uploads a stub then switches baud and fails # with Invalid head of packet (0x50). ROM loader at 2000000 writes only ota_0. & $py $esptool --chip esp32p4 -p $Port -b 2000000 --no-stub --before default_reset --after hard_reset write_flash --flash_mode dio --flash_freq 80m --flash_size 32MB 0x70000 $app if ($LASTEXITCODE -ne 0) { throw "Flash failed with exit code $LASTEXITCODE" }