mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-08-14 06:39:45 +00:00
* fix(tdeck): improve display startup and brightness handling * fix(energy-sweep): use instant RSSI and only lock LoRa while scanning * feat(cardputer-zero): add linux shells and M5 SDK baseline * feat(cardputer-zero): add linux runtime baseline and shell ui simulator * feat(cardputer-zero): unify linux shell boot and polish simulator * docs(cardputer-zero): define final-shape adaptation spec * feat(cardputer-zero): integrate shared linux runtimes and pages * fix(linux-sim): mount repo root in dev container * Fix GPS runtime semantics and transport init Add a GPS specification and align platform runtimes around explicit GPS enable, power, receiver configuration, and external NMEA export semantics. Keep internal NMEA parsing independent from external export settings, stop treating gps_mode as an enable flag, and update phone/UI config paths to use gps_enabled. Decouple board-level GPS transport readiness from UBX receiver probing on T-Deck, T-Deck Pro, and T-LoRa Pager, and let boards own UART teardown. Verified with pio run -e tdeck, pio run -e tlora_pager_sx1262, pio run -e gat562_mesh_evb_pro, and pio run -e tdeck_pro_a7682e. * Add Russian localization pack Add an installable European Cyrillic Extended locale bundle with Russian translations, Cyrillic font metadata, and package catalog entry. Credit polarikus for the Russian translations based on the polarikus/trail-mate localization PR. * Prepare 0.1.23-alpha release * Fix T-Watch Morse release build * Format CI-checked sources * Fix Cardputer Linux CI dependencies * Fix WSL validation smoke target build * Prepare 0.1.24-alpha release * fix: unblock Cardputer Zero Linux CI
266 lines
8.0 KiB
PowerShell
266 lines
8.0 KiB
PowerShell
param(
|
|
[ValidateSet('reconfigure', 'build', 'flash', 'monitor', 'flash-monitor')]
|
|
[string]$Action = 'build',
|
|
[string]$Target = 'tab5',
|
|
[string]$BuildDir = '',
|
|
[string]$Port = ''
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Get-RepoRoot {
|
|
return Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
}
|
|
|
|
function Get-WorkspaceSettings([string]$RepoRoot) {
|
|
$settingsPath = Join-Path $RepoRoot '.vscode\settings.json'
|
|
if (-not (Test-Path $settingsPath)) {
|
|
return $null
|
|
}
|
|
|
|
try {
|
|
return Get-Content $settingsPath -Raw | ConvertFrom-Json
|
|
}
|
|
catch {
|
|
Write-Warning "Failed to parse ${settingsPath}: $_"
|
|
return $null
|
|
}
|
|
}
|
|
|
|
function Resolve-IdfPath([string]$RepoRoot, $Settings) {
|
|
$candidates = @()
|
|
if ($env:IDF_PATH) {
|
|
$candidates += $env:IDF_PATH
|
|
}
|
|
if ($Settings -and $Settings.PSObject.Properties.Name -contains 'idf.currentSetup' -and $Settings.'idf.currentSetup') {
|
|
$candidates += [string]$Settings.'idf.currentSetup'
|
|
}
|
|
$frameworkRoot = 'C:\ProgramData\Espressif\frameworks'
|
|
if (Test-Path $frameworkRoot) {
|
|
$candidates += (Get-ChildItem $frameworkRoot -Directory -Filter 'esp-idf-v*' | Sort-Object Name -Descending | ForEach-Object { $_.FullName })
|
|
}
|
|
|
|
foreach ($candidate in $candidates) {
|
|
if (-not $candidate) {
|
|
continue
|
|
}
|
|
$normalized = $candidate.TrimEnd('\', '/')
|
|
if (Test-Path (Join-Path $normalized 'tools\idf.py')) {
|
|
return $normalized
|
|
}
|
|
}
|
|
|
|
throw 'Unable to resolve ESP-IDF path. Set IDF_PATH or update .vscode/settings.json.'
|
|
}
|
|
|
|
function Resolve-PythonExe($Settings) {
|
|
$candidates = @()
|
|
if ($env:IDF_PYTHON_ENV_PATH) {
|
|
$candidates += (Join-Path $env:IDF_PYTHON_ENV_PATH 'Scripts\python.exe')
|
|
}
|
|
if ($Settings -and $Settings.PSObject.Properties.Name -contains 'idf.pythonBinPathWin' -and $Settings.'idf.pythonBinPathWin') {
|
|
$candidates += [string]$Settings.'idf.pythonBinPathWin'
|
|
}
|
|
$pythonEnvRoot = 'C:\ProgramData\Espressif\python_env'
|
|
if (Test-Path $pythonEnvRoot) {
|
|
$candidates += (Get-ChildItem $pythonEnvRoot -Directory -Filter 'idf*_py*_env' | Sort-Object Name -Descending | ForEach-Object { Join-Path $_.FullName 'Scripts\python.exe' })
|
|
}
|
|
$candidates += 'python.exe'
|
|
|
|
foreach ($candidate in $candidates) {
|
|
try {
|
|
$command = Get-Command $candidate -ErrorAction Stop
|
|
return $command.Source
|
|
}
|
|
catch {
|
|
if (Test-Path $candidate) {
|
|
return $candidate
|
|
}
|
|
}
|
|
}
|
|
|
|
throw 'Unable to resolve Python executable for ESP-IDF.'
|
|
}
|
|
|
|
function Resolve-NinjaExe() {
|
|
$candidates = @()
|
|
$toolsRoot = 'C:\ProgramData\Espressif\tools\ninja'
|
|
if (Test-Path $toolsRoot) {
|
|
$candidates += (Get-ChildItem $toolsRoot -Directory | Sort-Object Name -Descending | ForEach-Object { Join-Path $_.FullName 'ninja.exe' })
|
|
}
|
|
$candidates += 'ninja.exe'
|
|
|
|
foreach ($candidate in $candidates) {
|
|
try {
|
|
$command = Get-Command $candidate -ErrorAction Stop
|
|
return $command.Source
|
|
}
|
|
catch {
|
|
if (Test-Path $candidate) {
|
|
return $candidate
|
|
}
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Resolve-RiscvToolchainBin() {
|
|
$toolsRoot = 'C:\ProgramData\Espressif\tools\riscv32-esp-elf'
|
|
if (-not (Test-Path $toolsRoot)) {
|
|
return $null
|
|
}
|
|
|
|
$candidates = Get-ChildItem $toolsRoot -Directory |
|
|
Sort-Object LastWriteTime -Descending |
|
|
ForEach-Object { Join-Path $_.FullName 'riscv32-esp-elf\bin' }
|
|
|
|
foreach ($candidate in $candidates) {
|
|
$gccPath = Join-Path $candidate 'riscv32-esp-elf-gcc.exe'
|
|
if (Test-Path $gccPath) {
|
|
return $candidate
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Resolve-Port($Settings, [string]$RequestedPort) {
|
|
if ($RequestedPort) {
|
|
return $RequestedPort
|
|
}
|
|
if ($env:ESPPORT) {
|
|
return $env:ESPPORT
|
|
}
|
|
if ($Settings) {
|
|
if ($Settings.PSObject.Properties.Name -contains 'idf.portWin' -and $Settings.'idf.portWin') {
|
|
return [string]$Settings.'idf.portWin'
|
|
}
|
|
if ($Settings.PSObject.Properties.Name -contains 'idf.port' -and $Settings.'idf.port') {
|
|
return [string]$Settings.'idf.port'
|
|
}
|
|
}
|
|
return 'COM6'
|
|
}
|
|
|
|
function Get-CacheEntryValue([string]$CachePath, [string]$Key) {
|
|
if (-not (Test-Path $CachePath)) {
|
|
return $null
|
|
}
|
|
|
|
$prefix = "${Key}:"
|
|
foreach ($line in Get-Content $CachePath) {
|
|
if ($line.StartsWith($prefix)) {
|
|
$parts = $line.Split('=', 2)
|
|
if ($parts.Length -eq 2) {
|
|
return $parts[1]
|
|
}
|
|
}
|
|
}
|
|
return $null
|
|
}
|
|
|
|
function Reset-StaleIdfBuildDir([string]$RepoRoot, [string]$BuildDir, [string]$IdfPath) {
|
|
$buildDirPath = if ([System.IO.Path]::IsPathRooted($BuildDir)) {
|
|
$BuildDir
|
|
} else {
|
|
Join-Path $RepoRoot $BuildDir
|
|
}
|
|
|
|
if (-not (Test-Path $buildDirPath)) {
|
|
return
|
|
}
|
|
|
|
$cacheFiles = @(
|
|
Join-Path $buildDirPath 'CMakeCache.txt'
|
|
Join-Path $buildDirPath 'bootloader\CMakeCache.txt'
|
|
)
|
|
|
|
$normalizedCurrent = $IdfPath.TrimEnd('\', '/')
|
|
foreach ($cachePath in $cacheFiles) {
|
|
$cachedIdfPath = Get-CacheEntryValue $cachePath 'IDF_PATH'
|
|
if (-not $cachedIdfPath) {
|
|
continue
|
|
}
|
|
|
|
$normalizedCached = $cachedIdfPath.TrimEnd('\', '/')
|
|
if ($normalizedCached -ne $normalizedCurrent) {
|
|
Write-Warning "Stale ESP-IDF cache detected in $buildDirPath"
|
|
Write-Warning "Cached IDF_PATH: $normalizedCached"
|
|
Write-Warning "Current IDF_PATH: $normalizedCurrent"
|
|
Write-Host "[trail-mate] Removing stale build directory $buildDirPath"
|
|
Remove-Item -LiteralPath $buildDirPath -Recurse -Force
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
$repoRoot = Get-RepoRoot
|
|
$settings = Get-WorkspaceSettings $repoRoot
|
|
$idfPath = Resolve-IdfPath $repoRoot $settings
|
|
$pythonExe = Resolve-PythonExe $settings
|
|
$ninjaExe = Resolve-NinjaExe
|
|
$riscvToolchainBin = Resolve-RiscvToolchainBin
|
|
$portValue = Resolve-Port $settings $Port
|
|
if (-not $BuildDir) {
|
|
$BuildDir = "build.$Target"
|
|
}
|
|
|
|
Reset-StaleIdfBuildDir $repoRoot $BuildDir $idfPath
|
|
|
|
$env:IDF_PATH = $idfPath
|
|
$env:IDF_PYTHON_ENV_PATH = Split-Path -Parent (Split-Path -Parent $pythonExe)
|
|
if ($riscvToolchainBin) {
|
|
$env:PATH = "$riscvToolchainBin;$env:PATH"
|
|
}
|
|
if ($ninjaExe) {
|
|
$env:PATH = "$(Split-Path -Parent $ninjaExe);$env:PATH"
|
|
}
|
|
$espRomElfRoot = 'C:\ProgramData\Espressif\tools\esp-rom-elfs'
|
|
if ((-not $env:ESP_ROM_ELF_DIR) -and (Test-Path $espRomElfRoot)) {
|
|
$latestRomDir = Get-ChildItem $espRomElfRoot -Directory | Sort-Object Name -Descending | Select-Object -First 1
|
|
if ($latestRomDir) {
|
|
$env:ESP_ROM_ELF_DIR = $latestRomDir.FullName
|
|
}
|
|
}
|
|
|
|
$idfPy = Join-Path $idfPath 'tools\idf.py'
|
|
$baseArgs = @($idfPy, '-B', $BuildDir, "-DTRAIL_MATE_IDF_TARGET=$Target")
|
|
if ($Action -ne 'build' -and $portValue) {
|
|
$baseArgs += @('-p', $portValue)
|
|
}
|
|
|
|
$actionArgs = switch ($Action) {
|
|
'reconfigure' { ,@('reconfigure') }
|
|
'build' { ,@('build') }
|
|
'flash' { ,@('flash') }
|
|
'monitor' { ,@('monitor') }
|
|
'flash-monitor' { ,@('flash', 'monitor') }
|
|
}
|
|
|
|
Write-Host "[trail-mate] RepoRoot : $repoRoot"
|
|
Write-Host "[trail-mate] ESP-IDF : $idfPath"
|
|
Write-Host "[trail-mate] Python : $pythonExe"
|
|
Write-Host "[trail-mate] Target : $Target"
|
|
Write-Host "[trail-mate] BuildDir : $BuildDir"
|
|
if ($portValue) {
|
|
Write-Host "[trail-mate] Port : $portValue"
|
|
}
|
|
if ($Action -eq 'flash-monitor') {
|
|
Write-Warning 'Tab5 may re-enter ROM download mode after auto reset. Prefer running flash and monitor as two separate tasks.'
|
|
}
|
|
|
|
Push-Location $repoRoot
|
|
try {
|
|
& $pythonExe @baseArgs @actionArgs
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
if ($Action -eq 'flash') {
|
|
Write-Host '[trail-mate] Flash completed. For Tab5, press RESET once manually, then start the monitor task.'
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|