fix(mesh): esptool --no-stub + fix retry's broken --baud arg ordering
The improved error logging from the last fix immediately paid off — real esptool output on the next attempt showed two distinct bugs: 1. Debian's esptool package (4.7.0+dfsg-0.1) ships without the precompiled "stub flasher" blobs (stripped for DFSG compliance), so the default stub-loader flash mode fails immediately: FileNotFoundError: ... No such file or directory: '.../stub_flasher_32s3.json'. Fixed with --no-stub, which talks directly to the ROM bootloader instead — the standard workaround for this exact Debian packaging gap. 2. The fallback-baud retry appended `--baud 115200` AFTER the subcommand token (erase_flash/write_flash), but esptool requires global flags before the subcommand — every retry failed with "esptool: error: unrecognized arguments: --baud 115200", so the retry logic added earlier never actually got a chance to run. esptool_with_retry now builds global args (--chip/--port/--baud/--no-stub) and subcommand args separately and always concatenates them in the right order, instead of relying on call-site ordering of a single flat arg list. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ff532465cf
commit
a1cb83dfb2
@ -657,36 +657,48 @@ const ESPTOOL_FALLBACK_BAUD: &str = "115200";
|
||||
|
||||
async fn esptool_erase_and_write(path: &str, image: &Path, job: &Arc<FlashJob>) -> Result<()> {
|
||||
job.set_stage(FlashStage::Erasing).await;
|
||||
esptool_with_retry(
|
||||
&["--chip", ESPTOOL_CHIP, "--port", path, "erase_flash"],
|
||||
job,
|
||||
)
|
||||
.await
|
||||
.context("esptool erase_flash failed")?;
|
||||
esptool_with_retry(path, &["erase_flash"], job)
|
||||
.await
|
||||
.context("esptool erase_flash failed")?;
|
||||
|
||||
job.set_stage(FlashStage::Writing).await;
|
||||
let image_str = image.to_string_lossy().to_string();
|
||||
esptool_with_retry(
|
||||
&[
|
||||
"--chip",
|
||||
ESPTOOL_CHIP,
|
||||
"--port",
|
||||
path,
|
||||
"write_flash",
|
||||
"0x0",
|
||||
&image_str,
|
||||
],
|
||||
job,
|
||||
)
|
||||
.await
|
||||
.context("esptool write_flash failed")?;
|
||||
esptool_with_retry(path, &["write_flash", "0x0", &image_str], job)
|
||||
.await
|
||||
.context("esptool write_flash failed")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn esptool_with_retry(args: &[&str], job: &Arc<FlashJob>) -> Result<()> {
|
||||
/// esptool's global flags (--chip/--port/--baud/--no-stub) MUST precede the
|
||||
/// subcommand token (erase_flash/write_flash/...) — confirmed live
|
||||
/// 2026-07-23: appending `--baud 115200` after the subcommand on the retry
|
||||
/// path produced "esptool: error: unrecognized arguments: --baud 115200"
|
||||
/// every time, so the fallback-baud retry never actually got a chance to
|
||||
/// run. Building global args separately from subcommand args keeps this
|
||||
/// correct by construction instead of relying on call-site ordering.
|
||||
///
|
||||
/// --no-stub is not optional here: Debian's `esptool` package
|
||||
/// (4.7.0+dfsg-0.1) ships without the precompiled "stub flasher" blobs
|
||||
/// (stripped for DFSG compliance), so the default stub-loader flash mode
|
||||
/// fails immediately with `FileNotFoundError: ... stub_flasher_32s3.json`
|
||||
/// — also confirmed live. --no-stub talks directly to the ROM bootloader
|
||||
/// instead, which doesn't need those files (slower, but actually works on
|
||||
/// this packaging).
|
||||
fn esptool_global_args<'a>(path: &'a str, baud: Option<&'a str>) -> Vec<&'a str> {
|
||||
let mut args = vec!["--chip", ESPTOOL_CHIP, "--port", path];
|
||||
if let Some(b) = baud {
|
||||
args.push("--baud");
|
||||
args.push(b);
|
||||
}
|
||||
args.push("--no-stub");
|
||||
args
|
||||
}
|
||||
|
||||
async fn esptool_with_retry(path: &str, subcommand: &[&str], job: &Arc<FlashJob>) -> Result<()> {
|
||||
let mut cmd = Command::new("esptool");
|
||||
cmd.args(args);
|
||||
cmd.args(esptool_global_args(path, None));
|
||||
cmd.args(subcommand);
|
||||
match run_streamed(cmd, None, job).await {
|
||||
Ok(()) => Ok(()),
|
||||
Err(first_err) => {
|
||||
@ -695,8 +707,8 @@ async fn esptool_with_retry(args: &[&str], job: &Arc<FlashJob>) -> Result<()> {
|
||||
))
|
||||
.await;
|
||||
let mut retry = Command::new("esptool");
|
||||
retry.args(args);
|
||||
retry.args(["--baud", ESPTOOL_FALLBACK_BAUD]);
|
||||
retry.args(esptool_global_args(path, Some(ESPTOOL_FALLBACK_BAUD)));
|
||||
retry.args(subcommand);
|
||||
run_streamed(retry, None, job)
|
||||
.await
|
||||
.context(format!("retry also failed (first attempt: {first_err:#})"))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user