flash-tool · v0.9.x
Docs · Commands

`phpflash flash`

Build the firmware if needed and write it to the board. Two guardrails bracket the write: a chip-versus-board check before it, and a storage-partition wipe after a microSD flash so a leftover embedded image cannot shadow the card.

phpflash flash writes the built firmware image to the connected board. It reads the project config, resolves a serial port, checks the connected chip matches the project's board, drives idf.py flash (which builds first if the tree is stale), and — for a microsd project — erases the board's storage partition afterward. Run it from the project directory.

bash terminal — flash
phpflash flash            # autodetect the port
phpflash flash -p /dev/ttyACM0
phpflash flash --force    # skip the chip-vs-board check

Flags

Flag Meaning
-p, --port <port> Serial port. Empty tries /dev/ttyACM*, then /dev/ttyUSB*, then lets ESP-IDF autodetect.
--force Flash even if the connected chip does not match the project's board (skips the board check).
--idf-path <dir> ESP-IDF location (overrides config, env and default).
--php-esp32-path <dir> php-esp32 location.

Which port it uses

The port is resolved in a fixed order, and the first source that yields a value wins:

  1. 01

    `-p` / `--port`

    — an explicit flag always wins.

  2. 02

    `[board].port`

    — the project config's serial port, if set.

  3. 03

    First serial device that exists

    — the lowest-numbered /dev/ttyACM* (the pattern the P4-Pico's CH343P bridge enumerates as), then /dev/ttyUSB*.

  4. 04

    Autodetect

    — if none of the above matched, the port is left empty and ESP-IDF autodetects.

Detection only checks that the device node exists; it never opens a port, so it cannot hang on or disturb a device that is not the board.

The pre-flash board check

Before writing, and unless --force is passed, flash probes the connected chip and compares it to the project board's ESP-IDF target. This stops you from flashing an image built for one chip onto a different one — an esp32s3 image onto a connected P4, say — which esptool would otherwise fail on cryptically, mid-write.

The probe runs esptool.py --chip auto flash_id against the port and reads the chip's ESP-IDF target (ESP32-P4 becomes esp32p4). The check is deliberately lenient, and only a confirmed mismatch aborts:

  1. 01

    Board target unresolved.

    If the project's board is not tied to a family target, there is nothing to compare against, and the flash proceeds.

  2. 02

    Probe could not run.

    A board that is not there yet, a busy port, or any probe error does not block the flash — esptool verifies the chip again during the actual write, so it stays the backstop.

  3. 03

    Targets match.

    The flash proceeds, after printing board check: <chip> (<target>) matches the project's <board>.

  4. 04

    Targets differ.

    The flash is refused with a message naming both the project's target and the connected chip, and pointing at [board].target, plugging in the right board, or --force.

text terminal — a confirmed mismatch aborts
board mismatch: this project targets "esp32s3" (esp32-s3-eth), but the connected chip is ESP32-P4 (esp32p4).
  Fix [board].target in php-esp32.config.toml, plug in the right board, or re-run with --force.

Two guardrails bracket the write

flash checks the connected chip against the project's board before writing and refuses on a confirmed mismatch — pass --force to override, for instance when two boards in the same family differ in a way the probe cannot see. After a microsd flash it also wipes the board's storage partition, so a leftover embedded image cannot mount and shadow the card. Both run for you; neither needs a flag to switch on.

The write, and the storage cleanup

With the check passed, flash runs idf.py flash into the project's own build tree. idf.py builds first if the tree is stale, so a flash on an unbuilt or changed project compiles before it writes — there is no separate build step you must remember.

After the write, and only for a microsd project (any storage_type that is not embedded), flash erases the board's storage partition:

bash terminal — the storage wipe after a microsd flash
==> idf.py flash
==> erasing 'storage' partition (microsd project: stops a leftover embedded image from shadowing the card)

The storage partition is the read-only FAT-image slot an embedded build flashes the PHP source into. A microsd project never writes it, so an image left there by an earlier embedded build — of this or another project — survives a microsd flash and still mounts at /app. Because the firmware prefers /app over /sdcard, the board would then silently run that stale source instead of the card, with no error. Erasing the partition guarantees the embedded mount fails and the firmware falls back to the microSD.

The erase is best-effort: it runs parttool erase_partition --partition-name storage, and a board with no storage partition (or one that cannot be reached) is not a flash failure — the error is reported and swallowed rather than returned.

Embedded projects skip the cleanup

The storage wipe runs only when the project's storage_type is not embedded. An embedded project wants its image in the storage partition, so the cleanup does not apply and its source is written there by the flash itself.

After flashing

Open the serial console with phpflash monitor to watch the boot log. On a networked board the log prints the address it came up on, which is where a web-server project is reachable.