下载 PDF
        SAMOVAR 
×
菜单

关于 Lua 脚本语言。

 
Samovar 支持基于脚本编程语言 Lua 的自动化。Lua 支持在编译固件时启用:在配置器中勾选“使用 Lua”(«Использовать Lua»)复选框(位于“硬件”(«Оборудование»)部分),在 PlatformIO 中选择 Samovar_lua环境,手动编译时在 user_config_override.h 文件中加入 #define USE_LUA 一行。脚本编辑器通过设置页面上的“编辑器”(«Редактор»)按钮打开(地址 http://samovar.local/edit)。不需要输入用户名和密码。
当前 Lua 版本为 5.4.4
 
Samovar 启动时会运行 init.lua 文件。它的作用是开启或关闭每秒一次的脚本执行循环,并初始化其他脚本要用到的变量。出厂的 init.lua 关闭了该循环:setNumVariable("loop_lua_fl",0)。
如果开启了循环运行脚本,每秒会运行两个脚本:先运行 script.lua,再运行与工作模式对应的脚本:rectificat.lua(精馏)、dist.lua(蒸馏)、beer.lua(啤酒)、bk.lua(醪塔)、nbk.lua(NBK)、suvid.lua(低温慢煮)、cheese.lua(奶酪制作)。在“Lua 模式”(«Lua-режим»)下(设置中模式列表里的一项,只在带 Lua 的固件中可见)没有模式脚本——全部模式逻辑都写在 script.lua 中。打开 http://samovar.local/lua 可以单次运行这一对脚本,打开 http://samovar.local/lua?script=file_name 可以运行单独的文件。
脚本在同一个任务中依次运行:一个脚本运行时,另一个等待。单次脚本运行不能超过 20 秒——超时后会被中止,并显示消息“Lua: chunk execution aborted by timeout”(«Lua: выполнение чанка прервано по таймауту»)。如果模式脚本连续 5 次出错结束,每秒循环会停止(loop_lua_fl 被重置为 0),并在控制台输出一条汇总消息;script.lua 连续出错 5 次后将不再运行,直到重新加载脚本,但循环不会停止。脚本错误会输出到浏览器控制台和串口监视器。
 
还可以在界面中添加运行文件的按钮。文件名为 btn_<mode>_button<N>.lua,其中 mode 是 rect、dist、beer、bk、nbk、suvid 或 cheese,N 是按钮编号。例如 btn_rect_button1.lua、btn_beer_button2.lua。按钮显示在其所属模式主页面的“附加”(«Дополнительно»)选项卡上的 Lua 区块中;那里还有用于执行一两行代码的“Lua:”(«Lua:»)输入框和“执行 Lua”(«Выполнить Lua»)按钮,以及“Lua 状态”(«Статус Lua»)——由脚本通过 setLuaStatus 函数设置的一行文字。
按钮名称在文件第一行用 --|名称^ 形式的注释设置(例如 --|Start^)。如果没有这一行,按钮按找到文件的顺序命名为 LUA1、LUA2 等。
出厂时每个模式有两个按钮:“开始”(«Начать»)(btn_*_button1.lua——开启每秒循环)和“停止”(«Остановить»)(btn_*_button2.lua——关闭循环)。
按钮的主要用途是启动循环运行脚本(例如设置一个变量的值,循环中运行的脚本随后可以读取它),或者开关某个执行设备(例如水泵)。
 
因此,按钮的使用架构如下(以蒸馏模式为例):
  1. 主受控循环:每秒一次 script.lua => dist.lua => script.lua => dist.lua =>...(“受控”是指可以在任何脚本中通过全局变量以编程方式启动或停止它)
  2. script.lua 脚本对所有模式通用,可以在其中放置任何模式下都要运行的代码(精馏、啤酒、低温慢煮等)
  3. dist.lua 脚本只在蒸馏模式下使用,可以在其中放置每秒都要执行的全部逻辑,这些逻辑取决于脚本从 Samovar 全局变量中获取的参数,例如 getNumVariable("TankTemp"),或者取决于任何脚本保存在公共命名空间中的变量,例如 getObject("StartTankFilling")。也就是说,可以在这个脚本的代码中写入例如这样的条件:
    if (StartTankFilling == "true" and TankFillingPercent < 80) then StartPump() else StopPump() end
    脚本每秒都会检查是否有需要向蒸馏釜加料的标志;如果有,再检查是否已经加满 80%,以及是否应该停止之前启动的泵。
  4. 按钮脚本 btn_dist_button1.lua:按下按钮时,通过 setObject("StartTankFilling", "true") 设置需要向蒸馏釜加料的标志。一秒后的下一个周期,dist.lua 脚本会读取这个标志并开启泵(如果蒸馏釜还没有加满)。这样,所有脚本共享一个可通过 setObject/getObject 访问的公共命名空间,可以借此交换数据。
  5. 按钮脚本 btn_dist_button2.lua:按下按钮时,可以通过 setNumVariable("loop_lua_fl", 0) 停止主受控循环,例如在脚本出现问题时。
 
L 型程序行(Lua 阶段)。除 NBK 外,所有模式的程序(精馏、蒸馏、醪塔、啤酒、奶酪制作)中都有 L 型行——由 Lua 控制的阶段。在程序编辑器中选择这种类型时,会打开“Lua 阶段”(«Lua-этап»)窗口,在其中设置:
程序运行到 L 行时,Samovar 运行所选文件并等待 setNextProgram() 调用——即转到下一行。脚本出错(“Lua 出错结束”(«Lua завершилась с ошибкой»))或超时都会停止该模式。L 型行不设置其他字段(温度、速度、容器、设备)。在文本形式中,这种行以 ^ 字符分隔文件和参数来保存,例如精馏中的 L;120;hops.lua^3^45;0;0;0。
dose.lua 脚本示例:按第二个参数给定的时间开启编号为第一个参数的继电器(在程序中:文件 dose.lua,参数 3 和 1500):
set_i2c_rele_state(tonumber(arg[1]), 1)
delay(tonumber(arg[2]))
set_i2c_rele_state(tonumber(arg[1]), 0)
setNextProgram()
如果运行过程中通过文件编辑器上传了某个 L 行正在运行的文件的新版本,Samovar 会重新读取脚本,无需重新启动程序。
 
Samovar 函数
在脚本中可以访问 Samovar 的内部变量(其中一部分不能修改,只能读取),并调用 Samovar 的函数。切换模式期间,改变 Samovar 状态的函数会以错误“mode switch blocks state changes”结束。
引脚编号取决于开发板。在这里和下文中,请传入其数值:
脚本中定义了常量 INPUT、OUTPUT、LOW、HIGH 以及命令结果代码 ACTUATOR_COMMAND_ACCEPTED、ACTUATOR_COMMAND_PENDING、ACTUATOR_COMMAND_APPLIED、ACTUATOR_COMMAND_FAILED。
 
pinMode(pin, mode) – 与 Arduino 的同名函数相同,将指定的输入/输出(引脚)设置为输入或输出模式。只有 RELE_CHANNEL1–RELE_CHANNEL4 和 LUA_PIN 端口可以更改模式。也就是说,如果要把 DEVKIT 开发板的 RELE_CHANNEL4 端口设为输入,调用 pinMode(13, INPUT);设为输出,调用 pinMode(13, OUTPUT)。
digitalWrite(pin, Value) – 与 Arduino 的同名函数相同,向数字输出(引脚)输出 HIGH 或 LOW。可用的端口有 RELE_CHANNEL1–RELE_CHANNEL4、WATER_PUMP_PIN、LUA_PIN、ALARM_BTN_PIN 和 BTN_PIN,其他端口会被忽略。也就是说,如果要在 RELE_CHANNEL4 输出端设置高电平,调用 digitalWrite(13, 1)。对于 WATER_PUMP_PIN 上的 PWM 水泵,值 1 让水泵全速运转,0 让它停止。如果加热紧急保护已触发,则不会写入加热通道(RELE_CHANNEL1、RELE_CHANNEL4)。
digitalRead(pin) – 与 Arduino 的同名函数相同,读取指定输入的值——HIGH 或 LOW。也就是说,如果要读取 RELE_CHANNEL4 输入或输出上的值,调用 digitalRead(13)
analogRead() – 与 Arduino 的同名函数相同,读取模拟输入 LUA_PIN 的值(无参数)。施加到模拟输入上的电压会被转换为 0 到 4095 的数值。注意!向输入端施加高于 3.3 伏的电压可能损坏该端口甚至整个 ESP32。在奶酪制作模式下,LUA_PIN 被 pH 传感器占用:对它调用 pinMode 和 digitalWrite 会出错结束。
exp_pinMode(pin, mode) – 与 pinMode(pin, mode) 相同,但用于控制 PCF8575 端口扩展器;pin 是扩展器端口号(0–15),mode 为 INPUT、OUTPUT 或 INPUT_PULLUP。扩展器的所有端口都可以选为读取或输出
exp_digitalWrite(pin, Value) – 与 digitalWrite(pin, Value) 相同,但用于控制 PCF8575 端口扩展器;pin 是扩展器端口号(0–15),Value 为 1 或 0。扩展器的所有端口都可以写入
exp_digitalRead(pin) – 与 digitalRead(pin) 相同,但用于控制 PCF8575 端口扩展器;pin 是扩展器端口号(0–15)。扩展器的所有端口都可以读取
exp_analogWrite(Value) – 与 analogWrite(pin, Value) 相同,但用于控制 PCF8591 端口扩展器,Value 为 0 到 255 的数值。该扩展器有一个可用于输出的端口。
exp_analogRead(pin) – 与 analogRead() 相同,但用于控制 PCF8591 端口扩展器;pin 是扩展器输入编号(0–3),返回 0 到 255 的数值。
exp_* 函数只存在于用 USE_EXPANDER(PCF8575)和 USE_ANALOG_EXPANDER(PCF8591)编译的固件中。
delay(ms) – 与 Arduino 的同名函数相同,按参数给定的毫秒数暂停程序执行(1 秒等于 1000 毫秒)。不超过 1000。
millis() – 与 Arduino 的同名函数相同,返回 Samovar 启动以来经过的毫秒数
setTimer(Num, Sec) – 将编号为 Num 的定时器设置为 Sec 秒(最多 65535)。共有 10 个定时器,编号 1 到 10。也就是说,脚本最多可以同时使用 10 个定时器
getTimer(Num) – 获取编号为 Num 的定时器剩余的秒数。如果定时器未设置或时间已到,函数返回 0
sendMsg(Msg, Level) – 如果 Level = -1,消息 Msg 输出到 COM 端口和浏览器控制台,便于调试。如果 Level 为 0、1 或 2,消息作为系统消息发送(0 – 报警,1 – 警告,2 – 通知):发送到显示屏、网页界面、应用和网站。
setPower(Power) – 开启/关闭 Samovar。setPower(0) 关闭,setPower(1) 开启(与当前模式的加热按钮相同)
setCurrentPower(Value) – 在调压器上设置值 Value。如果使用带功率控制的调压器(SEM/AVR),Value 是要设置的功率(瓦),否则是电压,精度 0.1 V。该函数只存在于带调压器的固件中。
setBodyTemp() – 在精馏过程中设定酒心温度。只在精馏模式下有效。在其他模式下会在控制台输出无法设定酒心温度的消息
setMixer(Val) – 开启/关闭搅拌器。setMixer(0) – 关闭,setMixer(1) – 开启
openValve(Val) – 打开/关闭水阀。openValve(0) – 关闭,openValve(1) – 打开
setPumpPwm(Val) – 设置水泵转速,Val 为 0 到 1023。只存在于带 PWM 水泵的固件中(USE_WATER_PUMP)。
setMixer、openValve、setCurrentPower 和 setPumpPwm 返回命令结果代码(ACTUATOR_COMMAND_* 常量)。
setAlarm() – 进入报警模式。Samovar 会切断电源、关闭水阀并关闭水泵
setNextProgram() – 转到下一个程序。相当于在界面中按下“下一个程序”(«Следующая программа»)按钮。只在加热开启时有效。
setPauseWithdrawal(Val) – 暂停/恢复取酒。setPauseWithdrawal(0) – 恢复,setPauseWithdrawal(1) – 暂停
setCapacity(Num) – 将舵机转到编号为 Num 的容器(0 – 排放,1 及以上 – 容器)。
setServoAngle(Angle) – 将舵机转到 Angle 度(0–180)。如果固件有舵机输出(SERVO_PIN),返回 1,否则返回 0。用于自制添加剂投放器(见 添加剂投放器);舵机与 setCapacity 共用,因此在精馏中转动舵机后必须重新设置容器。
getState() – 获取 Samovar 状态(一个数字),便于判断 Samovar 所处的状态:0 – 空闲;精馏:10 – 正在取酒,15 – 程序行之间的自动暂停,20 – 程序完成,30 – 取酒泵校准,50 – 加速升温,51 – 稳定,52 – 稳定完成;40 – 手动暂停(任何模式);1000 – 正在蒸馏,2000 – 啤酒,3000 – 醪塔,4000 – NBK,5000 – 奶酪制作。
setNumVariable("Variable", Val) – 设置 Samovar 的内部数值变量。并非所有变量都可以设置值(列表见下文)。setNumVariable("loop_lua_fl", 1) 会开启每秒一次的脚本循环。注意!如果不了解 Samovar 的工作原理,最好不要使用这个函数,因为它可能扰乱 Samovar 的运行
setStrVariable("Variable", Val) – 设置 Samovar 的内部字符串变量。并非所有变量都可以设置值。setStrVariable("SamovarStatus", "测试 Samovar 状态")
getNumVariable("Variable") – 获取 Samovar 的内部数值变量。TankTemp = getNumVariable("TankTemp") 会把蒸馏釜温度值赋给脚本变量 TankTemp
getStrVariable("Variable") – 获取 Samovar 的内部字符串变量。
program_type = getStrVariable("program_type")– 会把当前运行程序的类型赋给脚本变量 program_type。
 
脚本变量只在脚本运行期间存在,下一次运行时不会保留它们的值。有时需要在一次脚本运行中记住某个变量的值,并在另一次运行中读取它。为此有两个函数——setObject("Object",Val) 和 getObject("Object")/getObject("Object","NUMERIC")。设置的变量值保留到重启为止。不同的对象最多 32 个,超出时 setObject 会出错结束。
setLuaStatus(Status) - 在界面中显示脚本状态(“附加”(«Дополнительно»)选项卡上的“Lua 状态”(«Статус Lua»))。例如,跟踪脚本的执行:
i = getObject("cnt", "NUMERIC")
i = i + 1
setLuaStatus("计数器 cnt = "..i)
setObject("cnt", i)
setObject("Object",Val) – 在 Samovar 内存中记住值为 Val 的对象 Object。
getObject("Object") – 获取之前保存的对象 Object 的值。如果没有保存过这个对象,返回空字符串。如果把空字符串当作数字使用,它的值会是 nil——未定义。为了方便处理数值、免去对 nil 的检查,可以带附加参数 "NUMERIC" 调用这个函数;这时如果对象尚未初始化,会返回 0。
setObject/getObject 的使用示例:
n = 156
setObject("MyObject", n)
print (n)
n = n + 5
print (n)
n = getObject("MyObject")
print (n)
运行这个脚本,Arduino 串口监视器中会输出
156
161
156
 
http_request(Url) – 向地址 Url 发送 GET 请求并返回响应正文(或字符串 "error")。例如,可以把读数发送到自己的网络服务。也可以传入四个参数来发送 POST 请求:地址、方法、Content-Type 头和正文:
http_request("http://test.com:80/post?foo1=bar1", "POST", "Content-Type: text/text; charset=utf-8", "body")
将蒸馏釜温度发送到外部网络服务的脚本示例(请把地址和参数换成自己的):
local char_to_hex = function(c)
  return string.format("%%%02X", string.byte(c))
end
 
local function urlencode(url)
  if url == nil then
    return
  end
  url = url:gsub("\n", "\r\n")
  url = url:gsub("([^%w ])", char_to_hex)
  url = url:gsub(" ", "+")
  return url
end
 
function SendToServer(text)
  local key = "my_secret_key" -- 你的服务访问密钥
  http_request("http://example.com/notify?key=" .. key .. "&text=" .. urlencode(text))
end
 
local text = "当前蒸馏釜温度 = ".. getNumVariable("TankTemp")
SendToServer(text)
 
I2CStepper 板(搅拌器、计量泵、继电器)的函数——set_stepper_by_time、set_stepper_target、get_stepper_status、i2cpump_start、i2cpump_stop、i2cpump_get_speed、i2cpump_get_target_ml、i2cpump_get_remaining_ml、i2cpump_get_running、set_mixer_pump_target、get_mixer_pump_status、set_i2c_rele_state、get_i2c_rele_state、check_I2C_device——见 I2CStepper 页面。
 
变量
脚本中默认可用的内部变量列表(它们的值在每次脚本运行前填入,通过赋值修改没有意义):
 
可通过 getNumVariable 读取的变量(只读):WFpulseCount、pump_started、valve_status、SamSetup_Mode、Samovar_Mode、Samovar_CR_Mode(当前模式和正在切换到的模式,编号与 SamSetup_Mode 相同)、SteamTemp、PipeTemp、WaterTemp、TankTemp、ACPTemp、WFtotalMilliLitres、WFflowRate、program_volume、program_speed、program_temp、program_power、program_time、program_capacity_num、capacity_num、target_power_volt、PowerOn、PauseOn、program_Wait、alcohol(按温度计算的蒸馏釜酒精度)、alcohol_s(按蒸汽计算的酒精度)、water_pump_speed(水泵转速 0–1023)、pressure_value(蒸馏釜压力)、YY、MM、DD、HH、MI、SS(年、月、日、时、分、秒)。
可通过 getNumVariable 和 setNumVariable 访问的变量:acceleration_temp、boil_temp(记住的沸腾温度)、wp_count、test_num_val、loop_lua_fl、SetScriptOff、show_lua_script。只能通过 setNumVariable 设置:pmpKp、pmpKi、pmpKd——水泵的 PID 系数。
字符串变量(getStrVariable/setStrVariable):SamovarStatus——主页面上的状态文字(可读写),test_str_val(可读写),program_type(只读),Msg——读取时返回下一条未读系统消息或空字符串,写入时添加一条消息。
 
用于控制脚本的变量:
loop_lua_fl – 取值 0 或 1。0 – 不运行每秒脚本循环,1 – 运行。
SetScriptOff – 取值 0 或 1。1 – 停止每秒循环(重置 loop_lua_fl)。程序结束时 Samovar 会自动将其设为 1。
show_lua_script – 取值 0 或 1。0 – 不在控制台和串口监视器中显示正在运行的脚本,1 – 显示。可用于调试。同时还会显示为该脚本设置的所有变量的值。
例如,如果在 init.lua 脚本中调用下面这两个函数,每秒会运行两个脚本(script.lua 和由当前 Samovar 模式决定的脚本),并且每个脚本都会输出到串口监视器和浏览器控制台
setNumVariable("loop_lua_fl",1)
setNumVariable("show_lua_script",1)