<!-- Source: https://medius.k4tech.net/library/features/flash -->
# Flash

_Reflash a box from Rust_

Writes new firmware onto a box's two chips (see [Flashing](/native/flashing.md)), rebooting a chip into download mode then writing the image. Behind the `flash` Cargo feature, off by default.

```bash
cargo add medius --features flash
```

With the feature off, none of the `medius::flash` items below exist.

## What you need first

_esptool.py, the chip, the address_

[`esptool.py`](https://github.com/espressif/esptool) must be on `PATH` as the script, not a bare `esptool` binary (else [`Error::FlashTool`](/library/features/flash.md#errors)).

> **Warning**
>
> [`flash`](/library/features/flash.md#flash) exists on Linux and Windows only, compiled out on macOS. The consts and [`esptool_args`](/library/features/flash.md#args) are always present.

The four consts are public:

```text
const ESPTOOL: &str
```

```text
const CHIP: &str
```

```text
const FLASH_ADDR: &str
```

```text
const ROM_SETTLE: Duration
```

#### EXAMPLE

```rust
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); // 2s
```

## flash

_Reboot into download mode, then write the image_

```text
fn flash(port: &str, bin_path: impl AsRef<Path>, host: bool) -> Result<()>
```

_Blocks_

#### PARAMETERS

| 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`](/library/types/enums.md#reboot-target), `true` is [`RebootTarget::HostDownload`](/library/types/enums.md#reboot-target) (see [`reboot`](/library/admin.md#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](/native/flashing.md#two-chips).

Blocks for the wait plus the tool's runtime, returning `Ok(())` on a clean exit else [`Err(Error::FlashTool)`](/library/features/flash.md#errors).

#### EXAMPLE

```rust
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 runs_

```text
fn esptool_args(port: &str, bin_path: &Path) -> Vec<String>
```

_No round-trip_

Builds the exact argv [`flash`](/library/features/flash.md#flash) passes to `esptool.py`, without running it. To debug, reboot the chip into download mode via [`reboot`](/library/admin.md#reboot) and run these args by hand.

#### EXAMPLE

```rust
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.bin
```

## When it fails

_Error::FlashTool_

The feature adds `Error::FlashTool(String)` to the [`Error`](/library/types/errors.md) enum. It fires when `esptool.py` can't be spawned (not on `PATH`) or exits non-zero, the inner `String` carrying the stderr tail.

#### EXAMPLE

```rust
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 hardware_

[`flash`](/library/features/flash.md#flash) wraps `flash_with`; pass a fake `CommandRunner` and a no-op reboot closure to test without hardware.

```text
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_

#### EXAMPLE

```rust
// 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(()))?;
```
