跳至正文
首页 » 文章 » Lua 脚本支持

Lua 脚本支持

Samovar 支持使用脚本语言 Lua 实现自动化。脚本可以控制外部设备(阀门、水泵、搅拌器、加热器、继电器),读取传感器数值,并根据具体的装置调整流程逻辑。控制器的基本功能不会改变。

脚本编辑器内置于网页界面中:Settings → Editor(设置 → 编辑器)。它支持 Lua、JS、CSS、HTML 和 JSON 的语法高亮,检查括号以及 function … end 代码块是否配对,并可用 Tab 键自动补全。

解释器:Lua 5.4.4。ESP32 内置 Arduino 库的基础 API 经过扩展,增加了访问传感器、加热、水泵、搅拌器和继电器的函数。

如何启用

默认情况下,固件中的 Lua 支持是关闭的。要启用它,请打开固件目录中的 Samovar_ini.h 文件,将这一行

//#define USE_LUA

替换为

#define USE_LUA

之后重新编译固件并上传到控制器。

架构

控制器启动时会执行 init.lua 文件。它的作用是初始化其他脚本要用到的变量,并开启或关闭每秒一次的循环执行(setNumVariable("loop_lua_fl", 1))。

如果循环已开启,每秒会依次执行两个脚本:

  1. 通用脚本 script.lua——在所有模式下运行。
  2. 当前模式的脚本:beer.lua(啤酒)、bk.lua(发酵醪精馏塔)、nbk.lua(连续式发酵醪精馏塔)、dist.lua(蒸馏)、rectificat.lua(精馏)、cheese.lua(奶酪制作)或 suvid.lua(低温慢煮 (sous-vide))。

如果脚本在下一次启动时还没有执行完,本次启动会被跳过:已经在运行的脚本不会被中断。

在 http://samovar.local/lua 可以一次性运行所有脚本。

除了主脚本之外,还支持额外的模式按钮——文件名形如 btn_<mode>_buttonN.lua,例如 btn_rect_button1.lua。它们会显示在网页界面中,按下时可以调用您自己的代码。

重要提示。更新固件时,网页界面会从服务器下载 init.lua、script.lua、各模式文件和 btn_*.lua 的参考版本,但仅限控制器存储器中尚不存在该文件的情况。用户脚本不会被覆盖。即便如此,也请在电脑上保存好自己的版本——恢复出厂设置或重新安装网页界面后,它们会丢失。

外部设备

脚本可以连接 I²C 端口扩展器:

  • PCF8575——16 路数字端口的扩展器(由 USE_EXPANDER 标志定义)。每个端口都可以连接任意执行器或“按钮”类传感器。所有端口都可读可写。
  • PCF8591——模拟端口扩展器(由 USE_ANALOG_EXPANDER 标志定义)。用于模拟传感器,例如奶酪制作模式中的 pH 电极。

使用扩展器的方式有很多:可以自制安全传感器,触发时由脚本关闭 Samovar;也可以根据蒸汽温度控制额外的电加热管。具体逻辑取决于您的设备。

函数

GPIO 与 Arduino 兼容函数

pinMode(pin, mode)——输入/输出模式。只有引脚 RELE_CHANNEL1、RELE_CHANNEL2、RELE_CHANNEL3、RELE_CHANNEL4 和 LUA_PIN 可用。引脚的具体数值取决于引脚图,详见 GPIO 说明。

digitalWrite(pin, value)——在引脚上输出 HIGH(1)或 LOW(0)。

digitalRead(pin)——读取引脚的当前值(HIGH 或 LOW)。

analogRead()——读取 LUA_PIN 引脚的模拟值,范围 0–4095。该引脚上接有 pH 电极或 MPX5010DP 压力传感器,因此不要向它施加超过 3.3 V 的电压:否则会烧毁 ESP32。

delay(ms)——暂停,单位为毫秒。

millis()——自控制器启动以来经过的毫秒数。

端口扩展器

exp_pinMode(pin, mode)、exp_digitalWrite(pin, value)、exp_digitalRead(pin)——与 GPIO 函数相同,但作用于 PCF8575 的端口(0–15)。

exp_analogRead()、exp_analogWrite(value)——PCF8591 的模拟操作。仅在启用 USE_ANALOG_EXPANDER 时可用。

过程控制

setPower(power)——开启(1)或关闭(0)加热。

setCurrentPower(value)——设置功率调节器上的电压。如果调节器按功率控制,则该参数设置的是功率。仅在启用 SAMOVAR_USE_POWER 时可用。

setBodyTemp(temp)——设置精馏时的酒心温度。在其他模式下,该函数只向控制台和 Blynk 输出一条消息。

setMixer(value)——开启(1)或关闭(0)搅拌器。

openValve(value)——打开(1)或关闭(0)供水阀。

setAlarm()——紧急模式:控制器关闭加热,关闭阀门并停止水泵。

setNextProgram()——转到程序的下一行(相当于界面中的按钮)。

setPauseWithdrawal(value)——暂停(1)或恢复(0)取酒。

setCapacity(num)——切换当前的取酒容器。有效范围:0…CAPACITY_NUM。

setLuaStatus(value)——设置 Lua 模式的状态。如果处于忙碌状态,会抛出错误“Lua_status busy”。

setPumpPwm(pwm)——对水泵输出进行 PWM 控制(0…1023)。在启用 USE_WATER_PUMP 时可用。

setServoAngle(angle)——设置舵机角度(0…SERVO_ANGLE)。在定义了 SERVO_PIN 时可用。

setTimer(num, sec)——将计时器 num(1…10)设置为 sec 秒。共有 10 个计时器。

getTimer(num)——计时器的剩余时间,单位为毫秒。如果计时器未设置或已经到期,则返回 0。

getState()——控制器的数字状态。

控制器变量

getNumVariable(name) 和 setNumVariable(name, value)——读取和写入数值变量。只有明确标记为可写的变量才能写入(见下表)。示例:setNumVariable("TankTemp", 85) 会把蒸馏釜温度设为 85 °C。如果不了解 Samovar 的逻辑,修改变量有风险——可能会破坏算法。

getStrVariable(name) 和 setStrVariable(name, value)——字符串变量的同类函数。

脚本中可用的变量名称见下一节的表格。

对象——持久存储

普通的 Lua 变量只在脚本运行期间存在,下次启动时就会丢失。要在多次启动之间传递数值,请使用 setObject 和 getObject:

setObject("name", value)     -- 保存
getObject("name")            -- 读取(不存在则返回 "")
getObject("name", "NUMERIC") -- 按数字读取(不存在则返回 0)

最多 32 个键(LUA_OBJECT_STORE_MAX_KEYS)。数值会保留到控制器重启为止。

日志、调试、HTTP

sendMsg(msg, level)——输出一条消息。level = -1 时输出到 COM 端口和浏览器控制台(用于调试)。level = 0, 1, 2 时输出到控制台和 Blynk。

http_request(url [, method, headers, body])——HTTP 请求。便于与外部服务集成:发送 Telegram 通知、记录日志、调用 webhook。超时时间比普通下载短,并且调用在共享互斥锁下执行,因此不同脚本同时调用也不会冲突。

I²C 设备

check_I2C_device(address)——检查 I²C 总线上指定地址处是否有设备。设备有响应时返回 1,否则返回 0。

get_i2c_rele_state(relay) 和 set_i2c_rele_state(relay, state)——I²C 继电器的状态读取和切换。有效编号:1…4。

步进电机模块

set_stepper_by_time(speed, direction, seconds)——让步进电机以给定的速度(0…65535)和方向(0 或 1)运转指定的时间。如果成功启动则返回 1。

set_stepper_target(speed, direction, target)——执行给定数量的步数。如果成功启动则返回 1。

get_stepper_status()——步进电机模块的当前状态。

set_mixer_pump_target(target) 和 get_mixer_pump_status()——搅拌泵控制(0 或 1)。

I²C 水泵

当 use_I2C_dev = 2 时可用:

i2cpump_start(rate, ml)     -- 以给定的速度和体积启动水泵
i2cpump_stop()              -- 停止
i2cpump_get_speed()         -- 当前速度
i2cpump_get_target_ml()     -- 目标体积
i2cpump_get_remaining_ml()  -- 剩余待泵送体积
i2cpump_get_running()       -- 运行中为 1,否则为 0

无效的数值(NaN、Inf、≤ 0)会被静默忽略,不会报错。

协程看门狗

在 ESP32 版本中,Lua C 调用的嵌套上限从 200 降到了 60(LUAI_MAXCCALLS = 60),因为在 do_lua_script 任务的 8 KB 栈上,原来的上限根本无法达到。如果您的脚本使用 coroutine.create / wrap / resume,务必在 init.lua 中调用 armCoroutineWatchdog():否则协程会在触及上限之前耗尽栈空间。

变量

下表列出了脚本中可用的变量。RO 表示只读,RW 表示可以通过 setNumVariable 修改。

传感器读数(RO)

  • SteamTemp——蒸汽传感器温度。
  • PipeTemp——塔节传感器温度。
  • WaterTemp——水传感器温度。
  • TankTemp——蒸馏釜传感器温度。
  • ACPTemp——TCA(通大气管)传感器温度。
  • pressure_value——系统中的压力。
  • alcohol——实时计算出的酒精度。
  • alcohol_s——稳定状态下的酒精度。

状态(RO)

  • PowerOn——0/1,加热开启标志。
  • PauseOn——0/1,取酒暂停标志。
  • pump_started——0/1,水泵正在运行。
  • valve_status——0/1,水阀状态。
  • water_pump_speed——水泵当前速度。
  • program_Wait——0/1,程序已暂停。
  • SamSetup_Mode——Samovar 的运行模式。
  • Samovar_Mode——当前的过程模式。
  • Samovar_CR_Mode——按内部分类的当前模式。
  • target_power_volt——调节器的目标电压。
  • WFpulseCount、WFflowRate、WFtotalMilliLitres——流量计读数(如果有)。

当前程序行的参数(program_volume、program_speed、program_temp、program_power、program_time、program_capacity_num)以及当前取酒容器(capacity_num)也是只读的。

可以修改的参数(RW)

  • acceleration_temp——加速加热管的温度。
  • boil_temp——沸腾温度。
  • loop_lua_fl——0/1,关闭或开启每秒一次的脚本启动。
  • SetScriptOff——0/1,强制关闭脚本。
  • show_lua_script——0/1,将脚本内容和变量值输出到 COM 端口和控制台(用于调试)。
  • test_num_val——用于调试的数值变量。
  • wp_count——流量计脉冲数(在启用 USE_WATER_PUMP 时)。

字符串变量

  • SamovarStatus(RW)——界面中显示的当前状态。
  • program_type(RO)——正在运行的程序类型。
  • test_str_val(RW)——用于调试的字符串变量。
  • Msg(特殊)——读取时会沿事件环移动游标;写入通过 setStrVariable 进行。

虚拟传感器(SAMOVAR_LUA_SIMULATION)

启用模拟后,可使用 VirtualSteamTemp、VirtualPipeTemp、VirtualWaterTemp、VirtualTankTemp、VirtualACPTemp(均为 RW)。它们用于在没有硬件的情况下调试脚本。

时钟(RO)

YY、MM、DD、HH、MI、SS——根据控制器 RTC 得到的当前年、月、日、时、分和秒。

示例

setObject 和 getObject

n = 156
setObject("MyObject", n)
print(n)            -- 156
n = n + 5
print(n)            -- 161
n = getObject("MyObject")
print(n)            -- 156(读回了保存的值)

将蒸馏釜温度发送到 Telegram

local function urlencode(url)
  if url == nil then return end
  url = url:gsub("\n", "\r\n")
  url = url:gsub("([^%w ])", function(c)
    return string.format("%%%02X", string.byte(c))
  end)
  url = url:gsub(" ", "+")
  return url
end

function SendTelegram(text)
  local token = "5177...:AAG0b...."
  local chat_id = "38806....."
  http_request("http://api.telegram.org/bot" .. token
    .. "/sendMessage?chat_id=" .. chat_id
    .. "&text=" .. urlencode(text))
end

local text = "Current boiler temperature = "
  .. getNumVariable("TankTemp")
SendTelegram(text)

模拟液位传感器与水泵

该示例读取模拟液位传感器,当液位落在给定范围内时开启水泵。水泵状态通过 setObject 在多次启动之间保存。

start_pump = getObject("start_pump", "NUMERIC") + 0

sensor = analogRead()
if sensor >= 1000 and sensor <= 2000 and start_pump == 0 then
  setObject("start_pump", 1)
  digitalWrite(4, 1)
  print("Start pump")
elseif sensor == 0 then
  setObject("start_pump", 0)
  if start_pump == 1 then
    digitalWrite(4, 0)
    print("Finish pump")
  end
end

安全与限制

  • 同时最多 10 个计时器。
  • 对象存储中最多 32 个键(LUA_OBJECT_STORE_MAX_KEYS)。
  • Lua C 调用的嵌套上限:60(LUAI_MAXCCALLS)。如果使用 coroutine.*,务必调用 armCoroutineWatchdog()。
  • pinMode 可用的引脚:仅 RELE_CHANNEL1…RELE_CHANNEL4 和 LUA_PIN。奶酪 pH 电极占用的引脚不能使用——函数会返回错误。
  • 向 analogRead() 施加的电压不得超过 3.3 V。
  • setBodyTemp 仅在精馏模式下有效;在其他模式下会输出警告。
  • 调用 setAlarm() 时,控制器会停止加热、关闭阀门并关闭水泵。
  • i2cpump_start 的数值参数必须是有限的正数;NaN/Inf/≤0 会被静默忽略。

其他资源

发表回复