Lua 扩展模块(lua_module_*)
lua_module_* 是 ESP-Claw 中将硬件外设能力暴露给 Lua 脚本的扩展机制。每个模块本质上是一个标准 Lua C 模块(lua_CFunction 格式),通过 cap_lua_register_module 注册后,在 Lua 脚本中可以用 require("module_name") 加载。
内置模块总览
Section titled “内置模块总览”| 模块名 | 组件目录 | 说明 |
|---|---|---|
display | lua_module_display | LCD 屏幕绘图(文字、图形、JPEG/PNG) |
gpio | lua_module_gpio | GPIO 读写、方向配置 |
button | lua_module_button | 按键事件注册与回调 |
led_strip | lua_module_led_strip | WS2812 等可寻址 LED 灯带控制 |
audio | lua_module_audio | 音频播放/录制(PCM/WAV)与频谱分析 |
camera | lua_module_camera | 摄像头拍照与流式采集 |
lcd_touch | lua_module_lcd_touch | 触摸屏坐标读取 |
delay | lua_module_delay | delay.delay_ms(n) 毫秒级延时 |
storage | lua_module_storage | 文件系统操作 |
esp_heap | lua_module_esp_heap | 堆内存查询 |
system | lua_module_system | 时间、运行时长、IP、内存与 Wi-Fi 状态查询 |
mcpwm | lua_module_mcpwm | 通用 PWM 输出(频率/占空比控制) |
event_publisher | lua_module_event_publisher | 从 Lua 脚本向 Event Router 发布事件 |
board_manager | lua_module_board_manager | 板级初始化与外设句柄获取 |
每个 lua_module_* 组件对外只暴露一个注册函数,命名规则为 lua_module_<name>_register():
注册函数内部调用 cap_lua_register_module,将模块名与 luaopen_* 函数关联:
注意:所有模块必须在 cap_lua_register_group() 之前注册,之后注册会被拒绝(运行时锁定)。
应用层注册示例
Section titled “应用层注册示例”如何实现一个自定义 Lua 模块
Section titled “如何实现一个自定义 Lua 模块”以下展示实现一个简单的 myled 模块(控制单个 LED)的完整流程:
1. 创建组件目录
Section titled “1. 创建组件目录”2. 实现 Lua C 函数
Section titled “2. 实现 Lua C 函数”每个 Lua C 函数遵循统一签名 int func(lua_State *L):
- 通过
luaL_check*系列函数从 Lua 栈获取参数 - 执行实际操作(调用 ESP-IDF 驱动等)
- 将返回值 push 到栈,return 返回值数量
3. 头文件
Section titled “3. 头文件”4. 在 Lua 脚本中使用
Section titled “4. 在 Lua 脚本中使用”5. 为模块提供 Skills 文档
Section titled “5. 为模块提供 Skills 文档”lua_module_* 与 cap_* 一样,强烈建议为每个模块提供配套的 Skill 文档,告诉 LLM 如何在 Lua 脚本中正确使用该模块。
skills_list.json 的特殊之处
Section titled “skills_list.json 的特殊之处”与 cap_* 的 Skill 不同,lua_module_* 的 Skill 绑定的 cap_groups 不是自己的 Group(lua_module 没有独立的 Capability Group),而是绑定到 cap_lua:
激活该 Skill 时,cap_lua Group 的工具(lua_run_script、lua_write_script 等)会对当前 session 可见,同时 Skill 文档注入上下文,LLM 即可编写并执行使用 myled 模块的脚本。
关于 Lua 模块 Skill 文档的撰写规范(API 参考格式、要素说明等),请参阅 Skills 指南。
典型外设模块深度分析:lua_module_display
Section titled “典型外设模块深度分析:lua_module_display”lua_module_display 是最复杂的 Lua 模块,提供完整的 LCD 绘图能力,是理解复杂模块设计的最佳参考。
架构:HAL 抽象层
Section titled “架构:HAL 抽象层”lua_module_display 不直接操作 LCD 驱动,而是通过 display_hal.h 定义的 HAL(硬件抽象层)接口:
Lua 模块层只负责参数解析和 HAL 调用,板级代码实现 HAL 接口,这样同一套 Lua API 可以适配不同型号的 LCD 控制器。
参数解析模式
Section titled “参数解析模式”模块定义了辅助函数统一处理参数校验:
可选 table 参数模式
Section titled “可选 table 参数模式”文字绘制函数支持可选的 options table,通过逐字段读取 Lua table 实现:
Lua 脚本中的用法:
JPEG/PNG 解码
Section titled “JPEG/PNG 解码”lua_module_display 内置了以下图像处理能力:
| 函数 | 格式 | 说明 |
|---|---|---|
draw_jpeg_file | JPEG | 从文件路径解码并显示,委托给 HAL |
draw_png_file | PNG | 用 libpng 解码 RGBA → RGB565,再调用 HAL |
draw_jpeg_file_scaled | JPEG | 硬件缩放(需 scale_w/h 为 8 的倍数) |
draw_jpeg_file_fit | JPEG | 按比例缩放适配目标区域 |
draw_jpeg_file_crop | JPEG | 从 JPEG 中裁剪指定区域显示 |
PNG 解码在 C 层完成(避免 Lua 层内存管理复杂度),RGBA 转 RGB565 时以黑色为背景做 alpha 预乘:
内存 RGB565 绘图
Section titled “内存 RGB565 绘图”除了文件路径的图像绘制外,lua_module_display 还支持直接从内存缓冲区绘制 RGB565 数据,适用于摄像头预览等实时场景:
| 函数 | 说明 |
|---|---|
draw_rgb565_crop | 从 RGB565 缓冲区裁剪指定区域显示 |
draw_rgb565_scaled | 将 RGB565 缓冲区缩放到指定尺寸显示 |
draw_rgb565_fit | 将 RGB565 缓冲区按比例适配目标区域显示 |
data 参数可以是 Lua string 或 lightuserdata(如 camera.frame:ptr()),字节数需为 src_width * src_height * 2。典型用法见摄像头预览示例 camera_preview_demo.lua。
支持双缓冲模式(begin_frame / present / end_frame),避免绘制过程中的屏幕撕裂:
图像文件路径同样有安全校验,只允许以 / 开头的绝对路径,且必须以 .jpg、.jpeg、.png 结尾,禁止 .. 路径跳转。
附注:模块特殊逻辑
Section titled “附注:模块特殊逻辑”display 显示模块
Section titled “display 显示模块”显示模块有特殊的所有权管理逻辑(「仲裁」机制),确保 Lua 脚本可以独占使用显示资源,避免与其他任务冲突。
lua_module_display 模块在 display.init(...) 成功后,Lua 会自动获取前台显示所有权;display.deinit() 或脚本结束清理时释放所有权,避免与其他显示任务冲突。
另外,在显示 HAL 重新创建场景中,运行时会清理历史残留的 swap buffer / display callback 状态,减少跨脚本切换时的显示资源泄漏风险(特别是配合 lua_run_script_async 的 exclusive:"display" + replace:true 切换时)。
event_publisher 向 Event Router 发布事件
Section titled “event_publisher 向 Event Router 发布事件”publish_message 支持两种形式:
- 字符串形式:更加简洁,但可携带的信息较少
- 消息对象形式:可以携带更多信息,但需要手动构造消息对象