Scripting Extension
This chapter covers two ways to add functionality to the device using Lua scripts, without recompiling the firmware. “Lua Module Reference” lists the Lua modules and driver bindings available at runtime; “Skill System” explains how to install a Lua script as a skill that the Agent can invoke at runtime. Compared to C capabilities compiled into the firmware, scripting extensions are faster to iterate on and more flexible.
Lua Module Reference
What Are Lua Modules
Lua modules are C-implemented Lua extension libraries loaded inside scripts via require("module_name"). They are distinct from Skills (filesystem packages) and Capabilities (C-layer tools callable by the LLM). Modules are the toolbox available inside a running Lua script.
Two execution environments exist:
Skill sandbox (
lua_run/lua_run_async): a restricted set of modules is available. Dangerous standard libraries (io,os,debug) and dynamic loading (load,loadfile) are stripped.REPL (
AT+CLAW=lua): the full module set is available for interactive testing.
Modules Available in Skill Scripts
Software Modules
Module |
Description |
|---|---|
|
Capability calls — invoke any registered C-layer Capability from within a script via |
|
Filesystem — read and write files under |
|
System utilities — monotonic millisecond counter ( |
|
JSON parsing and serialization — decode LLM-supplied JSON arguments, encode return values |
|
Timers — create one-shot or repeating software timers with Lua callbacks |
|
UDP networking — send and receive UDP datagrams for LAN device communication |
|
Event bus — publish and subscribe to internal system events; use with |
Hardware Driver Modules
Module |
Description |
|---|---|
|
Pin control — configure input/output, read/write levels; |
|
Button events — event-driven key detection with press/release callbacks (wrapper over |
|
I2C bus — communicate as master with sensors, displays, and other I2C devices |
|
SPI bus — master-mode byte-level read/write |
|
Real-time clock — read and set the hardware RTC |
|
Display output — unified API for SPI and LCDC-connected screens |
|
Audio — record audio, obtain PCM streams, audio playback |
|
USB storage — access files on a USB Mass Storage Class device (USB drive) |
|
USB camera — control a USB Video Class camera and capture image frames |
REPL-Only Modules
Warning
The modules below are available only in the interactive REPL environment (AT+CLAW=lua). Calling them from a Skill script causes a runtime error because they are not loaded into the Skill sandbox. If you need related functionality inside a Skill, call the corresponding Capability via cap.call(), or validate logic in the REPL first.
REPL-exclusive modules (do not use in Skill scripts):
wifi— Wi-Fi scanning and connection managementuart— UART serial send/receivepwm— PWM waveform outputir— Infrared transmit/receivelcdc— LCD controller display outputadc— Analog-to-digital conversionthermal— On-chip temperature sensortouch— Capacitive touch detectionbasictimer— Hardware basic timer (precision timing)led_strip— WS2812 programmable LED strip control
Writing Skill Scripts
Entry Function
Every Skill script must define a run function. The Lua VM calls it after loading the script, passing the decoded argument table:
-- Top-level code runs first (helpers, constants)
function run(args)
-- args is already a decoded Lua table — do NOT call cjson.decode(args)
local pin = args.pin or "PA_22"
return '{"status":"ok","pin":"' .. pin .. '"}'
end
Scripts without a run function cannot be invoked by the Agent or via AT+CLAW=skill.
File Path Conventions
Path |
Purpose |
|---|---|
|
Throwaway — cleared on every reboot; use for quick tests |
|
Persistent user scripts — survive reboots, not managed by the skill catalog |
|
Agent-invokable Skills — must be at this exact path to be registered |
Lua 5.4 Bitwise Operators
Ameba-Claw runs Lua 5.4. There is no bit module. Use the native operators:
Operation |
Correct |
Incorrect (Lua 5.1 style) |
|---|---|---|
Bitwise AND |
|
|
Bitwise OR |
|
|
Bitwise XOR |
|
|
Right shift |
|
|
Left shift |
|
|
System Time and Sleep
The os module is not available. Use sys instead. Note that sys provides elapsed time since boot, not a Unix wall-clock timestamp:
local sys = require("sys")
local ms = sys.millis() -- monotonic ms since boot (wraps after ~49 days; NOT Unix time)
local up = sys.uptime() -- seconds since boot as a float (NOT Unix time)
sys.sleep_ms(500) -- block for 500 ms (safe in run() body; never call inside a timer callback)
To get the current wall-clock time, use cap.call("get_current_time", "{}") instead.
Correct Usage of cap.call
cap.call always returns two values. Capture both or the JSON payload is silently discarded:
local cap = require("cap")
-- Correct: capture ok (boolean) and result_json (string)
local ok, result_json = cap.call("file_read", '{"path":"vfs:/tmp/data.json"}')
if not ok then
return '{"error":' .. (result_json or '"unknown"') .. '}'
end
local t = require("cjson").decode(result_json)
-- Incorrect: captures only ok (the boolean), discards result_json
-- local result = cap.call("file_read", ...) -- do not do this
Complete Example: GPIO Blink LED
A complete Skill script that blinks an LED on a configurable pin:
local gpio = require("gpio")
local sys = require("sys")
function run(args)
local pin = args.pin or "PA_22"
local times = args.times or 6
gpio.open(pin, gpio.OUTPUT)
for i = 1, times do
gpio.write(pin, i % 2)
sys.sleep_ms(500)
end
gpio.close(pin)
return '{"status":"ok"}'
end
Save to vfs:/skills/blink_led/scripts/main.lua. The Agent can invoke it through conversation, or directly:
AT+CLAW=skill,blink_led
AT+CLAW=skill,blink_led,{"pin":"PA_22","times":10}
Skill System
What Is a Skill
A Skill is a self-contained extension package stored in the filesystem. Each Skill occupies a directory and must contain a SKILL.md metadata file; a scripts/main.lua execution script is optional. Skills come in two types:
Script-type Skill: contains
scripts/main.lua. The Agent invokes it at reasoning time via thelua_runCapability, which executes the script and returns its output to the LLM for further reasoning. Examples:usb_file,camera_capture.Knowledge-type Skill: only
SKILL.md, no execution script. Activating it injects the Skill’s documentation as context for the LLM and unlocks the corresponding Capability group (cap_groups). Examples:board_hardware_info,builtin_lua_modules,skill_authoring.
Built-in Skills reside in the read-only firmware partition at rolfs:/skills/<name>/ and are updated atomically with OTA. User-created Skills reside in the writable partition at vfs:/skills/<name>/ and persist across reboots.
Built-in Skills
Ameba-Claw ships with five built-in Skills covering the most common use cases.
Skill Name |
Type |
How to Activate |
Purpose |
Typical Use Case |
|---|---|---|---|---|
|
Knowledge |
Say “query board info” in chat |
Unlocks the |
Must activate before writing any hardware script to avoid incorrect pin numbers |
|
Knowledge |
Say “query Lua module API” in chat |
Provides a Lua module API index; LLM reads individual module docs on demand |
Activate when asking the Agent to write Lua scripts |
|
Script |
Ask to work with USB-drive files |
Read, write, list, and delete files on a FAT32 USB drive |
When a USB drive is inserted and you need file access |
|
Script |
Say “take a photo” or “capture an image” |
Captures a photo via USB UVC camera; unlocks the |
When a USB UVC camera is attached; activate |
|
Knowledge |
Activated automatically by the Agent |
Gives the LLM a workflow guide for creating new Skills via the |
Auto-triggered when the Agent creates a new Skill — no manual activation needed |
Note
Knowledge-type Skills (board_hardware_info, builtin_lua_modules, skill_authoring) have no execution script. Activating them injects documentation context or unlocks Capability groups — they do not run any code. AT+CLAW=skill,<name> is only meaningful for script-type Skills.
Always activate board_hardware_info before writing any script that touches hardware pins. Available pins differ between boards.
Script-type Skills can be run directly with an AT command:
AT+CLAW=skill,usb_file
AT+CLAW=skill,camera_capture
Ask the Agent to Write a Skill
You do not need Lua experience. Describe what you want in natural language; the Agent handles the rest.
Step 1 — Describe your requirement
Send a message in chat:
Add a breathing-LED effect to GPIO PA_22
Step 2 — Agent queries hardware information
The Agent activates the board_hardware_info Skill (knowledge-type) to confirm PA_22 is available on the current board and to get the correct pin-name format.
Step 3 — Agent writes the Lua script
The Agent activates builtin_lua_modules to look up the gpio module API, then generates a Lua script implementing the breathing-LED effect.
Step 4 — Agent saves the script as a new Skill
The Agent activates skill_authoring (knowledge-type, provides the save workflow guide) and then calls the skill_save Capability to write the script to vfs:/skills/breathing_led/scripts/main.lua and register it.
Step 5 — Agent confirms completion
Skill "breathing_led" has been created. PA_22 will pulse with a 1-second
breathing cycle. You can say "start breathing LED" at any time to run it.
After that, trigger it through conversation or directly:
AT+CLAW=skill,breathing_led
Tip
Just describe the effect you want. For example: “Read the temperature sensor every 30 seconds and send me the value” or “Play a chime when a GPIO signal arrives.” The Agent queries hardware info, looks up APIs, writes the script, and saves it.
Managing Installed Skills
View and Edit via the Web Console
Log in to the web management interface, open File Manager, and browse vfs:/skills/. Each Skill is a subdirectory; the main script is at scripts/main.lua. Edit main.lua directly — changes take effect immediately without restarting.
List All Skills
AT+CLAW=cap,skill_list
Uninstall a Skill
Delete the Skill’s directory in the file manager, or use:
AT+CLAW=fs,delete,vfs:/skills/blink_led
Run a Skill Directly
AT+CLAW=skill,<skill_name>
AT+CLAW=skill,<skill_name>,{"key":"value"}
Browse the Filesystem
AT+CLAW=fs,list,vfs:/skills/
SKILL.md Format
Every Skill directory must contain a SKILL.md with a YAML frontmatter block:
---
name: my_skill
description: "What this skill does — shown to the LLM"
compatibility: RTL8721F
metadata:
cap_groups: board lua # space-separated; groups unlocked on activation
manage_mode: editable # editable (user) or readonly (built-in)
---
# Skill documentation body
Full documentation for the LLM goes here...
The name field must match the directory name and use only [a-z0-9_-]. Built-in Skills use manage_mode: readonly. User-created Skills default to manage_mode: editable.