fix(mesh): restore esptool's esp32s3 stub instead of routing around it
--erase-all hit the exact same "ROM does not support function erase_flash"
error as a standalone erase_flash — confirmed live: esptool's --erase-all
is implemented as the same full-chip-erase command, not a per-sector loop,
so it needs the stub just as much. The real fix isn't finding another way
around the ROM bootloader's limitations — it's restoring the stub Debian's
package is missing.
Fetched the exact missing file (stub_flasher_32s3.json) from the matching
upstream esptool release tag and installed it directly on archy-x250-dev;
verified live with a read-only `flash_id` that stub mode now loads
("Uploading stub... Running stub..."). Removed --no-stub from flash.rs now
that normal stub-loader mode works correctly (faster and fully-featured vs.
the ROM-only fallback). scripts/self-update.sh now fetches and installs
this same file automatically whenever it's missing, so this isn't a
one-off manual fix tied to a single node.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
0ca8f25b1b
commit
d6019e47a5
@ -686,17 +686,13 @@ const ESPTOOL_CHIP: &str = "esp32s3";
|
|||||||
/// first hiccup, retry once at a conservative baud before giving up.
|
/// first hiccup, retry once at a conservative baud before giving up.
|
||||||
const ESPTOOL_FALLBACK_BAUD: &str = "115200";
|
const ESPTOOL_FALLBACK_BAUD: &str = "115200";
|
||||||
|
|
||||||
/// A standalone `erase_flash` (full-chip erase via a single ROM opcode) does
|
/// `write_flash --erase-all` erases the whole chip before writing, in one
|
||||||
/// NOT work with --no-stub — confirmed live 2026-07-23 against a real
|
/// esptool invocation. This needs the esp32s3 stub flasher loaded (see
|
||||||
/// Heltec V4: "A fatal error occurred: ESP32-S3 ROM does not support
|
/// esptool_global_args' doc comment) — without it, --erase-all hits the
|
||||||
/// function erase_flash." The ESP32-S3 ROM bootloader only implements
|
/// exact same ROM limitation a standalone `erase_flash` does ("ESP32-S3 ROM
|
||||||
/// per-sector erase (used internally as write_flash goes), not a bulk
|
/// does not support function erase_flash", confirmed live 2026-07-23), since
|
||||||
/// full-chip erase opcode — that's a stub-firmware-only feature, and
|
/// esptool's --erase-all is implemented as the same full-chip-erase command,
|
||||||
/// Debian's esptool package has no stub (see esptool_global_args' doc
|
/// not a per-sector loop.
|
||||||
/// comment). `write_flash --erase-all` gets the same "erase everything
|
|
||||||
/// first" outcome our "always erase before write" default requires, but via
|
|
||||||
/// the per-sector mechanism the ROM bootloader actually supports, as one
|
|
||||||
/// esptool invocation instead of two.
|
|
||||||
async fn esptool_erase_and_write(path: &str, image: &Path, job: &Arc<FlashJob>) -> Result<()> {
|
async fn esptool_erase_and_write(path: &str, image: &Path, job: &Arc<FlashJob>) -> Result<()> {
|
||||||
job.set_stage(FlashStage::Writing).await;
|
job.set_stage(FlashStage::Writing).await;
|
||||||
let image_str = image.to_string_lossy().to_string();
|
let image_str = image.to_string_lossy().to_string();
|
||||||
@ -711,28 +707,33 @@ async fn esptool_erase_and_write(path: &str, image: &Path, job: &Arc<FlashJob>)
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// esptool's global flags (--chip/--port/--baud/--no-stub) MUST precede the
|
/// esptool's global flags (--chip/--port/--baud) MUST precede the subcommand
|
||||||
/// subcommand token (erase_flash/write_flash/...) — confirmed live
|
/// token (erase_flash/write_flash/...) — confirmed live 2026-07-23:
|
||||||
/// 2026-07-23: appending `--baud 115200` after the subcommand on the retry
|
/// appending `--baud 115200` after the subcommand on the retry path
|
||||||
/// path produced "esptool: error: unrecognized arguments: --baud 115200"
|
/// produced "esptool: error: unrecognized arguments: --baud 115200" every
|
||||||
/// every time, so the fallback-baud retry never actually got a chance to
|
/// time, so the fallback-baud retry never actually got a chance to run.
|
||||||
/// run. Building global args separately from subcommand args keeps this
|
/// Building global args separately from subcommand args keeps this correct
|
||||||
/// correct by construction instead of relying on call-site ordering.
|
/// by construction instead of relying on call-site ordering.
|
||||||
///
|
///
|
||||||
/// --no-stub is not optional here: Debian's `esptool` package
|
/// Normal stub-loader mode (no --no-stub) needs the esp32s3 stub flasher
|
||||||
/// (4.7.0+dfsg-0.1) ships without the precompiled "stub flasher" blobs
|
/// blob at /usr/lib/python3/dist-packages/esptool/targets/stub_flasher/
|
||||||
/// (stripped for DFSG compliance), so the default stub-loader flash mode
|
/// stub_flasher_32s3.json — Debian's `esptool` package (4.7.0+dfsg-0.1)
|
||||||
/// fails immediately with `FileNotFoundError: ... stub_flasher_32s3.json`
|
/// ships without it (stripped for DFSG compliance: the prebuilt blob has no
|
||||||
/// — also confirmed live. --no-stub talks directly to the ROM bootloader
|
/// buildable-from-source path Debian could verify), so scripts/self-update.sh
|
||||||
/// instead, which doesn't need those files (slower, but actually works on
|
/// fetches the exact same file from the matching upstream esptool release
|
||||||
/// this packaging).
|
/// tag and installs it alongside the apt package (see the esptool install
|
||||||
|
/// step there). --no-stub (talk directly to the ROM bootloader, skip the
|
||||||
|
/// stub) was tried first and works for connecting, but the ROM bootloader
|
||||||
|
/// doesn't implement a full-chip-erase opcode at all — only the stub does —
|
||||||
|
/// so --no-stub broke our "always erase before write" default outright
|
||||||
|
/// rather than just being slower. Restoring the real stub file is the
|
||||||
|
/// correct fix, not routing around its absence.
|
||||||
fn esptool_global_args<'a>(path: &'a str, baud: Option<&'a str>) -> Vec<&'a str> {
|
fn esptool_global_args<'a>(path: &'a str, baud: Option<&'a str>) -> Vec<&'a str> {
|
||||||
let mut args = vec!["--chip", ESPTOOL_CHIP, "--port", path];
|
let mut args = vec!["--chip", ESPTOOL_CHIP, "--port", path];
|
||||||
if let Some(b) = baud {
|
if let Some(b) = baud {
|
||||||
args.push("--baud");
|
args.push("--baud");
|
||||||
args.push(b);
|
args.push(b);
|
||||||
}
|
}
|
||||||
args.push("--no-stub");
|
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -102,6 +102,34 @@ if ! command -v esptool >/dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Debian's esptool package (4.7.0+dfsg-0.1) ships without the precompiled
|
||||||
|
# esp32s3 "stub flasher" blob (stripped for DFSG compliance — no
|
||||||
|
# buildable-from-source path Debian could verify). Without it, esptool's
|
||||||
|
# normal stub-loader mode fails outright (FileNotFoundError), and the ROM
|
||||||
|
# bootloader fallback (--no-stub) doesn't implement a full-chip erase at
|
||||||
|
# all — confirmed live 2026-07-23 flashing a real Heltec V4, both ways.
|
||||||
|
# Fetching the exact same file from the matching upstream esptool release
|
||||||
|
# tag restores full (and correct) flashing behavior — it's the same
|
||||||
|
# open-source codebase, just the one blob Debian's packaging couldn't
|
||||||
|
# include.
|
||||||
|
if command -v esptool >/dev/null 2>&1; then
|
||||||
|
STUB_DIR="/usr/lib/python3/dist-packages/esptool/targets/stub_flasher"
|
||||||
|
STUB_FILE="$STUB_DIR/stub_flasher_32s3.json"
|
||||||
|
if [ ! -f "$STUB_FILE" ]; then
|
||||||
|
log "Fetching esptool's esp32s3 stub flasher (missing from the Debian package)..."
|
||||||
|
ESPTOOL_VERSION=$(esptool version 2>/dev/null | tail -1 | tr -d ' \t')
|
||||||
|
if [ -n "$ESPTOOL_VERSION" ] && sudo curl -fsSL -o "$STUB_FILE" \
|
||||||
|
"https://raw.githubusercontent.com/espressif/esptool/v${ESPTOOL_VERSION}/esptool/targets/stub_flasher/stub_flasher_32s3.json" \
|
||||||
|
2>>"$LOG_FILE"; then
|
||||||
|
sudo chmod 644 "$STUB_FILE"
|
||||||
|
ok "esp32s3 stub flasher installed"
|
||||||
|
else
|
||||||
|
sudo rm -f "$STUB_FILE" 2>/dev/null
|
||||||
|
warn "Unable to fetch esp32s3 stub flasher; LoRa firmware flashing will be unavailable"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Build-time prerequisites for reticulum-daemon/build.sh's PyInstaller step
|
# Build-time prerequisites for reticulum-daemon/build.sh's PyInstaller step
|
||||||
# below (discovered the hard way: ensurepip needs python3-venv, and
|
# below (discovered the hard way: ensurepip needs python3-venv, and
|
||||||
# PyInstaller itself needs objdump + libpython3.13.so at build time — none
|
# PyInstaller itself needs objdump + libpython3.13.so at build time — none
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user