feat(ruvllm-esp32): Add improved Windows PowerShell scripts

- Add setup.ps1: Auto-installs espup, espflash, and ESP32 toolchain
- Add build.ps1: Auto-detects toolchain paths, no hardcoded values
- Add flash.ps1: Auto-detects COM ports with interactive selection
- Add env.ps1: Sets up environment for current session
- Add monitor.ps1: Serial monitor with auto port detection
- Update CLI to use PowerShell scripts on Windows
- Improve COM port detection using System.IO.Ports
- Update README with improved Windows workflow

Fixes Windows-specific issues:
- No more hardcoded paths (C:\Users\ruv\...)
- Dynamic libclang and Python path resolution
- Auto-detection of ESP toolchain location
- Better error handling and user feedback

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rUv 2025-12-26 18:52:18 +00:00
parent 61a4a79d70
commit 960712a690
12 changed files with 1026 additions and 32 deletions

View file

@ -77,11 +77,29 @@ cd ruvector/examples/ruvLLM/esp32-flash
```powershell
git clone https://github.com/ruvnet/ruvector
cd ruvector\examples\ruvLLM\esp32-flash
.\install.ps1 # Install deps (restart PowerShell after)
.\install.ps1 build # Build
.\install.ps1 flash COM6 # Flash
# One-time setup (installs espup, espflash, toolchain)
.\scripts\windows\setup.ps1
# Load environment (run in each new terminal)
. .\scripts\windows\env.ps1
# Build (auto-detects toolchain paths)
.\scripts\windows\build.ps1
# Flash (auto-detects COM port)
.\scripts\windows\flash.ps1
# Or specify port manually
.\scripts\windows\flash.ps1 -Port COM6
```
**Windows Features:**
- ✅ Auto-detects ESP toolchain paths (no hardcoding)
- ✅ Auto-detects COM ports
- ✅ Dynamic libclang/Python path resolution
- ✅ Single setup script for first-time users
### Option 3: Manual Build
```bash

View file

@ -94,13 +94,31 @@ function detectPort() {
try {
if (platform === 'win32') {
// Windows: Look for COM ports
const result = execSync('wmic path Win32_SerialPort get DeviceID', { encoding: 'utf8' });
const ports = result.split('\n').filter(line => line.includes('COM')).map(line => line.trim());
return ports[0] || 'COM3';
// Windows: Use PowerShell for better COM port detection
try {
const result = execSync(
'powershell -Command "[System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object { [int]($_ -replace \'COM\', \'\') }"',
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
);
const ports = result.trim().split('\n').filter(p => p.match(/COM\d+/));
if (ports.length > 0) {
return ports[0].trim();
}
} catch {
// Fallback to wmic
const result = execSync('wmic path Win32_SerialPort get DeviceID 2>nul', { encoding: 'utf8' });
const ports = result.split('\n').filter(line => line.includes('COM')).map(line => line.trim());
if (ports.length > 0) return ports[0];
}
return 'COM3';
} else if (platform === 'darwin') {
// macOS
const files = fs.readdirSync('/dev').filter(f => f.startsWith('cu.usbserial') || f.startsWith('cu.SLAB'));
const files = fs.readdirSync('/dev').filter(f =>
f.startsWith('cu.usbserial') ||
f.startsWith('cu.SLAB') ||
f.startsWith('cu.wchusbserial') ||
f.startsWith('cu.usbmodem')
);
return files[0] ? `/dev/${files[0]}` : '/dev/cu.usbserial-0001';
} else {
// Linux
@ -127,27 +145,52 @@ async function installToolchain() {
const { platform } = detectPlatform();
try {
// Install espup
logStep('Installing espup...');
if (platform === 'win32') {
execSync('cargo install espup', { stdio: 'inherit' });
// Windows: Check if we have the PowerShell setup script
const scriptsDir = path.join(__dirname, '..', 'scripts', 'windows');
const setupScript = path.join(scriptsDir, 'setup.ps1');
if (fs.existsSync(setupScript)) {
logStep('Running Windows setup script...');
execSync(`powershell -ExecutionPolicy Bypass -File "${setupScript}"`, { stdio: 'inherit' });
} else {
// Fallback: manual installation
logStep('Installing espup...');
// Download espup for Windows
const espupUrl = 'https://github.com/esp-rs/espup/releases/latest/download/espup-x86_64-pc-windows-msvc.exe';
const espupPath = path.join(os.tmpdir(), 'espup.exe');
execSync(`powershell -Command "Invoke-WebRequest -Uri '${espupUrl}' -OutFile '${espupPath}'"`, { stdio: 'inherit' });
logStep('Running espup install...');
execSync(`"${espupPath}" install`, { stdio: 'inherit' });
// Install espflash
logStep('Installing espflash...');
execSync('cargo install espflash ldproxy', { stdio: 'inherit' });
}
logSuccess('Toolchain installed successfully!');
log('\nTo use the toolchain, run:', 'yellow');
log(' . .\\scripts\\windows\\env.ps1', 'cyan');
} else {
execSync('curl -L https://github.com/esp-rs/espup/releases/latest/download/espup-x86_64-unknown-linux-gnu -o /tmp/espup && chmod +x /tmp/espup && /tmp/espup install', { stdio: 'inherit' });
}
// Linux/macOS
logStep('Installing espup...');
const arch = os.arch() === 'arm64' ? 'aarch64' : 'x86_64';
const binary = platform === 'darwin'
? `espup-${arch}-apple-darwin`
: `espup-${arch}-unknown-linux-gnu`;
// Install espflash
logStep('Installing espflash...');
execSync('cargo install espflash ldproxy', { stdio: 'inherit' });
execSync(`curl -L https://github.com/esp-rs/espup/releases/latest/download/${binary} -o /tmp/espup && chmod +x /tmp/espup && /tmp/espup install`, { stdio: 'inherit' });
// Run espup install
logStep('Setting up ESP32 toolchain...');
execSync('espup install', { stdio: 'inherit' });
// Install espflash
logStep('Installing espflash...');
execSync('cargo install espflash ldproxy', { stdio: 'inherit' });
logSuccess('Toolchain installed successfully!');
log('\nPlease restart your terminal or run:', 'yellow');
if (platform === 'win32') {
log(' $env:PATH = [System.Environment]::GetEnvironmentVariable("Path","User")', 'cyan');
} else {
logSuccess('Toolchain installed successfully!');
log('\nPlease restart your terminal or run:', 'yellow');
log(' source $HOME/export-esp.sh', 'cyan');
}
@ -160,8 +203,9 @@ async function installToolchain() {
async function build(options = {}) {
const target = options.target || 'esp32';
const release = options.release || false;
const release = options.release !== false; // Default to release
const features = options.features || '';
const { platform } = detectPlatform();
logStep(`Building for ${target}${release ? ' (release)' : ''}...`);
@ -175,12 +219,33 @@ async function build(options = {}) {
const rustTarget = targetMap[target] || targetMap['esp32'];
let cmd = `cargo build --target ${rustTarget}`;
if (release) cmd += ' --release';
if (features) cmd += ` --features ${features}`;
try {
execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
if (platform === 'win32') {
// Windows: Use PowerShell build script if available
const scriptsDir = path.join(__dirname, '..', 'scripts', 'windows');
const buildScript = path.join(scriptsDir, 'build.ps1');
if (fs.existsSync(buildScript)) {
let psArgs = `-ExecutionPolicy Bypass -File "${buildScript}" -Target "${rustTarget}"`;
if (release) psArgs += ' -Release';
if (features) psArgs += ` -Features "${features}"`;
execSync(`powershell ${psArgs}`, { stdio: 'inherit', cwd: process.cwd() });
} else {
// Fallback to direct cargo
let cmd = `cargo build --target ${rustTarget}`;
if (release) cmd += ' --release';
if (features) cmd += ` --features ${features}`;
execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
}
} else {
// Linux/macOS
let cmd = `cargo build --target ${rustTarget}`;
if (release) cmd += ' --release';
if (features) cmd += ` --features ${features}`;
execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
}
logSuccess('Build completed!');
return true;
} catch (e) {
@ -192,12 +257,39 @@ async function build(options = {}) {
async function flash(port, options = {}) {
const actualPort = port || detectPort();
const target = options.target || 'esp32';
const { platform } = detectPlatform();
logStep(`Flashing to ${actualPort}...`);
const targetMap = {
'esp32': 'xtensa-esp32-espidf',
'esp32s2': 'xtensa-esp32s2-espidf',
'esp32s3': 'xtensa-esp32s3-espidf',
'esp32c3': 'riscv32imc-esp-espidf',
'esp32c6': 'riscv32imac-esp-espidf'
};
const rustTarget = targetMap[target] || targetMap['esp32'];
try {
const cmd = `espflash flash --monitor --port ${actualPort} target/xtensa-${target}-espidf/release/ruvllm-esp32`;
execSync(cmd, { stdio: 'inherit' });
if (platform === 'win32') {
// Windows: Use PowerShell flash script if available
const scriptsDir = path.join(__dirname, '..', 'scripts', 'windows');
const flashScript = path.join(scriptsDir, 'flash.ps1');
if (fs.existsSync(flashScript)) {
const psArgs = `-ExecutionPolicy Bypass -File "${flashScript}" -Port "${actualPort}" -Target "${rustTarget}"`;
execSync(`powershell ${psArgs}`, { stdio: 'inherit', cwd: process.cwd() });
} else {
// Fallback
const binary = `target\\${rustTarget}\\release\\ruvllm-esp32`;
execSync(`espflash flash --monitor --port ${actualPort} ${binary}`, { stdio: 'inherit' });
}
} else {
// Linux/macOS
const binary = `target/${rustTarget}/release/ruvllm-esp32`;
execSync(`espflash flash --monitor --port ${actualPort} ${binary}`, { stdio: 'inherit' });
}
logSuccess('Flash completed!');
return true;
} catch (e) {

View file

@ -0,0 +1,124 @@
# build.ps1 - Auto-configure and build RuvLLM ESP32
# Automatically detects toolchain paths - no manual configuration needed
param(
[string]$Target = "xtensa-esp32-espidf",
[switch]$Release = $true,
[string]$Features = ""
)
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Build ===" -ForegroundColor Cyan
Write-Host ""
# Auto-detect paths
$rustupHome = if ($env:RUSTUP_HOME) { $env:RUSTUP_HOME } else { "$env:USERPROFILE\.rustup" }
$cargoHome = if ($env:CARGO_HOME) { $env:CARGO_HOME } else { "$env:USERPROFILE\.cargo" }
# Find ESP toolchain
$espToolchain = (Get-ChildItem "$rustupHome\toolchains" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1)
if (-not $espToolchain) {
Write-Error "ESP toolchain not found. Run .\setup.ps1 first"
}
$espToolchainPath = $espToolchain.FullName
# Find libclang dynamically
$libclang = Get-ChildItem "$espToolchainPath" -Recurse -Filter "libclang.dll" -ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $libclang) {
Write-Error "libclang.dll not found in $espToolchainPath"
}
# Find Python
$python = Get-Command python -ErrorAction SilentlyContinue
if (-not $python) {
$python = Get-Command python3 -ErrorAction SilentlyContinue
}
if (-not $python) {
Write-Error "Python not found. Please install Python 3.8+"
}
$pythonPath = Split-Path $python.Source
# Find clang and xtensa-esp-elf paths
$clangBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "esp-clang" -ErrorAction SilentlyContinue |
Select-Object -First 1
$clangBinPath = if ($clangBin) { "$($clangBin.FullName)\bin" } else { "" }
$xtensaBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "xtensa-esp-elf" -ErrorAction SilentlyContinue |
Select-Object -First 1
$xtensaBinPath = if ($xtensaBin) { "$($xtensaBin.FullName)\bin" } else { "" }
# Set environment variables
$env:LIBCLANG_PATH = Split-Path $libclang.FullName
$env:RUSTUP_TOOLCHAIN = "esp"
$env:ESP_IDF_VERSION = "v5.1.2"
# Build PATH with all required directories
$pathParts = @(
$pythonPath,
"$pythonPath\Scripts",
$clangBinPath,
$xtensaBinPath,
"$cargoHome\bin"
) | Where-Object { $_ -ne "" }
$env:PATH = ($pathParts -join ";") + ";" + $env:PATH
Write-Host "Build Configuration:" -ForegroundColor Gray
Write-Host " Target: $Target"
Write-Host " Release: $Release"
Write-Host " Toolchain: $($espToolchain.Name)"
Write-Host " LIBCLANG_PATH: $($env:LIBCLANG_PATH)"
Write-Host ""
# Navigate to project directory
$projectDir = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
Push-Location $projectDir
try {
# Build cargo command
$cargoArgs = @("build")
if ($Release) {
$cargoArgs += "--release"
}
if ($Features) {
$cargoArgs += "--features"
$cargoArgs += $Features
}
Write-Host "Running: cargo $($cargoArgs -join ' ')" -ForegroundColor Gray
Write-Host ""
& cargo @cargoArgs
if ($LASTEXITCODE -ne 0) {
throw "Build failed with exit code $LASTEXITCODE"
}
Write-Host ""
Write-Host "Build successful!" -ForegroundColor Green
# Find the built binary
$buildDir = if ($Release) { "release" } else { "debug" }
$binary = Get-ChildItem "$projectDir\target\$Target\$buildDir" -Filter "*.elf" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notmatch "deps" } |
Select-Object -First 1
if ($binary) {
Write-Host "Binary: $($binary.FullName)" -ForegroundColor Cyan
}
Write-Host ""
Write-Host "Next: Run .\flash.ps1 to flash to device" -ForegroundColor Yellow
} finally {
Pop-Location
}

View file

@ -0,0 +1,60 @@
# env.ps1 - Set up ESP32 Rust environment for the current session
# Source this script: . .\env.ps1
$ErrorActionPreference = "SilentlyContinue"
# Find paths
$rustupHome = if ($env:RUSTUP_HOME) { $env:RUSTUP_HOME } else { "$env:USERPROFILE\.rustup" }
$cargoHome = if ($env:CARGO_HOME) { $env:CARGO_HOME } else { "$env:USERPROFILE\.cargo" }
# Find ESP toolchain
$espToolchain = (Get-ChildItem "$rustupHome\toolchains" -Directory |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1)
if (-not $espToolchain) {
Write-Host "ESP toolchain not found. Run setup.ps1 first." -ForegroundColor Red
return
}
$espToolchainPath = $espToolchain.FullName
# Find libclang
$libclang = Get-ChildItem "$espToolchainPath" -Recurse -Filter "libclang.dll" |
Select-Object -First 1
# Find clang bin
$clangBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "esp-clang" |
Select-Object -First 1
# Find xtensa-esp-elf bin
$xtensaBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "xtensa-esp-elf" |
Select-Object -First 1
# Find Python
$python = Get-Command python -ErrorAction SilentlyContinue
$pythonPath = if ($python) { Split-Path $python.Source } else { "" }
# Set environment variables
$env:LIBCLANG_PATH = if ($libclang) { Split-Path $libclang.FullName } else { "" }
$env:RUSTUP_TOOLCHAIN = "esp"
$env:ESP_IDF_VERSION = "v5.1.2"
# Build PATH
$pathAdditions = @()
if ($pythonPath) { $pathAdditions += $pythonPath; $pathAdditions += "$pythonPath\Scripts" }
if ($clangBin) { $pathAdditions += "$($clangBin.FullName)\bin" }
if ($xtensaBin) { $pathAdditions += "$($xtensaBin.FullName)\bin" }
$pathAdditions += "$cargoHome\bin"
$env:PATH = ($pathAdditions -join ";") + ";" + $env:PATH
# Display status
Write-Host ""
Write-Host "ESP32 Rust environment loaded" -ForegroundColor Green
Write-Host ""
Write-Host " RUSTUP_TOOLCHAIN: $($env:RUSTUP_TOOLCHAIN)" -ForegroundColor Gray
Write-Host " LIBCLANG_PATH: $($env:LIBCLANG_PATH)" -ForegroundColor Gray
Write-Host " ESP_IDF_VERSION: $($env:ESP_IDF_VERSION)" -ForegroundColor Gray
Write-Host ""
Write-Host "Ready to build! Run: .\build.ps1" -ForegroundColor Cyan

View file

@ -0,0 +1,99 @@
# flash.ps1 - Auto-detect COM port and flash RuvLLM ESP32
# Automatically finds connected ESP32 devices
param(
[string]$Port = "",
[switch]$Monitor = $true,
[string]$Target = "xtensa-esp32-espidf",
[switch]$Release = $true
)
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Flash ===" -ForegroundColor Cyan
Write-Host ""
# Auto-detect COM port if not specified
if (-not $Port) {
# Get available COM ports
Add-Type -AssemblyName System.IO.Ports
$ports = [System.IO.Ports.SerialPort]::GetPortNames() |
Where-Object { $_ -match "COM\d+" } |
Sort-Object { [int]($_ -replace "COM", "") }
if ($ports.Count -eq 0) {
Write-Error "No COM ports found. Is the ESP32 connected via USB?"
} elseif ($ports.Count -eq 1) {
$Port = $ports[0]
Write-Host "Auto-detected port: $Port" -ForegroundColor Green
} else {
Write-Host "Multiple COM ports found:" -ForegroundColor Yellow
Write-Host ""
for ($i = 0; $i -lt $ports.Count; $i++) {
Write-Host " [$i] $($ports[$i])"
}
Write-Host ""
$selection = Read-Host "Select port (0-$($ports.Count - 1))"
if ($selection -match "^\d+$" -and [int]$selection -lt $ports.Count) {
$Port = $ports[[int]$selection]
} else {
Write-Error "Invalid selection"
}
}
}
Write-Host "Using port: $Port" -ForegroundColor Cyan
Write-Host ""
# Find binary
$projectDir = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
$buildDir = if ($Release) { "release" } else { "debug" }
$targetDir = "$projectDir\target\$Target\$buildDir"
# Look for ELF or binary file
$binary = Get-ChildItem $targetDir -Filter "*.elf" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notmatch "deps" } |
Select-Object -First 1
if (-not $binary) {
$binary = Get-ChildItem $targetDir -Filter "ruvllm-esp32*" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notmatch "\." -or $_.Name -match "\.elf$" } |
Select-Object -First 1
}
if (-not $binary) {
Write-Host "Available files in $targetDir`:" -ForegroundColor Yellow
Get-ChildItem $targetDir -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " $($_.Name)" }
Write-Error "No binary found. Run .\build.ps1 first"
}
Write-Host "Binary: $($binary.Name)" -ForegroundColor Gray
Write-Host ""
# Check for espflash
$espflash = Get-Command espflash -ErrorAction SilentlyContinue
if (-not $espflash) {
Write-Error "espflash not found. Run .\setup.ps1 first"
}
# Build espflash command
$espflashArgs = @("flash", "--port", $Port, $binary.FullName)
if ($Monitor) {
$espflashArgs += "--monitor"
}
Write-Host "Flashing..." -ForegroundColor Cyan
Write-Host "Command: espflash $($espflashArgs -join ' ')" -ForegroundColor Gray
Write-Host ""
# Flash the device
& espflash @espflashArgs
if ($LASTEXITCODE -ne 0) {
Write-Error "Flash failed with exit code $LASTEXITCODE"
}
Write-Host ""
Write-Host "Flash complete!" -ForegroundColor Green

View file

@ -0,0 +1,41 @@
# monitor.ps1 - Open serial monitor for ESP32
# Auto-detects COM port
param(
[string]$Port = "",
[int]$Baud = 115200
)
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Serial Monitor ===" -ForegroundColor Cyan
Write-Host ""
# Auto-detect COM port if not specified
if (-not $Port) {
Add-Type -AssemblyName System.IO.Ports
$ports = [System.IO.Ports.SerialPort]::GetPortNames() |
Where-Object { $_ -match "COM\d+" } |
Sort-Object { [int]($_ -replace "COM", "") }
if ($ports.Count -eq 0) {
Write-Error "No COM ports found. Is the ESP32 connected?"
} elseif ($ports.Count -eq 1) {
$Port = $ports[0]
Write-Host "Auto-detected port: $Port" -ForegroundColor Green
} else {
Write-Host "Multiple COM ports found:" -ForegroundColor Yellow
for ($i = 0; $i -lt $ports.Count; $i++) {
Write-Host " [$i] $($ports[$i])"
}
$selection = Read-Host "Select port (0-$($ports.Count - 1))"
$Port = $ports[[int]$selection]
}
}
Write-Host "Opening monitor on $Port at $Baud baud..." -ForegroundColor Cyan
Write-Host "Press Ctrl+C to exit" -ForegroundColor Gray
Write-Host ""
# Use espflash monitor
& espflash monitor --port $Port --baud $Baud

View file

@ -0,0 +1,118 @@
# setup.ps1 - One-time Windows setup for RuvLLM ESP32
# Run this once to install/configure the ESP32 Rust toolchain
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Windows Setup ===" -ForegroundColor Cyan
Write-Host ""
# Find Rust ESP toolchain dynamically
$rustupHome = if ($env:RUSTUP_HOME) { $env:RUSTUP_HOME } else { "$env:USERPROFILE\.rustup" }
$cargoHome = if ($env:CARGO_HOME) { $env:CARGO_HOME } else { "$env:USERPROFILE\.cargo" }
# Check if Rust is installed
$rustc = Get-Command rustc -ErrorAction SilentlyContinue
if (-not $rustc) {
Write-Host "Rust not found. Installing rustup..." -ForegroundColor Yellow
Invoke-WebRequest -Uri "https://win.rustup.rs/x86_64" -OutFile rustup-init.exe
.\rustup-init.exe -y --default-toolchain stable
Remove-Item rustup-init.exe
$env:PATH = "$cargoHome\bin;" + $env:PATH
Write-Host "Rust installed successfully" -ForegroundColor Green
}
# Find or install ESP toolchain
$espToolchain = Get-ChildItem "$rustupHome\toolchains" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1
if (-not $espToolchain) {
Write-Host "ESP toolchain not found. Installing espup..." -ForegroundColor Yellow
# Download espup
$espupUrl = "https://github.com/esp-rs/espup/releases/latest/download/espup-x86_64-pc-windows-msvc.exe"
$espupPath = "$env:TEMP\espup.exe"
Write-Host "Downloading espup..." -ForegroundColor Gray
Invoke-WebRequest -Uri $espupUrl -OutFile $espupPath
Write-Host "Running espup install (this may take several minutes)..." -ForegroundColor Gray
& $espupPath install
if ($LASTEXITCODE -ne 0) {
Write-Error "espup install failed with exit code $LASTEXITCODE"
}
Remove-Item $espupPath -ErrorAction SilentlyContinue
# Re-check for toolchain
$espToolchain = Get-ChildItem "$rustupHome\toolchains" -Directory |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1
}
if (-not $espToolchain) {
Write-Error "ESP toolchain installation failed. Please install manually: https://esp-rs.github.io/book/"
}
Write-Host "Found ESP toolchain: $($espToolchain.Name)" -ForegroundColor Green
# Find Python
$python = Get-Command python -ErrorAction SilentlyContinue
if (-not $python) {
$python = Get-Command python3 -ErrorAction SilentlyContinue
}
if (-not $python) {
Write-Error "Python not found. Please install Python 3.8+ from https://python.org"
}
Write-Host "Found Python: $($python.Source)" -ForegroundColor Green
# Find libclang
$libclang = Get-ChildItem "$($espToolchain.FullName)" -Recurse -Filter "libclang.dll" -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($libclang) {
Write-Host "Found libclang: $($libclang.FullName)" -ForegroundColor Green
} else {
Write-Host "Warning: libclang.dll not found in toolchain" -ForegroundColor Yellow
}
# Install espflash if not present
$espflash = Get-Command espflash -ErrorAction SilentlyContinue
if (-not $espflash) {
Write-Host "Installing espflash..." -ForegroundColor Yellow
cargo install espflash
if ($LASTEXITCODE -ne 0) {
Write-Error "espflash installation failed"
}
Write-Host "espflash installed successfully" -ForegroundColor Green
} else {
Write-Host "Found espflash: $($espflash.Source)" -ForegroundColor Green
}
# Install ldproxy if not present
$ldproxy = Get-Command ldproxy -ErrorAction SilentlyContinue
if (-not $ldproxy) {
Write-Host "Installing ldproxy..." -ForegroundColor Yellow
cargo install ldproxy
if ($LASTEXITCODE -ne 0) {
Write-Error "ldproxy installation failed"
}
Write-Host "ldproxy installed successfully" -ForegroundColor Green
}
Write-Host ""
Write-Host "=== Setup Complete ===" -ForegroundColor Green
Write-Host ""
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host " Toolchain: $($espToolchain.Name)"
Write-Host " Python: $($python.Source)"
if ($libclang) {
Write-Host " Libclang: $($libclang.FullName)"
}
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host " 1. Run: .\build.ps1"
Write-Host " 2. Connect ESP32 via USB"
Write-Host " 3. Run: .\flash.ps1"
Write-Host ""

View file

@ -0,0 +1,124 @@
# build.ps1 - Auto-configure and build RuvLLM ESP32
# Automatically detects toolchain paths - no manual configuration needed
param(
[string]$Target = "xtensa-esp32-espidf",
[switch]$Release = $true,
[string]$Features = ""
)
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Build ===" -ForegroundColor Cyan
Write-Host ""
# Auto-detect paths
$rustupHome = if ($env:RUSTUP_HOME) { $env:RUSTUP_HOME } else { "$env:USERPROFILE\.rustup" }
$cargoHome = if ($env:CARGO_HOME) { $env:CARGO_HOME } else { "$env:USERPROFILE\.cargo" }
# Find ESP toolchain
$espToolchain = (Get-ChildItem "$rustupHome\toolchains" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1)
if (-not $espToolchain) {
Write-Error "ESP toolchain not found. Run .\setup.ps1 first"
}
$espToolchainPath = $espToolchain.FullName
# Find libclang dynamically
$libclang = Get-ChildItem "$espToolchainPath" -Recurse -Filter "libclang.dll" -ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $libclang) {
Write-Error "libclang.dll not found in $espToolchainPath"
}
# Find Python
$python = Get-Command python -ErrorAction SilentlyContinue
if (-not $python) {
$python = Get-Command python3 -ErrorAction SilentlyContinue
}
if (-not $python) {
Write-Error "Python not found. Please install Python 3.8+"
}
$pythonPath = Split-Path $python.Source
# Find clang and xtensa-esp-elf paths
$clangBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "esp-clang" -ErrorAction SilentlyContinue |
Select-Object -First 1
$clangBinPath = if ($clangBin) { "$($clangBin.FullName)\bin" } else { "" }
$xtensaBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "xtensa-esp-elf" -ErrorAction SilentlyContinue |
Select-Object -First 1
$xtensaBinPath = if ($xtensaBin) { "$($xtensaBin.FullName)\bin" } else { "" }
# Set environment variables
$env:LIBCLANG_PATH = Split-Path $libclang.FullName
$env:RUSTUP_TOOLCHAIN = "esp"
$env:ESP_IDF_VERSION = "v5.1.2"
# Build PATH with all required directories
$pathParts = @(
$pythonPath,
"$pythonPath\Scripts",
$clangBinPath,
$xtensaBinPath,
"$cargoHome\bin"
) | Where-Object { $_ -ne "" }
$env:PATH = ($pathParts -join ";") + ";" + $env:PATH
Write-Host "Build Configuration:" -ForegroundColor Gray
Write-Host " Target: $Target"
Write-Host " Release: $Release"
Write-Host " Toolchain: $($espToolchain.Name)"
Write-Host " LIBCLANG_PATH: $($env:LIBCLANG_PATH)"
Write-Host ""
# Navigate to project directory
$projectDir = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
Push-Location $projectDir
try {
# Build cargo command
$cargoArgs = @("build")
if ($Release) {
$cargoArgs += "--release"
}
if ($Features) {
$cargoArgs += "--features"
$cargoArgs += $Features
}
Write-Host "Running: cargo $($cargoArgs -join ' ')" -ForegroundColor Gray
Write-Host ""
& cargo @cargoArgs
if ($LASTEXITCODE -ne 0) {
throw "Build failed with exit code $LASTEXITCODE"
}
Write-Host ""
Write-Host "Build successful!" -ForegroundColor Green
# Find the built binary
$buildDir = if ($Release) { "release" } else { "debug" }
$binary = Get-ChildItem "$projectDir\target\$Target\$buildDir" -Filter "*.elf" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notmatch "deps" } |
Select-Object -First 1
if ($binary) {
Write-Host "Binary: $($binary.FullName)" -ForegroundColor Cyan
}
Write-Host ""
Write-Host "Next: Run .\flash.ps1 to flash to device" -ForegroundColor Yellow
} finally {
Pop-Location
}

View file

@ -0,0 +1,60 @@
# env.ps1 - Set up ESP32 Rust environment for the current session
# Source this script: . .\env.ps1
$ErrorActionPreference = "SilentlyContinue"
# Find paths
$rustupHome = if ($env:RUSTUP_HOME) { $env:RUSTUP_HOME } else { "$env:USERPROFILE\.rustup" }
$cargoHome = if ($env:CARGO_HOME) { $env:CARGO_HOME } else { "$env:USERPROFILE\.cargo" }
# Find ESP toolchain
$espToolchain = (Get-ChildItem "$rustupHome\toolchains" -Directory |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1)
if (-not $espToolchain) {
Write-Host "ESP toolchain not found. Run setup.ps1 first." -ForegroundColor Red
return
}
$espToolchainPath = $espToolchain.FullName
# Find libclang
$libclang = Get-ChildItem "$espToolchainPath" -Recurse -Filter "libclang.dll" |
Select-Object -First 1
# Find clang bin
$clangBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "esp-clang" |
Select-Object -First 1
# Find xtensa-esp-elf bin
$xtensaBin = Get-ChildItem "$espToolchainPath" -Recurse -Directory -Filter "xtensa-esp-elf" |
Select-Object -First 1
# Find Python
$python = Get-Command python -ErrorAction SilentlyContinue
$pythonPath = if ($python) { Split-Path $python.Source } else { "" }
# Set environment variables
$env:LIBCLANG_PATH = if ($libclang) { Split-Path $libclang.FullName } else { "" }
$env:RUSTUP_TOOLCHAIN = "esp"
$env:ESP_IDF_VERSION = "v5.1.2"
# Build PATH
$pathAdditions = @()
if ($pythonPath) { $pathAdditions += $pythonPath; $pathAdditions += "$pythonPath\Scripts" }
if ($clangBin) { $pathAdditions += "$($clangBin.FullName)\bin" }
if ($xtensaBin) { $pathAdditions += "$($xtensaBin.FullName)\bin" }
$pathAdditions += "$cargoHome\bin"
$env:PATH = ($pathAdditions -join ";") + ";" + $env:PATH
# Display status
Write-Host ""
Write-Host "ESP32 Rust environment loaded" -ForegroundColor Green
Write-Host ""
Write-Host " RUSTUP_TOOLCHAIN: $($env:RUSTUP_TOOLCHAIN)" -ForegroundColor Gray
Write-Host " LIBCLANG_PATH: $($env:LIBCLANG_PATH)" -ForegroundColor Gray
Write-Host " ESP_IDF_VERSION: $($env:ESP_IDF_VERSION)" -ForegroundColor Gray
Write-Host ""
Write-Host "Ready to build! Run: .\build.ps1" -ForegroundColor Cyan

View file

@ -0,0 +1,99 @@
# flash.ps1 - Auto-detect COM port and flash RuvLLM ESP32
# Automatically finds connected ESP32 devices
param(
[string]$Port = "",
[switch]$Monitor = $true,
[string]$Target = "xtensa-esp32-espidf",
[switch]$Release = $true
)
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Flash ===" -ForegroundColor Cyan
Write-Host ""
# Auto-detect COM port if not specified
if (-not $Port) {
# Get available COM ports
Add-Type -AssemblyName System.IO.Ports
$ports = [System.IO.Ports.SerialPort]::GetPortNames() |
Where-Object { $_ -match "COM\d+" } |
Sort-Object { [int]($_ -replace "COM", "") }
if ($ports.Count -eq 0) {
Write-Error "No COM ports found. Is the ESP32 connected via USB?"
} elseif ($ports.Count -eq 1) {
$Port = $ports[0]
Write-Host "Auto-detected port: $Port" -ForegroundColor Green
} else {
Write-Host "Multiple COM ports found:" -ForegroundColor Yellow
Write-Host ""
for ($i = 0; $i -lt $ports.Count; $i++) {
Write-Host " [$i] $($ports[$i])"
}
Write-Host ""
$selection = Read-Host "Select port (0-$($ports.Count - 1))"
if ($selection -match "^\d+$" -and [int]$selection -lt $ports.Count) {
$Port = $ports[[int]$selection]
} else {
Write-Error "Invalid selection"
}
}
}
Write-Host "Using port: $Port" -ForegroundColor Cyan
Write-Host ""
# Find binary
$projectDir = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
$buildDir = if ($Release) { "release" } else { "debug" }
$targetDir = "$projectDir\target\$Target\$buildDir"
# Look for ELF or binary file
$binary = Get-ChildItem $targetDir -Filter "*.elf" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notmatch "deps" } |
Select-Object -First 1
if (-not $binary) {
$binary = Get-ChildItem $targetDir -Filter "ruvllm-esp32*" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notmatch "\." -or $_.Name -match "\.elf$" } |
Select-Object -First 1
}
if (-not $binary) {
Write-Host "Available files in $targetDir`:" -ForegroundColor Yellow
Get-ChildItem $targetDir -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " $($_.Name)" }
Write-Error "No binary found. Run .\build.ps1 first"
}
Write-Host "Binary: $($binary.Name)" -ForegroundColor Gray
Write-Host ""
# Check for espflash
$espflash = Get-Command espflash -ErrorAction SilentlyContinue
if (-not $espflash) {
Write-Error "espflash not found. Run .\setup.ps1 first"
}
# Build espflash command
$espflashArgs = @("flash", "--port", $Port, $binary.FullName)
if ($Monitor) {
$espflashArgs += "--monitor"
}
Write-Host "Flashing..." -ForegroundColor Cyan
Write-Host "Command: espflash $($espflashArgs -join ' ')" -ForegroundColor Gray
Write-Host ""
# Flash the device
& espflash @espflashArgs
if ($LASTEXITCODE -ne 0) {
Write-Error "Flash failed with exit code $LASTEXITCODE"
}
Write-Host ""
Write-Host "Flash complete!" -ForegroundColor Green

View file

@ -0,0 +1,41 @@
# monitor.ps1 - Open serial monitor for ESP32
# Auto-detects COM port
param(
[string]$Port = "",
[int]$Baud = 115200
)
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Serial Monitor ===" -ForegroundColor Cyan
Write-Host ""
# Auto-detect COM port if not specified
if (-not $Port) {
Add-Type -AssemblyName System.IO.Ports
$ports = [System.IO.Ports.SerialPort]::GetPortNames() |
Where-Object { $_ -match "COM\d+" } |
Sort-Object { [int]($_ -replace "COM", "") }
if ($ports.Count -eq 0) {
Write-Error "No COM ports found. Is the ESP32 connected?"
} elseif ($ports.Count -eq 1) {
$Port = $ports[0]
Write-Host "Auto-detected port: $Port" -ForegroundColor Green
} else {
Write-Host "Multiple COM ports found:" -ForegroundColor Yellow
for ($i = 0; $i -lt $ports.Count; $i++) {
Write-Host " [$i] $($ports[$i])"
}
$selection = Read-Host "Select port (0-$($ports.Count - 1))"
$Port = $ports[[int]$selection]
}
}
Write-Host "Opening monitor on $Port at $Baud baud..." -ForegroundColor Cyan
Write-Host "Press Ctrl+C to exit" -ForegroundColor Gray
Write-Host ""
# Use espflash monitor
& espflash monitor --port $Port --baud $Baud

View file

@ -0,0 +1,118 @@
# setup.ps1 - One-time Windows setup for RuvLLM ESP32
# Run this once to install/configure the ESP32 Rust toolchain
$ErrorActionPreference = "Stop"
Write-Host "`n=== RuvLLM ESP32 Windows Setup ===" -ForegroundColor Cyan
Write-Host ""
# Find Rust ESP toolchain dynamically
$rustupHome = if ($env:RUSTUP_HOME) { $env:RUSTUP_HOME } else { "$env:USERPROFILE\.rustup" }
$cargoHome = if ($env:CARGO_HOME) { $env:CARGO_HOME } else { "$env:USERPROFILE\.cargo" }
# Check if Rust is installed
$rustc = Get-Command rustc -ErrorAction SilentlyContinue
if (-not $rustc) {
Write-Host "Rust not found. Installing rustup..." -ForegroundColor Yellow
Invoke-WebRequest -Uri "https://win.rustup.rs/x86_64" -OutFile rustup-init.exe
.\rustup-init.exe -y --default-toolchain stable
Remove-Item rustup-init.exe
$env:PATH = "$cargoHome\bin;" + $env:PATH
Write-Host "Rust installed successfully" -ForegroundColor Green
}
# Find or install ESP toolchain
$espToolchain = Get-ChildItem "$rustupHome\toolchains" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1
if (-not $espToolchain) {
Write-Host "ESP toolchain not found. Installing espup..." -ForegroundColor Yellow
# Download espup
$espupUrl = "https://github.com/esp-rs/espup/releases/latest/download/espup-x86_64-pc-windows-msvc.exe"
$espupPath = "$env:TEMP\espup.exe"
Write-Host "Downloading espup..." -ForegroundColor Gray
Invoke-WebRequest -Uri $espupUrl -OutFile $espupPath
Write-Host "Running espup install (this may take several minutes)..." -ForegroundColor Gray
& $espupPath install
if ($LASTEXITCODE -ne 0) {
Write-Error "espup install failed with exit code $LASTEXITCODE"
}
Remove-Item $espupPath -ErrorAction SilentlyContinue
# Re-check for toolchain
$espToolchain = Get-ChildItem "$rustupHome\toolchains" -Directory |
Where-Object { $_.Name -like "esp*" } |
Select-Object -First 1
}
if (-not $espToolchain) {
Write-Error "ESP toolchain installation failed. Please install manually: https://esp-rs.github.io/book/"
}
Write-Host "Found ESP toolchain: $($espToolchain.Name)" -ForegroundColor Green
# Find Python
$python = Get-Command python -ErrorAction SilentlyContinue
if (-not $python) {
$python = Get-Command python3 -ErrorAction SilentlyContinue
}
if (-not $python) {
Write-Error "Python not found. Please install Python 3.8+ from https://python.org"
}
Write-Host "Found Python: $($python.Source)" -ForegroundColor Green
# Find libclang
$libclang = Get-ChildItem "$($espToolchain.FullName)" -Recurse -Filter "libclang.dll" -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($libclang) {
Write-Host "Found libclang: $($libclang.FullName)" -ForegroundColor Green
} else {
Write-Host "Warning: libclang.dll not found in toolchain" -ForegroundColor Yellow
}
# Install espflash if not present
$espflash = Get-Command espflash -ErrorAction SilentlyContinue
if (-not $espflash) {
Write-Host "Installing espflash..." -ForegroundColor Yellow
cargo install espflash
if ($LASTEXITCODE -ne 0) {
Write-Error "espflash installation failed"
}
Write-Host "espflash installed successfully" -ForegroundColor Green
} else {
Write-Host "Found espflash: $($espflash.Source)" -ForegroundColor Green
}
# Install ldproxy if not present
$ldproxy = Get-Command ldproxy -ErrorAction SilentlyContinue
if (-not $ldproxy) {
Write-Host "Installing ldproxy..." -ForegroundColor Yellow
cargo install ldproxy
if ($LASTEXITCODE -ne 0) {
Write-Error "ldproxy installation failed"
}
Write-Host "ldproxy installed successfully" -ForegroundColor Green
}
Write-Host ""
Write-Host "=== Setup Complete ===" -ForegroundColor Green
Write-Host ""
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host " Toolchain: $($espToolchain.Name)"
Write-Host " Python: $($python.Source)"
if ($libclang) {
Write-Host " Libclang: $($libclang.FullName)"
}
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host " 1. Run: .\build.ps1"
Write-Host " 2. Connect ESP32 via USB"
Write-Host " 3. Run: .\flash.ps1"
Write-Host ""