honey_the_codewitch

espmon firmware build script

Feb 20th, 2026
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # build_all.ps1 - Build all ESP monitor board configurations in parallel
  2.  
  3. param(
  4.     [int]$MaxParallel = [Environment]::ProcessorCount
  5. )
  6.  
  7. $ErrorActionPreference = "Stop"
  8.  
  9. # Check for IDF_PATH
  10. if (-not $env:IDF_TOOLS_PATH) {
  11.     Write-Host "ERROR: IDF_PATH environment variable not set!" -ForegroundColor Red
  12.     Write-Host "Please install ESP-IDF and run the installer's environment setup."
  13.     exit 1
  14. }
  15.  
  16. Write-Host "Activating ESP-IDF environment..." -ForegroundColor Cyan
  17.  
  18. $espIdfConfigPath = Join-Path $env:IDF_TOOLS_PATH "esp_idf.json"
  19. if (-not (Test-Path $espIdfConfigPath)) {
  20.     Write-Host "ERROR: esp_idf.json not found at $espIdfConfigPath" -ForegroundColor Red
  21.     exit 1
  22. }
  23.  
  24. $espIdfConfig = Get-Content $espIdfConfigPath | ConvertFrom-Json
  25. $idfId = $espIdfConfig.idfSelectedId
  26. $initScript = Join-Path $env:IDF_TOOLS_PATH "Initialize-Idf.ps1"
  27.  
  28. Write-Host "Using ESP-IDF ID: $idfId" -ForegroundColor Gray
  29.  
  30. . $initScript -IdfId $idfId
  31.  
  32. if (-not (Get-Command idf.py -ErrorAction SilentlyContinue)) {
  33.     Write-Host "Failed to activate ESP-IDF environment!" -ForegroundColor Red
  34.     exit 1
  35. }
  36. $commonPath = Join-Path $PSScriptRoot ".."
  37. $commonPath = Join-Path $commonPath "common"
  38.  
  39. $nanopbgenPath = Join-Path $PSScriptRoot "nanopb_generator"
  40. $nanopbgenPath = Join-Path $nanopbgenPath "nanopb_generator.py"
  41.  
  42. #./nanopb_generator/nanopb_generator.py -I ../common espmon.proto -D ../common
  43. Write-Host "Generating nanopb code" -ForegroundColor Gray
  44. & pip install protobuf grpcio-tools
  45. & python $nanopbgenPath -I $commonPath espmon.proto -D $commonPath
  46.  
  47. # Read boards.json
  48. $boardsJsonPath = Join-Path $PSScriptRoot "boards.json"
  49. if (-not (Test-Path $boardsJsonPath)) {
  50.     Write-Host "ERROR: boards.json not found at $boardsJsonPath" -ForegroundColor Red
  51.     exit 1
  52. }
  53.  
  54. $boardsJsonTime = (Get-Item $boardsJsonPath).LastWriteTime
  55. $boardsData = Get-Content $boardsJsonPath | ConvertFrom-Json
  56. $boards = $boardsData.boards
  57.  
  58. # Capture UTC epoch timestamp once for the whole build.
  59. # This is passed to each firmware build as BUILD_TIMESTAMP_UTC so the
  60. # firmware can report an exact UTC epoch value without any date/time parsing.
  61. $buildTimestampUtc = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
  62. Write-Host "Build timestamp (UTC): $buildTimestampUtc" -ForegroundColor Gray
  63.  
  64. Write-Host ""
  65. Write-Host "========================================"
  66. Write-Host "Building $($boards.Count) board configurations in parallel"
  67. Write-Host "Max parallel jobs: $MaxParallel"
  68. Write-Host "========================================"
  69. Write-Host ""
  70.  
  71. # Create firmware output directory
  72. $firmwareDir = Join-Path $PSScriptRoot ".."
  73. $firmwareDir = Join-Path $firmwareDir "EspMon.PortDispatcher"
  74. $firmwareDir = Join-Path $firmwareDir "firmware"
  75.  
  76. if (-not (Test-Path $firmwareDir)) {
  77.     New-Item -ItemType Directory -Path $firmwareDir | Out-Null
  78. }
  79. $pbcCommonPath = Join-Path $commonPath "espmon.pb.c"
  80. $pbcTargetPath = Join-Path $PSScriptRoot "shared"
  81. $pbcTargetPath = Join-Path $pbcTargetPath "main"
  82. if (-not (Test-Path $pbcTargetPath) -or (Get-Item $pbcTargetPath).LastWriteTime -lt (Get-Item $pbcCommonPath).LastWriteTime) {
  83.     Write-Host "Copying protobuf source" -ForegroundColor Gray
  84.     Copy-Item $pbcCommonPath $pbcTargetPath
  85. }
  86.  
  87. # Start parallel builds
  88. $jobs = @()
  89. $scriptRootForJobs = $PSScriptRoot  # Capture it first
  90.  
  91. foreach ($board in $boards) {
  92.     # Wait if we have hit max parallel jobs
  93.     while ((Get-Job -State Running).Count -ge $MaxParallel) {
  94.         Start-Sleep -Milliseconds 100
  95.     }
  96.    
  97.     Write-Host "Starting build for: $($board.name)" -ForegroundColor Cyan
  98.  
  99.     $boardName = $board.name
  100.     $boardSlug = $board.slug
  101.     $boardTarget = $board.target
  102.     $boardId = $board.id
  103.     $boardDiagonalInches = $board.diagonal_inches
  104.    
  105.     $needsClean = $false
  106.     # Write idf_component.yml for this board
  107.     $ymlPath = Join-Path $PSScriptRoot "boards"
  108.     $ymlPath = Join-Path $ymlPath $boardSlug
  109.     $ymlPath = Join-Path $ymlPath "main"
  110.     $ymlPath = Join-Path $ymlPath "idf_component.yml"
  111.     if (-not (Test-Path $ymlPath) -or (Get-Item $ymlPath).LastWriteTime -lt $boardsJsonTime) {
  112.         $ymlLines = [System.Collections.Generic.List[string]]::new()
  113.         $ymlLines.Add("## IDF Component Manager Manifest File")
  114.         $ymlLines.Add("## Auto-generated by build_all.ps1 -- do not edit by hand.")
  115.         $ymlLines.Add("dependencies:")
  116.         $ymlLines.Add("  idf:")
  117.         $ymlLines.Add("    version: '>=5.4.3'")
  118.         # Merge global and board-specific dependencies
  119.         $allDeps = @()
  120.         if ($boardsData.dependencies) { $allDeps += $boardsData.dependencies }
  121.         if ($board.dependencies)      { $allDeps += $board.dependencies }
  122.         foreach ($dep in $allDeps) {
  123.             $ymlLines.Add("  $($dep.id): `"$($dep.version)`"")
  124.         }
  125.         Set-Content -Path $ymlPath -Value $ymlLines -Encoding UTF8
  126.         Write-Host "  Written: idf_component.yml" -ForegroundColor Gray
  127.         $needsClean = $true
  128.     } else {
  129.         Write-Host "  Skipped: idf_component.yml (up to date)" -ForegroundColor DarkGray
  130.     }
  131.  
  132.     # Write board_config.h for this board before the build
  133.     $boardConfigPath = Join-Path $PSScriptRoot "boards"
  134.     $boardConfigPath = Join-Path $boardConfigPath $boardSlug
  135.     $boardConfigPath = Join-Path $boardConfigPath "main"
  136.     $boardConfigPath = Join-Path $boardConfigPath "board_config.h"
  137.     if (-not (Test-Path $boardConfigPath) -or (Get-Item $boardConfigPath).LastWriteTime -lt $boardsJsonTime) {
  138.         # Escape inner quotes in display name for use in a C string literal
  139.         $escapedName = $boardName -replace '"', '\"'
  140.         $lines = [System.Collections.Generic.List[string]]::new()
  141.         $lines.Add("// <auto-generated>")
  142.         $lines.Add("//   Generated by build_all.ps1 -- do not edit by hand.")
  143.         $lines.Add("// </auto-generated>")
  144.         $lines.Add("#ifndef BOARD_CONFIG_H")
  145.         $lines.Add("#define BOARD_CONFIG_H")
  146.         $lines.Add("#define FIRMWARE_BOARD_ID $boardId")
  147.         $lines.Add("#define FIRMWARE_DISPLAY_NAME `"$escapedName`"")
  148.         $lines.Add("#define FIRMWARE_SLUG `"$boardSlug`"")
  149.         $lines.Add("#define LCD_INCHES ((float)${boardDiagonalInches})")
  150.         $lines.Add("#endif")
  151.         Set-Content -Path $boardConfigPath -Value $lines -Encoding UTF8
  152.         Write-Host "  Written: board_config.h" -ForegroundColor Gray
  153.     } else {
  154.         Write-Host "  Skipped: board_config.h (up to date)" -ForegroundColor DarkGray
  155.     }
  156.  
  157.     # Write CMakeLists.txt for this board
  158.     $cmakePath = Join-Path $PSScriptRoot "boards"
  159.     $cmakePath = Join-Path $cmakePath $boardSlug
  160.     $cmakePath = Join-Path $cmakePath "CMakeLists.txt"
  161.     if (-not (Test-Path $cmakePath) -or (Get-Item $cmakePath).LastWriteTime -lt $boardsJsonTime) {
  162.         $cmakeLines = [System.Collections.Generic.List[string]]::new()
  163.         $cmakeLines.Add("cmake_minimum_required(VERSION 3.16)")
  164.         $cmakeLines.Add("")
  165.         $cmakeLines.Add("# Auto-generated by build_all.ps1 -- do not edit by hand.")
  166.         $cmakeLines.Add("")
  167.         $cmakeLines.Add("# Injected by build system")
  168.         $cmakeLines.Add("add_compile_definitions(BUILD_TIMESTAMP_UTC=`${BUILD_TIMESTAMP_UTC})")
  169.         $cmakeLines.Add("")
  170.         $cmakeLines.Add("# Board-specific defines")
  171.         foreach ($define in $board.defines) {
  172.             $cmakeLines.Add("add_compile_definitions($define)")
  173.         }
  174.         $cmakeLines.Add("")
  175.         $cmakeLines.Add("include(`$ENV{IDF_PATH}/tools/cmake/project.cmake)")
  176.         $cmakeLines.Add("")
  177.         $cmakeLines.Add("project(espmon)")
  178.         Set-Content -Path $cmakePath -Value $cmakeLines -Encoding UTF8
  179.         Write-Host "  Written: CMakeLists.txt" -ForegroundColor Gray
  180.         $needsClean = $true
  181.     } else {
  182.         Write-Host "  Skipped: CMakeLists.txt (up to date)" -ForegroundColor DarkGray
  183.     }
  184.  
  185.     # If any generated files changed, wipe build artifacts so CMake/IDF starts fresh
  186.     if ($needsClean) {
  187.         $boardDir = Join-Path $PSScriptRoot "boards" | Join-Path -ChildPath $boardSlug
  188.         $buildDir = Join-Path $boardDir "build"
  189.         $managedComponentsDir = Join-Path $boardDir "managed_components"
  190.         foreach ($cleanDir in @($buildDir, $managedComponentsDir)) {
  191.             if (Test-Path $cleanDir) {
  192.                 Remove-Item $cleanDir -Recurse -Force
  193.                 Write-Host "  Cleaned: $($cleanDir | Split-Path -Leaf)" -ForegroundColor DarkYellow
  194.             }
  195.         }
  196.     }
  197.  
  198.     $job = Start-Job -ScriptBlock {
  199.         param($name, $slug, $target, $root, $buildTimestampUtc, $defines)
  200.  
  201.         $boardDir = "$root\boards\$slug"
  202.         $buildDir = "$boardDir\build"
  203.  
  204.         $result = @{
  205.             Name    = $name
  206.             Slug    = $slug
  207.             Success = $false
  208.             Error   = $null
  209.         }
  210.             Write-Host "Name: $name" -ForegroundColor Cyan
  211.             Write-Host "Slug: $slug" -ForegroundColor Cyan
  212.             Write-Host "Target: $target" -ForegroundColor Cyan
  213.             Write-Host ""
  214.         try {
  215.             # Set target
  216.             Write-Host "[$name] Setting target to $target..." -ForegroundColor Yellow
  217.             $setTargetOutput = & idf.py.exe -C $boardDir set-target $target 2>&1
  218.             if ($LASTEXITCODE -ne 0) {
  219.                 throw "Failed to set target: $setTargetOutput"
  220.             }
  221.            
  222.             # Build
  223.             Write-Host "[$name] Building..." -ForegroundColor Yellow
  224.             $dArgs = @("-DBUILD_TIMESTAMP_UTC=$buildTimestampUtc") + ($defines | ForEach-Object {
  225.                 if ($_ -match '=') { "-D$_" } else { "-D$_=1" }
  226.             })
  227.             $buildOutput = & idf.py -C $boardDir build @dArgs 2>&1 | Out-String
  228.             $buildExitCode = $LASTEXITCODE
  229.             if ($buildExitCode -ne 0) {
  230.                 throw "Build failed with exit code $buildExitCode : $buildOutput"
  231.             }
  232.            
  233.             Write-Host "[$name] Build completed, creating output directory..." -ForegroundColor Yellow
  234.             $firmwareOutDir = Join-Path $root  ".."
  235.             $firmwareOutDir = Join-Path $firmwareOutDir "Espmon.PortDispatcher"
  236.             $firmwareOutDir = Join-Path $firmwareOutDir "firmware"
  237.             $firmwareOutDir = Join-Path $firmwareOutDir $slug
  238.             if (-not (Test-Path $firmwareOutDir)) {
  239.                 New-Item -ItemType Directory -Path $firmwareOutDir | Out-Null
  240.             }
  241.  
  242.             Write-Host "[$name] Copying binaries..." -ForegroundColor Yellow
  243.             Copy-Item "$buildDir\bootloader\*.bin" $firmwareOutDir
  244.             Write-Host "[$name] Copied bootloader" -ForegroundColor Yellow
  245.             Copy-Item "$buildDir\partition_table\*.bin" $firmwareOutDir
  246.             Write-Host "[$name] Copied partition table" -ForegroundColor Yellow
  247.             $appBin = Get-ChildItem "$buildDir\*.bin" -Exclude "*bootloader*","*partition*" | Select-Object -First 1
  248.             Copy-Item $appBin.FullName -Destination "$firmwareOutDir\firmware.bin"
  249.             Write-Host "[$name] Copied app binary" -ForegroundColor Yellow    
  250.                    
  251.             $result.Success = $true
  252.             Write-Host "[$name] Build completed successfully!" -ForegroundColor Green
  253.         }
  254.         catch {
  255.             $result.Error = $_.Exception.Message
  256.             Write-Host "[$name] Build failed: $($_.Exception.Message)" -ForegroundColor Red
  257.         }
  258.    
  259.         return $result
  260.     } -ArgumentList $boardName, $boardSlug, $boardTarget, $scriptRootForJobs, $buildTimestampUtc, $board.defines
  261.     $jobs += $job
  262. }
  263.  
  264. # Wait for all jobs to complete and collect results
  265. Write-Host ""
  266. Write-Host "Waiting for builds to complete..." -ForegroundColor Cyan
  267. $results = $jobs | Wait-Job | Receive-Job
  268.  
  269. $jobs | Remove-Job
  270.  
  271. # Display summary
  272. Write-Host ""
  273. Write-Host "========================================"
  274. Write-Host "Build Summary"
  275. Write-Host "========================================"
  276.  
  277. $successCount = 0
  278. $failCount = 0
  279.  
  280. foreach ($result in $results) {
  281.     if ($result.Success) {
  282.         Write-Host "OK $($result.Name)" -ForegroundColor Green
  283.         $successCount++
  284.     }
  285.     else {
  286.         Write-Host "FAIL $($result.Name)" -ForegroundColor Red
  287.         Write-Host "  Error: $($result.Error)" -ForegroundColor Red
  288.         $failCount++
  289.     }
  290. }
  291.  
  292. Write-Host ""
  293. Write-Host "Success: $successCount | Failed: $failCount" -ForegroundColor $(if ($failCount -eq 0) { "Green" } else { "Yellow" })
  294.  
  295. if ($failCount -eq 0) {
  296.     Write-Host ""
  297.     Write-Host "All boards built successfully!" -ForegroundColor Green
  298.    
  299.     # Create boards.zip with all files except boards.json
  300.     Write-Host ""
  301.     Write-Host "Creating boards.zip..." -ForegroundColor Cyan
  302.    
  303.     $zipPath = Join-Path $firmwareDir "boards.zip"
  304.    
  305.     # Remove existing zip if present
  306.     if (Test-Path $zipPath) {
  307.         Remove-Item $zipPath -Force
  308.     }
  309.    
  310.     # Get all items in firmware directory (subdirectories only at this point)
  311.     $itemsToZip = Get-ChildItem -Path $firmwareDir -Directory
  312.    
  313.     if ($itemsToZip.Count -gt 0) {
  314.         # Create zip archive with all subdirectories
  315.         Compress-Archive -Path ($itemsToZip | ForEach-Object { $_.FullName }) `
  316.                         -DestinationPath $zipPath `
  317.                         -CompressionLevel Optimal
  318.        
  319.         Write-Host "Created boards.zip successfully" -ForegroundColor Green
  320.        
  321.         # Delete the loose directories
  322.         Write-Host "Cleaning up loose files..." -ForegroundColor Cyan
  323.        
  324.         $itemsToZip | ForEach-Object {
  325.             Remove-Item $_.FullName -Recurse -Force
  326.             Write-Host "  Removed: $($_.Name)" -ForegroundColor Gray
  327.         }
  328.        
  329.         Write-Host "Cleanup completed" -ForegroundColor Green
  330.     }
  331.     else {
  332.         Write-Host "Warning: No files to zip" -ForegroundColor Yellow
  333.     }
  334.    
  335.     # Copy boards.json to firmware directory after cleanup
  336.     Copy-Item $boardsJsonPath (Join-Path $firmwareDir "boards.json")
  337.  
  338.     # Generate FirmwareBuildTimestamp.cs
  339.     Write-Host ""
  340.     Write-Host "Generating FirmwareBuildTimestamp.cs..." -ForegroundColor Cyan
  341.  
  342.     # Use the same timestamp injected into the firmware so both sides are identical.
  343.    
  344.     $csContent = @"
  345. // <auto-generated>
  346. //   Generated by build_all.ps1 -- do not edit by hand.
  347. // </auto-generated>
  348.  
  349. using System;
  350.  
  351. namespace Espmon
  352. {
  353.    public static class FirmwareBuild
  354.    {
  355.        public static readonly ulong Timestamp = (ulong)$buildTimestampUtc;
  356.    }
  357. }
  358. "@
  359.  
  360.     $csOutputPath = Join-Path $firmwareDir ".."
  361.     $csOutputPath = Join-Path $csOutputPath "FirmwareBuildTimestamp.cs"
  362.     $csOutputPath = [System.IO.Path]::GetFullPath($csOutputPath)
  363.  
  364.     Set-Content -Path $csOutputPath -Value $csContent -Encoding UTF8
  365.     Write-Host "Written: $csOutputPath" -ForegroundColor Green
  366.     Write-Host "  Timestamp (UTC): $buildTimestampUtc" -ForegroundColor Gray
  367.  
  368.     Write-Host ""
  369.     Write-Host "Firmware package available in: firmware/" -ForegroundColor Cyan
  370.     Write-Host "  - boards.zip (firmware binaries)" -ForegroundColor Cyan
  371.     Write-Host "  - boards.json (metadata)" -ForegroundColor Cyan
  372.     exit 0
  373. }
  374. else {
  375.     Write-Host ""
  376.     Write-Host "Some boards failed to build. Check errors above." -ForegroundColor Red
  377.     exit 1
  378. }
Advertisement
Add Comment
Please, Sign In to add comment