cap_lua and Lua overview
Source: cap_lua.ccomponents/claw_capabilities/cap_lua/src/cap_lua.c · header: cap_lua.hcomponents/claw_capabilities/cap_lua/include/cap_lua.h
Where Lua fits
Section titled “Where Lua fits”ESP-Claw uses Lua as the primary on-device language for programmable automation. It plays three roles:
1. LLM-programmable execution layer
Section titled “1. LLM-programmable execution layer”The LLM can author scripts with file tools (write_file) and run them (lua_run_script). Lua bridges natural-language plans to hardware: GPIO, display, audio, etc.
2. Vehicle for automation actions
Section titled “2. Vehicle for automation actions”claw_event_router supports a run_script action so rules can fire Lua without going through an LLM:
That yields fully local, low-latency responses without network or model cost.
3. Rapid prototyping / extension surface
Section titled “3. Rapid prototyping / extension surface”Users who do not ship C firmware can still extend behavior via Lua. Native modules registered as lua_module_* / lua_driver_* expose peripherals through small Lua APIs.
Lua runtime sketch
Section titled “Lua runtime sketch”ESP-Claw runs Lua via cap_lua_runtime_* helpers. The runtime initializes in cap_lua_group_init, locks module registration, then starts.
Script path management
Section titled “Script path management”Writable cap_lua runtime scripts must live under the Script Base Dir. In edge_agent, the default Script Base Dir is /fatfs/scripts/ (apps can override it via cap_lua_set_base_dir). Read-only scripts bundled with Skills may run from /fatfs/skills/<skill_id>/scripts/....
pathmust target a.luafile.pathsupports two forms:- Managed relative path: e.g.
hello.luaorbuiltin/test/hello.lua(auto-expanded under${base_dir}and must not contain..). - Skill-local absolute path: e.g.
{CUR_SKILL_DIR}/scripts/hello.luawhen an active skill documents that script. It resolves under/fatfs/skills/<skill_id>/scripts/..., must stay under the Skill root, and must not contain...
- Managed relative path: e.g.
- Discover and read scripts with file tools:
list_dir {"keyword":"scripts/"}andread_file {"path":"scripts/hello.lua"}.
Module registration lock
Section titled “Module registration lock”cap_lua_register_module is only valid before cap_lua_group_init. After init, s_module_registration_locked = true and further registration returns ESP_ERR_INVALID_STATE, freezing the module set at boot.
cap_lua tool surface
Section titled “cap_lua tool surface”cap_lua currently registers six Callables:
| Tool ID | Purpose |
|---|---|
lua_run_script | Sync run; block until output |
lua_run_script_async | Async enqueue; returns job_id immediately |
lua_list_async_jobs | List async jobs (status filter) |
lua_get_async_job | Fetch status/output for one job (supports lookup by job_id or name) |
lua_stop_async_job | Stop one async job (by job_id or name) |
lua_stop_all_async_jobs | Stop async jobs in bulk (optionally filter by exclusive group) |
Sync vs async
Section titled “Sync vs async”| Mode | When to use | Timeout | Returns |
|---|---|---|---|
lua_run_script | Quick work / state reads | optional timeout_ms | script output string |
lua_run_script_async | Long work (animation, waiting on sensors) | timeout_ms=0 means run until stopped (default) | job_id |
lua_run_script_async supports name, exclusive, and replace controls:
name: assign a logical name for laterlua_get_async_job/lua_stop_async_joboperations.exclusive: mutual-exclusion group (for example,"display"), commonly used for single-slot resources.replace: true: when an active job with the samenameorexclusivegroup exists, attempt to preempt and replace it.
Typical flow:
Passing arguments
Section titled “Passing arguments”Optional JSON args becomes the global args inside the script:
When lua_run_script / lua_run_script_async is triggered by the Agent inside an IM conversation, if the tool call does not explicitly provide args.channel, args.chat_id, or args.session_id, the runtime auto-merges the current session context into args.
This lets scripts read conversation context directly (for example, for replies) without passing these fields every time.
Authoring scripts with file tools
Section titled “Authoring scripts with file tools”Use write_file to create or overwrite Lua scripts under the file capability base dir. Then run the same script through lua_run_script with the leading scripts/ removed, for example {"path":"hello.lua"}.
Runtime limits and stability
Section titled “Runtime limits and stability”- Lua timeout detection combines an instruction-hook check (triggered every fixed instruction count) with wall-clock timeout; when timed out, it throws
execution timed out. - The hook callback proactively calls
taskYIELD()to avoid tight loops monopolizing CPU and accidentally triggering the task watchdog. - Integer-like values in JSON
argsare preserved as Lua integer type where possible, reducing type ambiguity for GPIO values, pixel coordinates, and similar parameters.
C-callable helpers
Section titled “C-callable helpers”Besides tools, cap_lua exposes C helpers for firmware code:
Async job state machine
Section titled “Async job state machine”Jobs move through:
lua_list_async_jobs filters with "all" / "queued" / "running" / "done" / "failed" / "timeout" / "stopped".
Coordination With Skills
Section titled “Coordination With Skills”The cap_lua Skill instructs the model to inspect running Lua async jobs with lua_list_async_jobs / lua_get_async_job before stopping, replacing, or deactivating long-lived work.