Prompt file imported from andrico21/M5AirQ-Rust (
.github/prompts/esp32-rust-builder.prompt.md). Copyright stays with the author.
--- name: esp32-rust-builder description: > Use to validate ESP32 Rust code, flash firmware, or monitor serial output. Invoke when: building, compiling, checking code, flashing to board, monitoring device, catching panics, or diagnosing boot loops.
ESP32 Rust Build, Flash & Monitor
Four workflows. Default to Flow 1 unless the user explicitly requests flashing or monitoring.
Environment Setup (ALL flows)
Every terminal command MUST begin with toolchain activation:
. "$env:USERPROFILE\export-esp.ps1"
Working directory: z:\TempPersistent\M5AirQualityRust
Binaries -- always absolute paths:
- Cargo:
C:\Users\Andrico\.cargo\bin\cargo - espflash:
C:\Users\Andrico\.cargo\bin\espflash
Target: xtensa-esp32s3-none-elf -- Release profile only (--release).
Device port: COM4 -- never use any other port.
Binary artifact: target\xtensa-esp32s3-none-elf\release\m5-air-quality
PowerShell stderr handling
cargo and espflash emit most output (progress, warnings, errors) to stderr. The 2>&1 redirect merges stderr into stdout so Select-Object can filter it. In PowerShell, redirected stderr lines become ErrorRecord objects -- they still contain the original text but may render in red. When parsing terminal output:
- Treat all output lines as plain text regardless of color/stream origin.
error[E...]in the text means a Rust compiler error -- red coloring alone does NOT indicate failure.- The only reliable success/failure signal is the presence of
Finished(success) orerror[E/error:(failure) in the output text. - Do NOT interpret PowerShell's red-colored stderr lines as errors --
cargoputs normal progress on stderr.
Flow 1: Test Build (Default -- Code Validation)
When asked to build, compile, or check code:
- Run in terminal (non-background, timeout 120000):
. "$env:USERPROFILE\export-esp.ps1"; cd z:\TempPersistent\M5AirQualityRust; C:\Users\Andrico\.cargo\bin\cargo build --release 2>&1 | python scripts/fw_tool.py build BUILD OK: report success with one line.- Error output: the script extracts only error blocks -- no warnings, no progress noise. Read the output, fix the code, rebuild. Repeat until
BUILD OK. Do NOT dump raw errors to the user -- summarize.
Why
cargo build --releaseinstead ofcargo check? The release profile enables LTO andopt-level = "s"which can surface linker errors and size issues thatcheckmisses. The incremental build is fast after the first compile.
Flow 2: Flash to Board (Explicit Request Only)
DO NOT run unless user says "flash", "deploy", or "upload".
- Build first (Flow 1). Stop if build fails.
- Release COM4, then flash (non-background, timeout 60000):
Get-Process -Name espflash,putty,plink -ErrorAction SilentlyContinue | Stop-Process -Force; . "$env:USERPROFILE\export-esp.ps1"; cd z:\TempPersistent\M5AirQualityRust; C:\Users\Andrico\.cargo\bin\espflash flash --port COM4 target\xtensa-esp32s3-none-elf\release\m5-air-quality 2>&1 | python scripts/fw_tool.py flash FLASH OK: report success. Error output: the script extracts only the failure reason.- Common failures (apply to ALL flash/monitor flows):
Failed to open serial port COM4/Access is denied-> COM4 release step missed or a non-standard process holds the port. RunGet-Process | Where-Object { $_.ProcessName -match 'serial|monitor|term' }to find it, thenStop-Process -Force.Unable to connect/Timeout-> device not in bootloader. Hold BOOT button, tap RST, release BOOT.Chip type mismatch-> wrong target, verifyxtensa-esp32s3-none-elf.No serial ports found-> USB cable not connected or driver issue. Check Device Manager for COM4.
Flow 3: Flash + Monitor (Explicit Request Only)
The most common deployment workflow -- flash and immediately tail serial output.
- Build first (Flow 1). Stop if build fails.
- Release COM4, then Flash+Monitor -- log to file (background terminal):
Get-Process -Name espflash,putty,plink -ErrorAction SilentlyContinue | Stop-Process -Force; . "$env:USERPROFILE\export-esp.ps1"; cd z:\TempPersistent\M5AirQualityRust; C:\Users\Andrico\.cargo\bin\espflash flash --monitor --port COM4 target\xtensa-esp32s3-none-elf\release\m5-air-quality 2>&1 | Tee-Object -FilePath serial.log - Start as background process. Wait 15-20 seconds, then analyze:
cd z:\TempPersistent\M5AirQualityRust; python scripts/fw_tool.py serial serial.log CLEAN RUN: device is running normally.- Crash/boot loop output: the script extracts only crash traces with 3 lines of pre-context. Read and diagnose.
- To check again later, re-run the
fw_tool.py serialcommand -- the log file accumulates.
Flow 4: Monitor Only (Explicit Request Only)
When user says "monitor", "watch logs", "check serial", or "catch crash":
- Release COM4, then start monitor -- log to file (background terminal):
Get-Process -Name espflash,putty,plink -ErrorAction SilentlyContinue | Stop-Process -Force; . "$env:USERPROFILE\export-esp.ps1"; cd z:\TempPersistent\M5AirQualityRust; C:\Users\Andrico\.cargo\bin\espflash monitor --port COM4 2>&1 | Tee-Object -FilePath serial.log - Start as background process. Wait the user-specified duration (default 15s), then analyze:
cd z:\TempPersistent\M5AirQualityRust; python scripts/fw_tool.py serial serial.log CLEAN RUN: confirm normal operation. Crash output: diagnose per Panic Analysis below.- To re-check, re-run the
fw_tool.py serialcommand.
Panic & Crash Analysis
When analyzing serial output, look for these patterns:
Rust Panics
panicked at 'message', file:line-- extract file and line, read that code, explain cause.unwrap()onNone/Err-- identify whichunwrap()and suggest proper error handling.index out of bounds-- buffer/array sizing issue, check heapless capacity.Guru Meditation Error-- ESP-IDF fatal exception (rare in pure no_std, but possible from ROM code).abort() was called-- explicit abort, usually from panic handler or alloc failure.Backtrace:-- stack trace follows. Capture all0x...addresses until the next blank line or reboot.
When a crash is found, capture everything from the first crash pattern line up to (and including) the next rst: reset line. That's the full crash trace.
ESP32 Resets
rst:0x1 (POWERON)-- normal power-on.rst:0x3 (SW_RESET)-- software reset (intentional or panic handler).rst:0x8 (TG1WDT_SYS_RST)-- Task watchdog timeout. An async task blocked too long without yielding. Find the blocking operation (busy-wait, long SPI transfer, tight loop withoutTimer::after().await).rst:0xc (RTC_SW_CPU_RST)-- RTC watchdog or brownout. If on battery, check power hold GPIO46 and battery voltage.USB_UART_CHIP_RESET-- USB disconnect or brownout during operation. If repeating = boot loop.
Boot Loop Detection
If the device repeatedly shows the boot banner (ESP-ROM:...) within the monitoring window:
- Check for stack overflow -- increase stack size in main or spawned tasks.
- Check for peripheral init failure -- an
unwrap()on hardware init that fails on second boot. - Check for WiFi connection infinite retry without yield.
- Look at the LAST log line before reset -- that's the crash site.
Memory Issues
esp_alloc: out of memory-- heap exhausted. Reduce allocations, use heapless.StackOverflow-- task stack too small. Increase#[embassy_executor::task(stack_size = N)]or reduce local variables.
Common Embedded Rust Errors (Build-Time)
| Error Pattern | Cause | Fix |
|---|---|---|
trait bound ... Send is not satisfied |
Holding non-Send across await | Restructure to drop before await |
future is not Send |
Non-Send type in async task | Use Rc -> remove, ensure all shared state is Send |
no method named ... found for struct |
Missing feature flag on crate | Check Cargo.toml features |
multiple definitions of ... |
Linker duplicate symbol | Check for duplicate #[entry] or #[global_allocator] |
region 'dram' overflowed |
Too much static/BSS data | Reduce static buffers, use smaller heapless capacities |
undefined reference to ... |
Missing esp-hal feature | Enable the peripheral feature in esp-hal |