Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Typical 8-Step Workflow

This is the standard end-to-end flow your AI assistant runs when you ask it to build firmware. Read it once and the rest of the manual makes a lot more sense.

1. Assistant reads install://overview              → confirms your setup
2. Assistant runs doctor                           → checks everything is healthy
3. Assistant runs store_versions                   → sees IDF v5.3.1 is available
4. Assistant runs project.init (target: esp32s3)   → writes .espctl.toml
5. Assistant runs build (target: esp32s3)          → returns task_id
6. Assistant watches build.status until succeeded  → tracks progress
7. Assistant runs logs.tail to show build output   → shows you what happened
8. Assistant runs artifacts.manifest               → shows firmware size + flashable files

Here’s what each step does and why.


1. Read install://overview

Assistant → espctl: read("install://overview")
espctl → Assistant: env-var table, modes, basic setup

espctl ships its own setup guide as a resource. Reading it once at the start of a session gives the assistant an immediate picture of:

  • Which env vars you’ve set (and which you haven’t).
  • Whether it’s in remote-build mode (the default) or plan-only mode.
  • The list of AI tools and their config snippet URLs.

This is also a good first move when troubleshooting — if the resource is unreachable, espctl itself isn’t running, which is the kind of “obvious in hindsight” detail an assistant might miss without checking.

2. Run doctor

Assistant → espctl: doctor
espctl → Assistant: { status: "healthy", checks: [...], errors: [] }

doctor runs a handful of health checks (build server reachable, access key valid, project settings parse, IDF versions match). If anything is wrong, it fails fast with a structured error pointing at the offending check.

Run this every time you start a new session, even if it worked yesterday. Catches the most common “wait, why isn’t it working?” failures before you try to do real work.

3. List build server versions

Assistant → espctl: store_versions
espctl → Assistant: { versions: ["v5.2.2", "v5.3.1"], default: "v5.3.1" }

Confirms which IDF version a build will use by default and shows the alternatives. If your project pins a specific version in .espctl.toml, the assistant will note any mismatch and either use the pin or fall back to the default depending on what idf_select_version decides.

4. Initialize the project

Assistant → espctl: project.init { target: "esp32s3" }
espctl → Assistant: { project_root: "...", config_path: ".espctl.toml", ... }

Creates .espctl.toml and the build subfolder. Safe to run twice — if the project is already set up, this does nothing.

If you’re working on an existing project, skip this step. The assistant will still run validate_config against the existing .espctl.toml to make sure nothing’s broken.

5. Start the build

Assistant → espctl: build { target: "esp32s3", profile: "release" }
espctl → Assistant: { task_id: "0abf...e2", status: "pending" }

The build is sent to the build server and starts running in a sandbox. You get a task_id right away — the build itself runs in the background.

6. Watch until done

loop:
  Assistant → espctl: build.status { task_id: "0abf...e2" }
  espctl → Assistant: { status: "running", phase: "compiling", progress: 0.42 }
  wait 2s
until status == "succeeded" or "failed"

Most assistants check every 1–3 seconds. A more efficient pattern is to subscribe to the build://log/{task_id} resource and get pushed updates instead — but checking is simple and works everywhere.

7. Read the logs

Assistant → espctl: logs.tail { task_id: "0abf...e2", lines: 100 }
espctl → Assistant: { lines: [{ seq, ts, stream, text }, ...] }

Once the build finishes, pull the last N lines of output. This is what your assistant shows you as “the build log”.

If the build failed, your assistant will also run parse_build_errors to extract structured error messages — much more useful than dumping 500 lines of raw output.

8. Read the manifest

Assistant → espctl: artifacts.manifest { task_id: "0abf...e2" }
espctl → Assistant: { artifacts: [...], flash_size, flash_freq, ... }

The manifest is the official record of what the build produced and how to flash it. Your assistant can stream individual .bin files to your local disk for flashing, or hand them straight to the esphome.cloud web flasher.

Security note: Your compiled firmware may contain embedded secrets (Wi-Fi credentials, API keys). Treat .bin files as sensitive.


Variations

This is the happy path. Real workflows often diverge:

  • Build fails at step 6 → Assistant runs parse_build_errors against the log, then the diagnose-build-error prompt. You get a structured “this is what’s wrong, here’s the fix” instead of a wall of text.
  • You change the chip target → Insert a set_target call between steps 4 and 5. The assistant warns you that this clears the build cache.
  • You need an interactive serial monitor after the build → Assistant uses the Monitor tab or espctl monitor --port /dev/ttyUSB0.
  • You want to know what the build would do without running it → Replace step 5 (build) with generate_build_plan. No side effects.

See Browser Wizard for the same flow when you’re clicking through a web page instead of chatting with an assistant, or MCP Console if you want to call the tools by hand.