Skip to content

标准库在不同机型上的差异 ​

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 官方函数,所以:

  1. 官方文档里查不到它们,属正常,不是您看漏了;
  2. 软 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: ... = 可用。
  • 比查任何表格都快,也不会被文档更新时间骗到。

验证方法:

  1. 在 ESP32 机型上跑,os.execute/xpcall/string.fromhex 大概率打印 nil。
  2. 在软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。

验证方法:

  1. 在电脑/模拟器上跑,打印 PATH = ...。
  2. 在嵌入式上跑,打印「本机型不支持 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。

验证方法:

  1. 在模拟器上跑,encoder.tohex 生效,正常输出。
  2. 把 encoder 分支注释掉,在机型上 string.tohex 生效;在模拟器上走到 error 分支。

注意事项:

  • string.fromhex 是「转成 hex 文本」、string.tohex 是「从 hex 文本转回」,名字和方向相反,第一次用先拿已知值验一遍。
  • encoder 在极少数老固件上也可能没注册,所以最稳的是先 if encoder and encoder.tohex then 再决定。

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研