# Flash it

What idf.py flash writes onto a Kode Dot over USB-C, what it leaves alone, how to read a failure, and how to know the image landed.

## What it is

`idf.py flash` is the one command that puts your build on the Kode Dot. It
writes three things over the USB-C cable: the bootloader, the partition
table and your app, each at its own place in the 32 MB flash. It erases only
the sectors it writes, so settings stored in NVS, Wi-Fi credentials among
them, survive a flash.

A Kode Dot ships with kodeOS, and the bootloader and the partition table are
part of it. A flash replaces both, so **kodeOS stops starting** and your app
is what the Kode Dot runs from then on.

The same cable is the console. `idf.py monitor` shows the boot log and then
your own log lines, and `flash monitor` does both in one go, which is how
you will run it most of the time.

## How it works

```mermaid
flowchart LR
  pc["idf.py flash"] -- "USB-C, USB-Serial-JTAG" --> p4["ESP32-P4"]
  p4 --> bl["0x2000 bootloader"]
  p4 --> pt["0x8000 partition table"]
  p4 --> app["0x10000 app"]
  p4 -. "untouched" .-> nvs["NVS, settings"]
```

*Three regions written over the cable; the rest of the flash is left alone*

The P4 speaks to the computer through its built-in USB-Serial-JTAG, so no
adapter is needed and the same port carries JTAG for a debugger. The play
buttons on this site write the same three regions through the browser's Web
Serial, with the offsets read from a manifest next to the binaries.

## Specifications

| Thing | Value |
| --- | --- |
| Command | `idf.py -p <port> flash`, or `flash monitor` to stay on the log |
| Transport | USB-C, the only one there is |
| Writes | Bootloader at `0x2000`, partition table at `0x8000`, app at `0x10000` |
| Leaves | Everything else, NVS included |
| Speed | `idf.py -b <baud> flash` raises it; a flaky cable is the usual reason to lower it |
| Port on macOS | `/dev/cu.usbmodem*`. It changes on every reconnect and between bootloader and app |
| Erase everything | `idf.py erase-flash`, a separate command, on purpose |
| Other ways on | The marketplace over the same cable from Chrome or Edge, the Kode app over Bluetooth, or the marketplace over Wi-Fi to a Kode Dot that is on your account and online |

## Gotchas

> **WARNING**
>
> **After a flash, kodeOS and the apps installed on it are gone from the
> screen.** The flash replaced kodeOS's bootloader and partition table.
> **Recovery**, top right of these docs, installs kodeOS again and erases the
> whole Kode Dot first. The play buttons on this site do not do this: they
> install the program as an app and leave kodeOS in place.

> **WARNING**
>
> **The flash feels stuck, so you reached for `erase-flash`.** It throws away
> NVS, Wi-Fi credentials included, and rarely fixes anything. A watchdog loop
> in particular is not a flash problem: `start/unbox` has the battery reset
> that clears it.

> **WARNING**
>
> **A P4 straight from the factory refuses to flash.** With no battery the
> P4's backup domain is unpowered and the flasher's stub browns out, with an
> error that points elsewhere. Fit the battery, or flash with `--no-stub`.

> **NOTE**
>
> **The boot log loops and never reaches your code.** The image landed and the
> problem is in it. Read the last lines before the reset; flashing the same
> binary again changes nothing.

## Example code

The commands, in the order you run them, with what each one prints when it
worked.

```sh
# flash.sh - 2026-09-18 - from Kode with love
#
# Build, find the port, flash, and stay on the log. The flash ends with one
# "Hash of data verified" per region; the monitor then shows the bootloader,
# the app's name and version, and your own log lines.
idf.py build
ls /dev/cu.usbmodem*
idf.py -p /dev/cu.usbmodem2101 flash monitor
```

## In the SDK

- [`tools`](https://github.com/kodediy/kode-sdk/tree/main/tools): Kode's flashing and bench scripts in `kode-sdk`.
