Bring the agent closer to the hardware.
ESPClaw is an ESP32-native agent runtime for boards with real sensors, real I/O, and real constraints. It combines:
- an iterative LLM tool loop
- a local workspace and admin UI
- installable Lua apps and reusable Lua components
- persistent tasks, behaviors, and events
- OTA-capable firmware for supported boards
The goal is simple: make an embedded board feel more like a programmable runtime at the hardware boundary than a fixed-function firmware image.
- Browser landing page, flasher, and WebAssembly browser lab: espclaw.dev
- Latest binaries: GitHub Releases
- Manual flashing and OTA notes: docs/ota.md
Most LLM agents assume a server, a filesystem, and effectively unbounded memory. ESPClaw assumes:
- a microcontroller
- tight RAM budgets
- real GPIO, I2C, PWM, camera, Wi-Fi, and storage
- an operator who may be using a browser, UART console, simulator, or Telegram chat
That leads to a different design:
- remote models, local tool execution
- hot-swappable Lua logic instead of reflashing for every iteration
- explicit board profiles and hardware capability discovery
- chunked data paths for large markdown and Lua artifacts
- in-memory log tails over HTTP and tool calls for debugging without serial
- recoverable, inspectable, operator-visible runtime behavior
An app is an installable Lua bundle under /workspace/apps/<app_id>/.
Use apps for:
- weather stations
- camera automations
- robot behaviors
- diagnostics
A component is a reusable Lua module with metadata, installable independently from apps and published into /workspace/lib.
Use components for:
- sensor drivers
- protocol helpers
- control math
- shared business logic
A task is a running instance of an app.
Use tasks for:
- periodic loops
- event-driven handlers
- “run this now” automation
A behavior is a saved task configuration that can autostart after reboot.
Use behaviors for:
- always-on monitoring
- boot-time automation
- persistent schedules
Events connect producers and consumers without hard-wiring everything together.
Use events for:
- sensor fan-out
- UART-triggered flows
- hardware watches
- decoupled app coordination
+----------------------+
| Telegram / Web / |
| UART / Simulator |
+----------+-----------+
|
v
+----------------------+
| Agent Loop |
| tool schemas |
| iterative runs |
| safety / yolo |
+----------+-----------+
|
+--------------+--------------+
| |
v v
+----------------------+ +----------------------+
| Workspace | | Tool Runtime |
| apps | | fs / wifi / gpio |
| components | | i2c / camera / ota |
| sessions / memory | | app / task / event |
+----------+-----------+ +----------+-----------+
| |
+--------------+--------------+
|
v
+----------------------+
| ESP32 Hardware |
+----------------------+
ESPClaw is already usable for real local operator workflows.
Working areas:
- admin UI served directly from the device
- UART serial chat with slash commands
- simulator with the same app/chat/runtime model
- Lua apps, components, tasks, events, and behaviors
- OTA updates on supported boards
- chunked blob upload and install-from-file/blob/url flows
- web search and fetch tools
- Telegram bot control and photo replies on camera boards
See the full support matrix for target-by-target status.
Primary targets:
esp32s3- AI Thinker
esp32cam
Design bias:
- PSRAM-capable boards first
- SD-backed workspace where available
- OTA-safe partitioning on supported builds
Board configuration is explicit and board-aware. Apps can use named hardware aliases through espclaw.board.* instead of hard-coding raw pins.
See:
ESPClaw exposes one shared operator model across:
- admin web UI
- UART console
- simulator console
- Telegram
Slash commands are available on UART and the shared console path:
/help/status/tools/tool <name> [json]/wifi status/wifi scan/wifi join <ssid> [password]/telegram status/telegram token <token>/telegram poll <seconds>/telegram enable/telegram disable/telegram clear-token/yolo status/yolo on/yolo off/reboot/factory-reset
Everything else becomes a normal iterative LLM turn.
Build and run the host simulator:
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure
./build/espclaw_simulator --workspace /tmp/espclaw-dev --port 8080 --profile esp32s3Then open:
http://127.0.0.1:8080/
This is the fastest way to explore:
- the admin UI
- the shared chat path
- app install/run flows
- components
- chunked blob uploads
Bootstrap a repo-local ESP-IDF:
mkdir -p .deps
git clone -b v5.5.2 --recursive https://github.com/espressif/esp-idf.git .deps/esp-idf-v5.5.2
IDF_TOOLS_PATH=$PWD/.deps/.espressif ./.deps/esp-idf-v5.5.2/install.sh esp32s3,esp32
source scripts/use-idf.shBuild for esp32s3:
cd firmware
idf.py -B build-esp32s3 set-target esp32s3
idf.py -B build-esp32s3 buildBuild for esp32cam / classic esp32:
cd firmware
idf.py -B build-esp32 set-target esp32
idf.py -B build-esp32 buildFor onboarding and runtime setup, see docs/onboarding.md.
ESPClaw has a first-class reusable component model.
A component can be:
- installed from inline source
- installed from a workspace file
- installed from a committed blob
- installed from a URL
- installed from a manifest
That means the intended open-source reuse shape is:
component -> shared driver/helper/module
app -> end-user feature
task -> live schedule
behavior -> persisted schedule
event -> decoupled signal
A concrete example:
ms5611_drivercomponentweather_stationapp usingrequire("sensors.ms5611")paragliding_varioapp using the same component
See:
ESPClaw has chunk-aware flows for content that does not fit comfortably in one request buffer.
Supported patterns:
- chunked blob upload into the workspace
- app install from file/blob/url
- component install from file/blob/url/manifest
- bounded context selection and summarization for large markdown files
Key tools and APIs:
context.chunkscontext.loadcontext.searchcontext.selectcontext.summarize/api/blobs/*
ESPClaw is a networked embedded system, not a toy shell script. If you run it on hardware, treat it like software that can:
- connect to cloud providers
- store auth credentials
- talk to Telegram
- fetch remote URLs
- modify local device state
Start here:
Start here:
Runtime model:
Operator surfaces:
Hardware and validation:
Project docs:
Host test loop:
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failureReal-device bench:
python3 scripts/real_device_bench.py --device http://<device-ip>:8080Releases:
- GitHub Actions builds host artifacts and firmware artifacts
- tagged
v*pushes publish GitHub Releases - Cloudflare Pages serves the public site at
espclaw.dev - the
pages.ymlworkflow builds and validates the static site + WASM runtime bundle used for deployment
ESPClaw is MIT-licensed and intended to be hackable.
What that means in practice:
- Lua apps and components are expected to be edited and shared
- simulator and firmware flows should stay aligned
- CI should prove both host and firmware builds
- the repo should be operable from a fresh checkout
If you build something cool with it, the most useful contribution is usually one of:
- a reusable component
- a board definition
- a polished example app
- a reproducible bug report with board/runtime context
Please read:
Pull requests that improve clarity, reproducibility, hardware support, and operator ergonomics are especially valuable.