主题
标准库在不同机型上的差异
Lua 自带的那批库(base / string / os 等)在设备上和软 PLC 模拟器上不是同一套。 固件按机型裁剪了标准库以省 Flash,所以同一份脚本可能在模拟器里跑得好、下载到设备上就报错 —— 而且报的是 attempt to call a nil value,看不出根因是"这个函数在本机型上根本没编进去"。
本页只列有差异的函数。没列出来的,两边行为一致,按 Lua 5.4 官方文档写就行。
用之前先判一下,别让整个脚本挂掉
lua
if os.getenv then
print(os.getenv("PATH"))
end把这类调用包在 if 函数名 then 里,脚本在任何机型上都能继续跑完其余逻辑。
ESP32 机型上没有、模拟器上有的
这 9 个函数在设备固件里是有意裁掉的(省 Flash 空间),不是"暂时没实现",也不是版本旧 —— 升级到任何新固件都不会把它们补回来,写设备脚本请当它们不存在。
| 函数 | 在设备上会怎样 | 怎么办 |
|---|---|---|
os.execute(cmd) | 取到 nil,调用即报错 | 设备上没有 shell,不要指望调用任何外部程序。需要跑批处理就在 PC 侧做 |
os.exit(code) | 取到 nil,调用即报错 | 脚本没有"提前退出进程"这回事。要停逻辑就用标志位跳出主循环;要重启设备用 system.restart |
os.getenv(name) | 取到 nil,调用即报错 | 设备上没有环境变量。参数走 ini 或 plc.kv_set / plc.kv_get |
os.setlocale(...) | 取到 nil,调用即报错 | 不需要,也没有。设备上的时间与地区行为由固件决定 |
os.tmpname() | 取到 nil,调用即报错 | 自己拼一个固定路径,用完 os.remove 删掉 |
dofile(path) | 取到 nil,调用即报错 | 没有等价替代。不要设计成"从文件里读一段 Lua 代码来执行" |
loadfile(path) | 取到 nil,调用即报错 | 同上 |
load(str) | 取到 nil,调用即报错 | 同上。设备侧不支持动态求值字符串代码 |
xpcall(f, handler, ...) | 取到 nil,调用即报错 | 改用 pcall,它在机型上都在:local ok, err = pcall(f, ...),需要错误上下文就在调用点自己带上。详见 base 基础库 |
xpcall 是这批里最容易踩的一个
写"健壮的 Lua"时,训练分布会强烈倾向让您用 xpcall 配一个错误处理函数。 在软 PLC 模拟器上它工作正常,下到 ESP32 机型上第一次触发异常路径才会炸 —— 而异常路径往往就是您要保护的那条。给设备写代码请统一用 pcall。
模拟器上没有、ESP32 机型上有的
| 函数 | 实际做什么 |
|---|---|
string.fromhex(s) | 传入字节串,返回它的十六进制文本 |
string.tohex(s) | 传入十六进制文本,返回对应的字节串 |
这两个函数的名字和它们做的事是反的
fromhex 是"转成 hex 文本",tohex 是"从 hex 文本转回来" —— 与按英文字面理解的方向正好相反。 其中 hex 文本不区分大小写(内部会先转大写再还原)。 别凭名字推断方向,第一次用先拿一个已知值验一遍。
这两个是固件侧额外挂进 string 库的,不是 Lua 官方函数,所以:
- 官方文档里查不到它们,属正常,不是您看漏了;
- 软 PLC 模拟器与上位机侧没有它们,脚本在模拟器里自检会失败。
跨机型搬运建议改用 encoder 库:它在机型与模拟器上都注册了 tohex / fromhex / to16hex / from16hex / tobase64 / frombase64 六个函数。
tohex 与 fromhex 的具体入参格式(十六进制文本大小写、16hex 与 hex 的区别)本页不下断言 —— 各参数的含义与可直接照抄的例子见 encoder 编码转换, 拿不准就在目标机型上先试一次:
lua
print(encoder.tohex, encoder.fromhex) -- 两个都打印出 function: 才说明这台能用还有一处坑:task / event 不是 Lua 标准库
task.start / task.stop / task.list 是统一并发库(后台任务)的函数,与它配套的是 event(event.on/wait/off/emit/gather/done)。两者都不是 Lua 标准库的东西, 按 utf8.task(...) 去写是错的。utf8 库本身只有官方的那几个: utf8.len / utf8.codepoint / utf8.char / utf8.offset / utf8.codes / utf8.charpattern。
怎么确认您手上这台机型到底有没有
机型之间也有差别(例如 bacnet / opcua / water 只在 ESP32-S3 平台上编译)。 最可靠的办法是在设备上直接打印:
lua
print(os.execute, xpcall, string.fromhex, encoder.tohex)打印出 function: ... 的就是这台能用,打印 nil 的就是没有。 把这一行贴进临时脚本跑一次,比查任何表格都快、也不会被文档的更新时间骗到。
相关页面
例程
本页是「机型差异参考」,没有专属 API;但「怎么写一份在每台机器上都能跑完、不崩」的脚本,本身就是最重要的能力。下面 3 个例程演示运行时探测可用 API 的写法,都是可直接下载运行的完整脚本,且不使用任何阻塞主循环的写法。
例程 1:打印一组 API 看本台机器有没有
适用:全部机型 / 软PLC模拟器 / 上位机 | 依赖模块:(无,纯探测)| 难度:★☆☆
场景:同一份脚本要在 ESP32 机型、软PLC模拟器、上位机间搬。先跑一句,确认目标函数在本台存在再写逻辑。
完整代码(可直接下载运行):
lua
print(os.execute, xpcall, string.fromhex, encoder.tohex)
-- 打印出 function: ... 的就是这台能用;打印 nil 的就是没有要点:
- 函数名本身就是「值」,可以直接
print,不用加括号(加了括号是调用)。 - 打印
nil= 本台未注册该函数;function: ...= 可用。 - 比查任何表格都快,也不会被文档更新时间骗到。
验证方法:
- 在 ESP32 机型上跑,
os.execute/xpcall/string.fromhex大概率打印nil。 - 在软PLC模拟器上跑,三者都应打印
function: ...(除string.fromhex是机型专属,模拟器没有)。
注意事项:
- 这个函数不存在时,调用它才会炸(
attempt to call a nil value),只print不会炸。 - 模拟器和机型「都有」的同一函数,行为也可能因版本不同有细微差异,关键路径以实测为准。
例程 2:用 if 函数名 then 兜底可选调用
适用:同上 | 依赖模块:(无)| 难度:★☆☆
场景:脚本要用 os.getenv 取环境变量,但嵌入式上没有。直接调用会整段崩,用 if 包裹后任何机型都能跑完其余逻辑。
完整代码(可直接下载运行):
lua
if os.getenv then
print("PATH =", os.getenv("PATH"))
else
print("本机型不支持 os.getenv,改用 ini / plc.kv_get 取参数")
end要点:
if os.getenv then判断的是「这个变量是不是函数」,不存在时为nil,条件为假。- 把机型相关调用都这样包一层,脚本就能「优雅降级」而非崩溃。
- 同理适用于
os.execute、xpcall、dofile、load、loadfile。
验证方法:
- 在电脑/模拟器上跑,打印
PATH = ...。 - 在嵌入式上跑,打印「本机型不支持 os.getenv...」,脚本继续往下走。
注意事项:
- 别用
if os.getenv ~= nil then之外的写法(如if os.getenv() then是调用,会先炸)。 - 文档说「某函数机型没有」,不代表「永远没有」;以运行时
print(func)检测为准,别凭记忆硬编码。
例程 3:cross-机型安全的 hex 编解码
适用:同上 | 依赖模块:encoder string | 难度:★★☆
场景:要算字节串的十六进制文本。机型上有 string.fromhex,但模拟器/上位机没有;encoder.tohex 两边都有,优先用。
完整代码(可直接下载运行):
lua
local function to_hex(s)
if encoder and encoder.tohex then
return encoder.tohex(s) -- 机型 + 模拟器都支持,优先
elseif string.tohex then
return string.tohex(s) -- 仅机型
else
error("本台没有可用的 hex 编码函数")
end
end
print(to_hex("AB")) -- 输出字节串 0xAB 的十六进制文本要点:
encoder.tohex / fromhex在标准库差异页被推荐为跨机型统一方案(机型与模拟器都注册了)。string.fromhex/string.tohex方向与名字相反,且仅机型有,模拟器会打印nil。- 先探测
encoder,再回退string,最后显式报错,调用方不会拿到神秘nil。
验证方法:
- 在模拟器上跑,
encoder.tohex生效,正常输出。 - 把
encoder分支注释掉,在机型上string.tohex生效;在模拟器上走到error分支。
注意事项:
string.fromhex是「转成 hex 文本」、string.tohex是「从 hex 文本转回」,名字和方向相反,第一次用先拿已知值验一遍。encoder在极少数老固件上也可能没注册,所以最稳的是先if encoder and encoder.tohex then再决定。