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

cap

Capability calls — invoke any registered C-layer Capability from within a script via cap.call("id", json)

file

Filesystem — read and write files under vfs:/ and rolfs:/, text and binary modes

sys

System utilities — monotonic millisecond counter (sys.millis()), uptime in seconds (sys.uptime()), blocking sleep (sys.sleep_ms(n))

cjson

JSON parsing and serialization — decode LLM-supplied JSON arguments, encode return values

timer

Timers — create one-shot or repeating software timers with Lua callbacks

udp

UDP networking — send and receive UDP datagrams for LAN device communication

event

Event bus — publish and subscribe to internal system events; use with gpio.on() to replace busy-polling

Hardware Driver Modules

Module

Description

gpio

Pin control — configure input/output, read/write levels; gpio.on(pin, edge, fn) for interrupt-driven callbacks

button

Button events — event-driven key detection with press/release callbacks (wrapper over gpio)

i2c

I2C bus — communicate as master with sensors, displays, and other I2C devices

spi

SPI bus — master-mode byte-level read/write

rtc

Real-time clock — read and set the hardware RTC

display

Display output — unified API for SPI and LCDC-connected screens

audio

Audio — record audio, obtain PCM streams, audio playback

usb_msc

USB storage — access files on a USB Mass Storage Class device (USB drive)

usb_uvc

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 management

  • uart — UART serial send/receive

  • pwm — PWM waveform output

  • ir — Infrared transmit/receive

  • lcdc — LCD controller display output

  • adc — Analog-to-digital conversion

  • thermal — On-chip temperature sensor

  • touch — Capacitive touch detection

  • basictimer — 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

vfs:/tmp/<name>.lua

Throwaway — cleared on every reboot; use for quick tests

vfs:/scripts/<name>.lua

Persistent user scripts — survive reboots, not managed by the skill catalog

vfs:/skills/<name>/scripts/main.lua

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

a & b

bit.band(a, b)

Bitwise OR

a | b

bit.bor(a, b)

Bitwise XOR

a ~ b

bit.bxor(a, b)

Right shift

a >> n

bit.rshift(a, n)

Left shift

a << n

bit.lshift(a, n)

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

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 the lua_run Capability, 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

board_hardware_info

Knowledge

Say “query board info” in chat

Unlocks the board Capability group; gives the LLM the board’s pin and peripheral list

Must activate before writing any hardware script to avoid incorrect pin numbers

builtin_lua_modules

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

usb_file

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

camera_capture

Script

Say “take a photo” or “capture an image”

Captures a photo via USB UVC camera; unlocks the audio_stream Capability group

When a USB UVC camera is attached; activate board_hardware_info first to confirm UVC support

skill_authoring

Knowledge

Activated automatically by the Agent

Gives the LLM a workflow guide for creating new Skills via the skill_save Capability

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.