Descargar PDF
        SAMOVAR 
×
Menú

Sobre el lenguaje de script Lua.

 
Samovar admite automatización basada en el lenguaje de programación de scripts Lua. La compatibilidad con Lua se activa al compilar el firmware: en el configurador, con la casilla «Usar Lua» («Использовать Lua») (sección «Equipo» («Оборудование»)); en PlatformIO, con el entorno Samovar_lua; en una compilación manual, con la línea #define USE_LUA en el archivo user_config_override.h. El editor de scripts se abre con el botón «Editor» («Редактор») de la página de Ajustes (dirección http://samovar.local/edit). No se pide usuario ni contraseña.
La versión actual de Lua es la 5.4.4
 
Al arrancar Samovar se ejecuta el archivo init.lua. Su función es activar/desactivar el bucle de ejecución de scripts cada segundo e inicializar las variables que se usarán en otros scripts. Tal como se entrega, init.lua desactiva el bucle: setNumVariable("loop_lua_fl",0).
Si la ejecución de scripts en bucle está activada, cada segundo se ejecutan dos scripts: primero script.lua y después el script cuyo nombre depende del modo de funcionamiento: rectificat.lua (Rectificación), dist.lua (Destilación), beer.lua (Cerveza), bk.lua (Columna de mosto), nbk.lua (NBK), suvid.lua (Sous vide), cheese.lua (Elaboración de queso). En el «Modo Lua» («Lua-режим») (un elemento de la lista de modos de Ajustes, visible solo en una compilación con Lua) no hay script de modo: toda la lógica del modo se escribe en script.lua. También se puede ejecutar una vez el par de scripts abriendo http://samovar.local/lua, y un archivo concreto en http://samovar.local/lua?script=file_name.
Los scripts se ejecutan uno tras otro en una única tarea: mientras uno se ejecuta, el otro espera. Una sola ejecución de un script no puede durar más de 20 segundos; pasado ese tiempo se interrumpe con el mensaje «Lua: ejecución del chunk interrumpida por tiempo de espera» («Lua: выполнение чанка прервано по таймауту»). Si un script de modo termina con error 5 veces seguidas, el bucle de cada segundo se detiene (loop_lua_fl se pone a 0) y se imprime un único mensaje de resumen en la consola; script.lua deja de ejecutarse tras 5 errores seguidos hasta que se recarguen los scripts, sin detener el bucle. Los errores de los scripts se escriben en la consola del navegador y en el monitor del puerto.
 
También puede añadir a la interfaz botones para ejecutar archivos. Nombre del archivo: btn_<mode>_button<N>.lua, donde mode es rect, dist, beer, bk, nbk, suvid o cheese, y N es el número del botón. Por ejemplo, btn_rect_button1.lua, btn_beer_button2.lua. Los botones se muestran en la pestaña «Adicional» («Дополнительно») de la página principal del modo para el que se crearon, en el bloque Lua; allí también está el campo «Lua:» («Lua:») con el botón «Ejecutar Lua» («Выполнить Lua») para ejecutar una o dos líneas de código, y «Estado Lua» («Статус Lua»), una línea que el script establece con la función setLuaStatus.
El nombre del botón se fija en la primera línea del archivo con un comentario de la forma --|Nombre^ (por ejemplo, --|Start^). Si no existe esa línea, los botones se llaman LUA1, LUA2, etc., en el orden de los archivos encontrados.
Tal como se entrega, cada modo tiene dos botones: «Iniciar» («Начать») (btn_*_button1.lua, activa el bucle de cada segundo) y «Detener» («Остановить») (btn_*_button2.lua, lo desactiva).
La función principal de los botones es poner en marcha la ejecución de scripts en bucle (por ejemplo, fijando el valor de una variable que luego se puede leer en el script que se ejecuta en bucle) o encender o apagar un dispositivo de control (por ejemplo, una bomba).
 
Así, la arquitectura de uso de los botones es la siguiente (como ejemplo, para el modo de destilación):
  1. El bucle principal controlado: una vez por segundo script.lua => dist.lua => script.lua => dist.lua =>... (controlado en el sentido de que se puede iniciar/detener por programa desde cualquier script mediante una variable global)
  2. El script script.lua es común a todos los modos; aquí puede poner el código que debe ejecutarse en cualquier modo (rectificación, cerveza, sous vide, etc.)
  3. El script dist.lua solo se usará en el modo de destilación; en él puede poner toda la lógica que debe ejecutarse una vez por segundo según parámetros que este script obtiene de las variables globales de Samovar, por ejemplo getNumVariable("TankTemp"), o de variables guardadas por cualquier script en el espacio de nombres común, por ejemplo getObject("StartTankFilling"). Es decir, en el código de este script puede poner, por ejemplo, la condición:
    if (StartTankFilling == "true" and TankFillingPercent < 80) then StartPump() else StopPump() end
    y cada segundo el script comprobará si hay un indicador de que hay que llenar la caldera y, si lo hay, si ya está llena al 80% y si ahora hay que detener la bomba puesta en marcha antes.
  4. El script de botón btn_dist_button1.lua, que, al pulsar el botón, fija el indicador de que hay que llenar la caldera mediante setObject("StartTankFilling", "true"). En el siguiente ciclo, un segundo después, el script dist.lua leerá este indicador y encenderá la bomba (si la caldera aún no está llena). Así, todos los scripts comparten un espacio de nombres común, accesible mediante setObject/getObject, que les permite intercambiar datos.
  5. El script de botón btn_dist_button2.lua, que, al pulsar el botón, puede detener el bucle principal controlado mediante setNumVariable("loop_lua_fl", 0), por ejemplo si algo va mal con los scripts.
 
Una línea de programa de tipo L (etapa Lua). En los programas de todos los modos salvo NBK (Rectificación, Destilación, Columna de mosto, Cerveza, Elaboración de queso) hay una línea de tipo L, una etapa controlada por Lua. Al elegir este tipo en el editor del programa se abre la ventana «Etapa Lua» («Lua-этап»), en la que se indica:
Cuando el programa llega a una línea L, Samovar ejecuta el archivo seleccionado y espera una llamada a setNextProgram(), que es el paso a la línea siguiente. Un error del script («Lua terminó con un error» («Lua завершилась с ошибкой»)) o el agotamiento del tiempo límite detienen el modo. Los demás campos de la línea (temperatura, velocidad, recipiente, dispositivo) no se indican para el tipo L. En forma de texto, una línea así se guarda como el archivo y los parámetros separados por el carácter ^, por ejemplo L;120;hops.lua^3^45;0;0;0 para la rectificación.
Un ejemplo del script dose.lua, que enciende el relé cuyo número está en el primer parámetro durante el tiempo indicado en el segundo (en el programa: archivo dose.lua, parámetros 3 y 1500):
set_i2c_rele_state(tonumber(arg[1]), 1)
delay(tonumber(arg[2]))
set_i2c_rele_state(tonumber(arg[1]), 0)
setNextProgram()
Si durante el funcionamiento sube mediante el editor de archivos una nueva versión del archivo que está ejecutando en ese momento una línea L, Samovar volverá a leer el script sin reiniciar el programa.
 
Funciones de Samovar
Desde los scripts puede acceder a las variables internas de Samovar (algunas no se pueden cambiar, son de solo lectura) y llamar a funciones de Samovar. Mientras hay un cambio de modo en curso, las funciones que cambian el estado de Samovar terminan con el error «mode switch blocks state changes».
Los números de pin dependen de la placa. Aquí y más abajo, pase su valor numérico:
En los scripts están definidas las constantes INPUT, OUTPUT, LOW, HIGH y los códigos de resultado de las órdenes ACTUATOR_COMMAND_ACCEPTED, ACTUATOR_COMMAND_PENDING, ACTUATOR_COMMAND_APPLIED, ACTUATOR_COMMAND_FAILED.
 
pinMode(pin, mode) – como la función homónima de Arduino, establece el modo de funcionamiento de la entrada/salida (pin) indicada como entrada o como salida. El cambio de modo solo está disponible para los puertos RELE_CHANNEL1–RELE_CHANNEL4 y LUA_PIN. Es decir, si necesita configurar el puerto RELE_CHANNEL4 de la placa DEVKIT como entrada, llame a pinMode(13, INPUT); como salida, pinMode(13, OUTPUT).
digitalWrite(pin, Value) – como la función homónima de Arduino, aplica un valor HIGH o LOW a una salida digital (pin). Están disponibles los puertos RELE_CHANNEL1–RELE_CHANNEL4, WATER_PUMP_PIN, LUA_PIN, ALARM_BTN_PIN y BTN_PIN; los demás se ignoran. Es decir, si necesita poner un valor alto en la salida RELE_CHANNEL4, llame a digitalWrite(13, 1). Para WATER_PUMP_PIN con una bomba PWM, el valor 1 enciende la bomba a plena velocidad y 0 la detiene. Si se ha activado la protección de emergencia del calentamiento, no se escribe en los canales del calentador (RELE_CHANNEL1, RELE_CHANNEL4).
digitalRead(pin) – como la función homónima de Arduino, lee el valor de la entrada indicada, HIGH o LOW. Es decir, si necesita leer el valor establecido en la entrada o salida RELE_CHANNEL4, llame a digitalRead(13)
analogRead() – como la función homónima de Arduino, lee el valor de la entrada analógica LUA_PIN (sin parámetros). La tensión aplicada a la entrada analógica se convertirá en un valor de 0 a 4095. ¡ATENCIÓN! Aplicar a la entrada una tensión superior a 3.3 voltios puede dañar el puerto o todo el ESP32. En el modo Elaboración de queso, LUA_PIN está ocupado por el sensor de pH: pinMode y digitalWrite para él terminan con error.
exp_pinMode(pin, mode) – como pinMode(pin, mode), pero para controlar el expansor de puertos PCF8575; pin es el número de puerto del expansor (0–15), mode es INPUT, OUTPUT o INPUT_PULLUP. Todos los puertos del expansor se pueden elegir tanto para lectura como para salida
exp_digitalWrite(pin, Value) – como digitalWrite(pin, Value), pero para controlar el expansor de puertos PCF8575; pin es el número de puerto del expansor (0–15), Value es 1 o 0. Todos los puertos del expansor están disponibles para escritura
exp_digitalRead(pin) – como digitalRead(pin), pero para controlar el expansor de puertos PCF8575; pin es el número de puerto del expansor (0–15). Todos los puertos del expansor están disponibles para lectura
exp_analogWrite(Value) – como analogWrite(pin, Value), pero para controlar el expansor de puertos PCF8591; Value es un valor de 0 a 255. El expansor tiene un puerto disponible para salida.
exp_analogRead(pin) – como analogRead(), pero para controlar el expansor de puertos PCF8591; pin es el número de entrada del expansor (0–3); devuelve un valor de 0 a 255.
Las funciones exp_* solo existen en un firmware compilado con USE_EXPANDER (PCF8575) y USE_ANALOG_EXPANDER (PCF8591).
delay(ms) – como la función homónima de Arduino, pausa la ejecución del programa durante el número de milisegundos indicado en el parámetro (1000 milisegundos en 1 segundo). No más de 1000.
millis() – como la función homónima de Arduino, devuelve el número de milisegundos desde que arrancó Samovar
setTimer(Num, Sec) – pone el temporizador número Num a Sec segundos (hasta 65535). Hay 10 temporizadores, del 1 al 10. Es decir, los scripts pueden trabajar a la vez con no más de 10 temporizadores
getTimer(Num) – obtiene el tiempo restante del temporizador número Num en segundos. Si el temporizador no está puesto o el tiempo se ha agotado, la función devuelve 0
sendMsg(Msg, Level) – si Level = -1, el mensaje Msg se imprime en el puerto COM y en la consola del navegador, útil para depurar. Si Level es 0, 1 o 2, el mensaje se envía como mensaje del sistema (0 – alarma, 1 – advertencia, 2 – notificación): a la pantalla, a la interfaz web, a las aplicaciones y al sitio web.
setPower(Power) – enciende/apaga Samovar. setPower(0) lo apaga, setPower(1) lo enciende (igual que el botón de calentamiento del modo actual)
setCurrentPower(Value) – establece el valor Value en el regulador. Si se usa un regulador con control de potencia (SEM/AVR), Value es la potencia en vatios que se fija; en otro caso, es la tensión con una precisión de 0.1 V. La función solo existe en una compilación con regulador de potencia.
setBodyTemp() – fija la temperatura del corazón durante la rectificación. Solo funciona en el modo de rectificación. En otros modos imprime en la consola un mensaje de que no se puede fijar la temperatura del corazón
setMixer(Val) – enciende/apaga el agitador. setMixer(0) – apagado, setMixer(1) – encendido
openValve(Val) – abre/cierra la válvula de agua. openValve(0) – cerrar, openValve(1) – abrir
setPumpPwm(Val) – fija la velocidad de la bomba de agua, Val de 0 a 1023. Solo existe en una compilación con bomba PWM (USE_WATER_PUMP).
setMixer, openValve, setCurrentPower y setPumpPwm devuelven el código de resultado de la orden (las constantes ACTUATOR_COMMAND_*).
setAlarm() – activa el modo de alarma. Samovar apagará la potencia, cerrará la válvula de agua y apagará la bomba de agua
setNextProgram() – pasa al programa siguiente. Igual que pulsar el botón «Programa siguiente» («Следующая программа») de la interfaz. Solo funciona con el calentamiento encendido.
setPauseWithdrawal(Val) – pausa/reanuda la extracción. setPauseWithdrawal(0) – reanudar, setPauseWithdrawal(1) – pausar
setCapacity(Num) – gira el servo al recipiente número Num (0 – desagüe, 1 y siguientes – recipientes).
setServoAngle(Angle) – gira el servo a Angle grados (0–180). Devuelve 1 si el firmware tiene salida de servo (SERVO_PIN), en otro caso 0. Se usa para dosificadores de aditivos caseros (consulte Dosificadores de aditivos); el servo es compartido con setCapacity, así que tras girarlo en rectificación hay que volver a fijar el recipiente.
getState() – obtiene el estado de Samovar (un número), útil para saber en qué estado se encuentra Samovar: 0 – en reposo; rectificación: 10 – extracción en curso, 15 – pausa automática entre líneas del programa, 20 – programa completado, 30 – calibración de la bomba de extracción, 50 – calentamiento inicial, 51 – estabilización, 52 – estabilización completada; 40 – pausa manual (en cualquier modo); 1000 – destilación en curso, 2000 – cerveza, 3000 – columna de mosto, 4000 – NBK, 5000 – elaboración de queso.
setNumVariable("Variable", Val) – establece una variable numérica interna de Samovar. No a todas las variables se les puede asignar valor (lista más abajo). setNumVariable("loop_lua_fl", 1) activará el bucle de scripts de cada segundo. ¡Atención! Si no entiende cómo funciona Samovar, es mejor no usar esta función, ya que podría alterar el funcionamiento de Samovar
setStrVariable("Variable", Val) – establece una variable de cadena interna de Samovar. No a todas las variables se les puede asignar valor. setStrVariable("SamovarStatus", "Test Samovar status")
getNumVariable("Variable") – obtiene una variable numérica interna de Samovar. TankTemp = getNumVariable("TankTemp") asignará el valor de la temperatura de la caldera a la variable del script TankTemp
getStrVariable("Variable") – obtiene una variable de cadena interna de Samovar.
program_type = getStrVariable("program_type")– asignará a la variable del script program_type el tipo del programa que se está ejecutando.
 
Las variables de un script solo existen mientras el script se ejecuta. En la siguiente ejecución sus valores no se conservan. A veces hay que recordar el valor de una variable en una ejecución del script y leerlo en otra. Para ello hay dos funciones: setObject("Object",Val) y getObject("Object")/getObject("Object","NUMERIC"). Los valores asignados se conservan hasta el reinicio. No puede haber más de 32 objetos distintos; si se supera, setObject termina con error.
setLuaStatus(Status) - muestra el estado del script en la interfaz («Estado Lua» («Статус Lua») en la pestaña «Adicional» («Дополнительно»)). Por ejemplo, para seguir la ejecución del script:
i = getObject("cnt", "NUMERIC")
i = i + 1
setLuaStatus("Counter cnt = "..i)
setObject("cnt", i)
setObject("Object",Val) – memoriza en la memoria de Samovar el objeto Object con el valor Val.
getObject("Object") – obtiene el valor guardado previamente del objeto Object. Si no se guardó ningún objeto con ese nombre, se devuelve una cadena vacía. Si intenta usar una cadena vacía como número, tomará el valor nil, indefinido. Para facilitar el trabajo con valores numéricos y evitar comprobar nil, puede llamar a esta función con el parámetro adicional "NUMERIC"; en ese caso, si el objeto aún no se ha inicializado, se devuelve 0.
Un ejemplo de trabajo con setObject/getObject:
n = 156
setObject("MyObject", n)
print (n)
n = n + 5
print (n)
n = getObject("MyObject")
print (n)
Si ejecuta este script, imprimirá en el monitor del puerto de Arduino
156
161
156
 
http_request(Url) – realiza una petición GET a la dirección Url y devuelve el cuerpo de la respuesta (o la cadena "error"). Por ejemplo, puede enviar lecturas a su propio servicio web. También puede enviar una petición POST pasando cuatro parámetros: la dirección, el método, la cabecera Content-Type y el cuerpo:
http_request("http://test.com:80/post?foo1=bar1", "POST", "Content-Type: text/text; charset=utf-8", "body")
Un ejemplo de script que envía la temperatura de la caldera a un servicio web externo (sustituya la dirección y los parámetros por los suyos):
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" -- your service access key
  http_request("http://example.com/notify?key=" .. key .. "&text=" .. urlencode(text))
end
 
local text = "Current boiler temperature = ".. getNumVariable("TankTemp")
SendToServer(text)
 
Las funciones de la placa I2CStepper (agitador, bomba dosificadora, relé) – 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 – se describen en la página I2CStepper.
 
Variables
La lista de variables internas disponibles por defecto en un script (sus valores se rellenan antes de cada ejecución del script; cambiarlos por asignación no sirve de nada):
 
Variables disponibles mediante getNumVariable (solo lectura): WFpulseCount, pump_started, valve_status, SamSetup_Mode, Samovar_Mode, Samovar_CR_Mode (el modo actual y el modo al que se cambia, números como en 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 (el contenido de alcohol en la caldera según la temperatura), alcohol_s (el contenido de alcohol según el vapor), water_pump_speed (la velocidad de la bomba de agua 0–1023), pressure_value (la presión en la caldera), YY, MM, DD, HH, MI, SS (año, mes, día, horas, minutos, segundos).
Variables disponibles mediante getNumVariable y setNumVariable: acceleration_temp, boil_temp (la temperatura de ebullición memorizada), wp_count, test_num_val, loop_lua_fl, SetScriptOff, show_lua_script. Solo mediante setNumVariable: pmpKp, pmpKi, pmpKd, los coeficientes PID de la bomba de agua.
Variables de cadena (getStrVariable/setStrVariable): SamovarStatus – el texto de estado de la página principal (lectura y escritura), test_str_val (lectura y escritura), program_type (solo lectura), Msg – al leerla devuelve el siguiente mensaje del sistema no leído o una cadena vacía; al escribirla añade un mensaje.
 
Variables para controlar los scripts:
loop_lua_fl – valores 0 o 1. Si es 0, no se ejecuta el bucle de scripts de cada segundo; si es 1, se ejecuta.
SetScriptOff – valores 0 o 1. Si es 1, se detiene el bucle de cada segundo (pone a cero loop_lua_fl). Samovar lo pone a 1 por sí mismo al final de un programa.
show_lua_script – valores 0 o 1. Si es 0, no se muestra el script en ejecución en la consola ni en el monitor del puerto; si es 1, se muestra. Se puede usar para depurar. También mostrará los valores de todas las variables establecidas para este script.
Por ejemplo, si llama a estas dos funciones en el script init.lua, cada segundo se ejecutarán dos scripts (script.lua y el que determina el modo actual de Samovar) y cada script se imprimirá en el monitor del puerto y en la consola del navegador
setNumVariable("loop_lua_fl",1)
setNumVariable("show_lua_script",1)