The smallest possible AI personal assistant for ESP32.
zclaw is written in C and runs on ESP32 boards with a strict all-in firmware budget target of <= 888 KiB on the default build. It supports scheduled tasksGPIO controlpersistent memoryand custom tool composition through natural language.
The 888 KiB cap is all-in firmware sizenot just app code.
It includes zclaw logic plus ESP-IDF/FreeRTOS runtimeWi-Fi/networkingTLS/cryptoand cert bundle overhead.
Fun to usefun to hack on.
Use the docs site for complete guides and reference.
- Full documentation
- Local Admin Console
- Use cases: useful + fun
- Changelog (web)
- Complete README (verbatim)
One-line bootstrap (macOS/Linux):
bash <(curl -fsSL https://raw.githubusercontent.com/tnm/zclaw/main/scripts/bootstrap.sh)Already cloned?
./install.shNon-interactive install:
./install.sh -ySetup notes
bootstrap.shclones/updates the repo and then runs./install.sh. You can inspect/verify the bootstrap flow first (includingZCLAW_BOOTSTRAP_SHA256integrity checks); see the Getting Started docs.- Linux dependency installs auto-detect
apt-getpacmandnforzypperduringinstall.shruns. - In non-interactive modeunanswered install prompts default to
nounless you pass-y(or saved preferences/explicit flags apply). - For encrypted credentials in flashuse secure mode (
--flash-mode securein install flowor./scripts/flash-secure.shdirectly). - After flashingprovision WiFi + LLM credentials with
./scripts/provision.sh. - You can re-run either
./scripts/provision.shor./scripts/provision-dev.shat any time (no reflash required) to update runtime credentials: WiFi SSID/passwordLLM backend/model/API key (or Ollama API URL)and Telegram token/chat ID allowlist. - Default LLM rate limits are
100/hourand1000/day; change compile-time limits inmain/config.h(RATELIMIT_*). - Quick validation path: run
./scripts/web-relay.shand send a test message to confirm the device can answer. - If serial port is busyrun
./scripts/release-port.shand retry. - For repeat local reprovisioning without retyping secretsuse
./scripts/provision-dev.shwith a local profile file (provision-dev.shwrapsprovision.sh --yes).
- Chat via Telegram or hosted web relay
- Timezone-aware schedules (
dailyperiodicand one-shotonce) - Built-in + user-defined tools
- For brand-new built-in capabilitiesadd a firmware tool (C handler + registry entry) via the Build Your Own Tool docs.
- Runtime diagnostics via
get_diagnostics(quick/runtime/memory/rates/time/all scopes) - GPIO read/write control with guardrails (including bulk
gpio_read_all) - USB local admin console for recoverysafe modeand pre-network bring-up
- Persistent memory across reboots
- Persona options:
neutralfriendlytechnicalwitty - Provider support for AnthropicOpenAIOpenRouterand Ollama (custom endpoint)
Tested targets: ESP32ESP32-C3ESP32-S3and ESP32-C6. Classic ESP32-WROOM/ESP32 DevKit boards are supported. Test reports for other ESP32 variants are very welcome!
Recommended starter board: Seeed XIAO ESP32-C3
Typical fast loop:
./scripts/test.sh host
./scripts/build.sh
./scripts/flash.sh --kill-monitor /dev/cu.usbmodem1101
./scripts/provision-dev.sh --port /dev/cu.usbmodem1101
./scripts/monitor.sh /dev/cu.usbmodem1101Profile setup oncethen re-use:
./scripts/provision-dev.sh --write-template
# edit ~/.config/zclaw/dev.env
./scripts/provision-dev.sh --show-config
./scripts/provision-dev.sh
# if Telegram keeps replaying stale updates:
./scripts/telegram-clear-backlog.sh --show-configMore details in the Local Dev & Hacking guide.
Show scripts
./scripts/flash-secure.sh- Flash with encryption./scripts/provision.sh- Provision credentials to NVS./scripts/provision-dev.sh- Local profile wrapper for repeat provisioning./scripts/telegram-clear-backlog.sh- Clear queued Telegram updates./scripts/erase.sh- Erase NVS only (--nvs) or full flash (--all) with guardrails./scripts/monitor.sh- Serial monitor./scripts/emulate.sh- Run QEMU profile./scripts/web-relay.sh- Hosted relay + mobile chat UI./scripts/benchmark.sh- Benchmark relay/serial latency./scripts/test.sh- Run host/device test flows./scripts/test-api.sh- Run live provider API checks (manual/local)
When the board is in safe modeunprovisionedor the LLM path is unavailableyou can still operate it over USB serial without Wi-Fi or an LLM round trip.
./scripts/monitor.sh /dev/cu.usbmodem1101
# then type:
/wifi status
/wifi scan
/bootcount
/gpio all
/rebootAvailable local-only commands:
/gpio [all|pin|pin high|pin low]/diag [scope] [verbose]/reboot/wifi [status|scan]/bootcount/factory-reset confirm(destructive; wipes NVS and reboots)
Full reference: Local Admin Console
Current default esp32 breakdown (grouped image bytes from idf.py -B build size-components):
| Segment | Bytes | Size | Share |
|---|---|---|---|
zclaw app logic (libmain.a) |
39276 |
~38.4 KiB | ~4.6% |
| Wi-Fi + networking stack | 378624 |
~369.8 KiB | ~44.4% |
| TLS/crypto stack | 134923 |
~131.8 KiB | ~15.8% |
| cert bundle + app metadata | 98425 |
~96.1 KiB | ~11.5% |
| other ESP-IDF/runtime/drivers/libc | 201786 |
~197.1 KiB | ~23.7% |
Total image size from this build is 853034 bytes; padded zclaw.bin is 853184 bytes (~833.2 KiB)leaving 56128 bytes (~54.8 KiB) under the 888 KiB cap.
Relay path benchmark (includes web relay processing + device round trip):
./scripts/benchmark.sh --mode relay --count 20 --message "ping"Direct serial benchmark (host round trip + first response time). If firmware logs
METRIC request ... linesthe report also includes device-side timing:
./scripts/benchmark.sh --mode serial --serial-port /dev/cu.usbmodem1101 --count 20 --message "ping"MIT
