Flash
Reflash a box from RustWrites new firmware onto a box's two chips (see Flashing), rebooting a chip into download mode then writing the image. Behind the flash Cargo feature, off by default.
cargo add medius --features flashWith the feature off, none of the medius::flash items below exist.
What you need first
esptool.py, the chip, the addressesptool.py must be on PATH as the script, not a bare esptool binary (else Error::FlashTool).
flash exists on Linux and Windows only, compiled out on macOS. The consts and esptool_args are always present.
The four consts are public:
const ESPTOOL: &str
const CHIP: &str
const FLASH_ADDR: &str
const ROM_SETTLE: Duration
use medius::flash;
println!("tool: {}", flash::ESPTOOL); // esptool.py
println!("chip: {}", flash::CHIP); // esp32s3
println!("address: {}", flash::FLASH_ADDR); // 0x10000
println!("settle: {:?}", flash::ROM_SETTLE); // 2sflash
Reboot into download mode, then write the imagefn flash(port: &str, bin_path: impl AsRef<Path>, host: bool) -> Result<()>
Blocks
| Parameter | Type | Description |
|---|---|---|
port | &str | Serial port the box is on, for example /dev/ttyACM0. |
bin_path | impl AsRef<Path> | Path to the firmware image to write. |
host | bool | Picks the chip. false flashes the device chip, true the host chip. |
The host flag picks the reboot target: false is RebootTarget::DeviceDownload, true is RebootTarget::HostDownload (see reboot for the full set).
One call reboots the chosen chip into download mode, frees the port, then runs esptool.py write_flash; the firmware-side sequence is on Flashing.
Blocks for the wait plus the tool's runtime, returning Ok(()) on a clean exit else Err(Error::FlashTool).
use medius::flash;
// false -> device chip
flash::flash("/dev/ttyACM0", "device.bin", false)?;
// true -> host chip
flash::flash("/dev/ttyACM0", "host.bin", true)?;Inspecting the command
See exactly what esptool runsfn esptool_args(port: &str, bin_path: &Path) -> Vec<String>
No round-trip
Builds the exact argv flash passes to esptool.py, without running it. To debug, reboot the chip into download mode via reboot and run these args by hand.
use std::path::Path;
use medius::flash;
let argv = flash::esptool_args("/dev/ttyACM0", Path::new("device.bin"));
println!("esptool.py {}", argv.join(" "));
// esptool.py --chip esp32s3 --port /dev/ttyACM0 --before no_reset
// --after hard_reset write_flash 0x10000 device.binWhen it fails
Error::FlashToolThe feature adds Error::FlashTool(String) to the Error enum. It fires when esptool.py can't be spawned (not on PATH) or exits non-zero, the inner String carrying the stderr tail.
use medius::{flash, Error};
match flash::flash("/dev/ttyACM0", "device.bin", false) {
Ok(()) => println!("flashed"),
Err(Error::FlashTool(msg)) => {
eprintln!("flash failed: {msg}");
eprintln!("hint: is esptool.py on PATH and the port correct?");
}
Err(e) => eprintln!("other error: {e}"),
}Flashing in tests
Swap the runner, skip the hardwareflash wraps flash_with; pass a fake CommandRunner and a no-op reboot closure to test without hardware.
fn flash_with<R, F>(port: &str, bin_path: &Path, host: bool, runner: &R, reboot: F) -> Result<()> where R: CommandRunner, F: FnOnce(&str, bool) -> Result<()>
Blocks
// A fake runner records the argv instead of running esptool; the reboot is a no-op.
flash::flash_with("/dev/ttyACM0", bin, false, &recording_runner, |_port, _host| Ok(()))?;