chapter 1
Getting Started
Fast path from blank machine to a live ESP32 agent. Defaults are safe, and explicit flags keep runs repeatable.
Basic Hardware
- Tested targets: ESP32-C3, ESP32-S3, and ESP32-C6.
- Recommended starter board: Seeed XIAO ESP32-C3 (USB-C, small footprint, low cost).
- Use a real data USB cable (not charge-only), then connect board to host.
- zclaw's setup scripts will generally find the right serial port without you having to do anything.
One-Line Bootstrap
bash <(curl -fsSL https://raw.githubusercontent.com/tnm/zclaw/main/scripts/bootstrap.sh)
Optional checksum-verified bootstrap (same flow):
ZCLAW_BOOTSTRAP_SHA256="<sha256-from-release-notes>" \ bash <(curl -fsSL https://raw.githubusercontent.com/tnm/zclaw/main/scripts/bootstrap.sh)
Integrity check: when ZCLAW_BOOTSTRAP_SHA256 (or --sha256) is set, bootstrap.sh hashes its own downloaded contents with SHA-256 and compares to your expected value before it clones/updates or runs ./install.sh. Any mismatch exits immediately.
Bootstrap clones or updates the repo, then runs ./install.sh.
- Installer remembers answers in
~/.config/zclaw/install.env(disable with--no-remember). - Interactive flashing defaults to standard mode; encrypted flashing is opt-in with
--flash-mode secure. - On Linux, dependency installs auto-detect
apt-get,pacman,dnf, orzypper; unsupported distros fall back to manual package install guidance.
Want non-interactive install? Use -y and explicit no/yes flags: ./install.sh -y --build --flash --provision --no-qemu --no-cjson. Without -y, unanswered non-interactive prompts default to no.
Common Install Patterns
# Interactive default path ./install.sh # Non-interactive provisioning path ./install.sh -y --build --flash --provision --monitor # Explicit secure flash mode ./install.sh -y --build --flash --flash-mode secure
First Boot Flow
- Flash firmware (
./scripts/flash.shor./scripts/flash-secure.sh). Flash scripts auto-detect chip target and can prompt foridf.py set-target <chip>on mismatch. - Provision credentials (
./scripts/provision.sh --port <serial-port>). - Enter WiFi SSID, LLM provider, and model. Use API key for Anthropic/OpenAI/OpenRouter, or API URL for Ollama. Optional Telegram fields can be filled now or later.
- Reboot and inspect logs with
./scripts/monitor.sh.
ESP32-WROOM/ESP32 DevKit boards run target esp32; local chat uses UART0 automatically on targets without USB Serial/JTAG (no manual CONFIG_ZCLAW_CHANNEL_UART toggle needed).
LLM Provisioning Options
Backend/model/API settings are runtime credentials in NVS, so you can change them any time without reflashing firmware. The install flow prompts for the same values when you run ./install.sh with provisioning enabled.
- anthropic, openai, openrouter: require
--api-key. - ollama: requires
--api-url; API key is optional. - Use
--modelto override defaults on any backend.
# OpenAI ./scripts/provision.sh --port /dev/cu.usbmodem1101 --backend openai --model gpt-5.2 --api-key sk-... # OpenRouter ./scripts/provision.sh --port /dev/cu.usbmodem1101 --backend openrouter --model minimax/minimax-m2.5 --api-key or-... # Ollama (LAN host) ./scripts/provision.sh --port /dev/cu.usbmodem1101 --backend ollama --model qwen3:8b --api-url http://192.168.1.50:11434
For Ollama, use a LAN-reachable host/IP; 127.0.0.1 points to the ESP32 itself and will not reach your laptop/server.
To switch model/provider later, re-run provisioning with new --backend/--model values, or update ZCLAW_BACKEND/ZCLAW_MODEL in provision-dev.sh profile and re-run.
Telegram Path
- Create bot via @BotFather.
- Get chat ID via @userinfobot.
- Provide Telegram credentials during provisioning: use
--tg-tokenand--tg-chat-id(single ID or comma-separated allowlist, up to 4 IDs) for non-interactive runs, or enter them when prompted in interactive provisioning.
Runtime accepts messages only from the configured chat ID allowlist.
To change authorized Telegram chats later, re-run provisioning with a new --tg-chat-id value (either provision.sh or provision-dev.sh).
Telegram control commands: /start//help (help), /settings (status), /stop (pause message intake), /resume (resume message intake).
If old queued messages keep replaying during local dev, run ./scripts/telegram-clear-backlog.sh --show-config once on your host.
First Conversation
Talk to zclaw in normal language. You do not need command syntax.
what are all GPIO states turn GPIO 5 on remind me daily at 8:15 to water plants remember that GPIO 4 controls the arcade machine create a tool called arcade_on that turns GPIO 4 on turn the arcade on in 10 minutes
Example flow:
You: create a tool called arcade_on that turns GPIO 4 on zclaw: Created tool "arcade_on". Say "run arcade_on" anytime. You: turn the arcade on in 10 minutes zclaw: Scheduled it. I will run arcade_on once in 10 minutes.
Persona options are neutral, friendly, technical, and witty.
switch to witty persona what persona are you using
Persona only changes wording/tone, not tool behavior or safety decisions. Persona is saved on-device until changed or reset.
Fast Re-Provision (Local Dev)
# One-time: create local profile template ./scripts/provision-dev.sh --write-template # Edit ~/.config/zclaw/dev.env once, then: ./scripts/provision-dev.sh --show-config ./scripts/provision-dev.sh
This keeps your WiFi/API/Telegram values in a local profile for repeat dev flashes. Output is redacted; only Telegram bot ID is shown as a safe identifier.
What Re-Provision Updates
Both ./scripts/provision.sh and ./scripts/provision-dev.sh write the same runtime config in NVS:
- WiFi: SSID + password
- LLM: backend + model + API key (or Ollama API URL)
- Telegram: bot token + authorized chat ID allowlist
For Ollama, point --api-url to a LAN host (for example http://192.168.1.50:11434), not 127.0.0.1.
No firmware reflash is required for these changes. provision-dev.sh is a convenience wrapper around provision.sh --yes with a local profile.
Web Relay Path
# Device connected ./scripts/web-relay.sh --serial-port /dev/cu.usbmodem1101 --host 127.0.0.1 --port 8787 # No hardware (mock agent) ./scripts/web-relay.sh --mock-agent --host 127.0.0.1 --port 8787
If you bind to a non-loopback host (for example 0.0.0.0), set ZCLAW_WEB_API_KEY first.
Only one process should own the serial port at a time.
# No-clone bootstrap path ZCLAW_WEB_API_KEY='long-random-secret' \ bash <(curl -fsSL https://raw.githubusercontent.com/tnm/zclaw/main/scripts/bootstrap-web-relay.sh) -- --serial-port /dev/cu.usbmodem1101 --host 0.0.0.0 --port 8787
Serial Port Conflicts
# Release ESP-IDF monitor/holders ./scripts/release-port.sh # Relay helper can stop monitor holders automatically ./scripts/web-relay.sh --serial-port /dev/cu.usbmodem1101 --kill-monitor --host 127.0.0.1 --port 8787
Reset / Erase
# Erase only saved credentials/settings (keeps firmware) ./scripts/erase.sh --nvs --port /dev/cu.usbmodem1101 # Full factory wipe (firmware + settings) ./scripts/erase.sh --all --port /dev/cu.usbmodem1101
Guardrails: you must explicitly choose --nvs or --all. Full erase requires confirmation (or --yes in non-interactive runs).
Advanced Board Config
source ~/esp/esp-idf/export.sh idf.py menuconfig
Adjust GPIO safety range/allowlist under zclaw Configuration -> GPIO Tool Safety.
Other Board Notes
Most boards work with the generic target flow, but some need explicit board-safe pin/reset defaults.
If you move between chips (for example, C3/S3 to WROOM), accept the flash-script prompt to run idf.py set-target <chip> (or run it manually once).
# ESP32-S3-BOX-3 preset ./scripts/build.sh --box-3 ./scripts/flash.sh --box-3 /dev/cu.usbmodem1101 # or encrypted flash: ./scripts/flash-secure.sh --box-3 /dev/cu.usbmodem1101
--box-3 applies esp32s3 target, board-safe GPIO allowlist, and factory-reset pin defaults.
If Something Breaks
# ESP-IDF repair cd ~/esp/esp-idf ./install.sh esp32,esp32c3,esp32c6,esp32s3 # Build/test routines ./scripts/build.sh ./scripts/test.sh host