Saltar al contenido
Inicio » Artículos » Compatibilidad con scripts Lua

Compatibilidad con scripts Lua

El Samovar admite la automatización en el lenguaje de scripts Lua. Los scripts controlan equipos externos (válvulas, bombas, un agitador, calentadores, relés), leen los valores de los sensores y modifican la lógica del proceso para adaptarla a una instalación concreta. La funcionalidad básica del controlador no cambia.

El editor de scripts está integrado en la interfaz web: Ajustes → Editor. Admite el resaltado de sintaxis para Lua, JS, CSS, HTML y JSON, la comprobación del equilibrio de paréntesis y de bloques function … end, y el autocompletado con la tecla Tab.

Intérprete: Lua 5.4.4. La API básica de la biblioteca Arduino integrada en el ESP32 se amplía con funciones para acceder a los sensores, al calentamiento, a las bombas, al agitador y a los relés.

Cómo activarlo

De forma predeterminada, la compatibilidad con Lua está desactivada en el firmware. Para activarla, abra el archivo Samovar_ini.h en el directorio del firmware y sustituya la línea

//#define USE_LUA

por

#define USE_LUA

Después, vuelva a compilar el firmware y cárguelo en el controlador.

Arquitectura

Al arrancar el controlador se ejecuta el archivo init.lua. Su función es inicializar las variables que se usarán en otros scripts y activar o desactivar el ciclo de ejecución una vez por segundo (setNumVariable("loop_lua_fl", 1)).

Si el ciclo está activado, cada segundo se ejecutan dos scripts, uno tras otro:

  1. El script.lua común, que se ejecuta en todos los modos.
  2. El script del modo actual: beer.lua (cerveza), bk.lua (columna de mosto), nbk.lua (columna de mosto continua), dist.lua (destilación), rectificat.lua (rectificación), cheese.lua (elaboración de queso) o suvid.lua (sous vide).

Si un script no ha terminado cuando llega el siguiente lanzamiento, ese lanzamiento se omite: un script que ya se está ejecutando no se interrumpe.

En http://samovar.local/lua se puede ejecutar todos los scripts una sola vez.

Además de los scripts principales, se admiten botones adicionales de modo: archivos con el formato btn_<mode>_buttonN.lua, por ejemplo btn_rect_button1.lua. Se muestran en la interfaz web y permiten llamar a su propio código al pulsarlos.

Importante. Al actualizar el firmware, la interfaz web descarga del servidor las versiones de referencia de init.lua, script.lua, los archivos de los modos y btn_*.lua, pero solo si ese archivo aún no existe en la memoria del controlador. Los scripts del usuario no se sobrescriben. Aun así, conserve sus propias versiones en el ordenador: tras un restablecimiento de fábrica o una reinstalación de la interfaz web se perderán.

Equipos externos

A los scripts se pueden conectar expansores de puertos I²C:

  • PCF8575: un expansor con 16 puertos digitales (se define con la bandera USE_EXPANDER). A cada puerto se puede conectar cualquier actuador o sensor de tipo «botón». Todos los puertos están disponibles tanto para lectura como para escritura.
  • PCF8591: un expansor de puertos analógicos (se define con la bandera USE_ANALOG_EXPANDER). Se usa para sensores analógicos, por ejemplo un electrodo de pH en el modo de elaboración de queso.

Hay muchas formas de trabajar con los expansores: puede construir su propio sensor de seguridad que apague el Samovar desde un script cuando se active, o controlar una resistencia calefactora adicional según la temperatura del vapor. La lógica concreta depende de su equipo.

Funciones

GPIO y compatibles con Arduino

pinMode(pin, mode): modo de entrada/salida. Solo están disponibles los pines RELE_CHANNEL1, RELE_CHANNEL2, RELE_CHANNEL3, RELE_CHANNEL4 y LUA_PIN. Los valores numéricos de los pines dependen del mapa de pines y se indican en la descripción de los GPIO.

digitalWrite(pin, value): pone HIGH (1) o LOW (0) en un pin.

digitalRead(pin): lee el valor actual de un pin (HIGH o LOW).

analogRead(): lee el valor analógico del pin LUA_PIN en el intervalo 0–4095. A este pin se conecta un electrodo de pH o un sensor de presión MPX5010DP, por lo que no aplique más de 3,3 V: destruiría el ESP32.

delay(ms): pausa en milisegundos.

millis(): milisegundos transcurridos desde el arranque del controlador.

Expansores de puertos

exp_pinMode(pin, mode), exp_digitalWrite(pin, value), exp_digitalRead(pin): igual que las funciones de GPIO, pero para los puertos del PCF8575 (0–15).

exp_analogRead(), exp_analogWrite(value): operaciones analógicas en el PCF8591. Solo están disponibles cuando USE_ANALOG_EXPANDER está activado.

Control del proceso

setPower(power): enciende (1) o apaga (0) el calentamiento.

setCurrentPower(value): establece la tensión en el regulador de potencia. Si el regulador se controla por potencia, el argumento establece la potencia. Solo está disponible con SAMOVAR_USE_POWER.

setBodyTemp(temp): establece la temperatura del corazón durante la rectificación. En los demás modos la función solo imprime un mensaje en la consola y en Blynk.

setMixer(value): enciende (1) o apaga (0) el agitador.

openValve(value): abre (1) o cierra (0) la válvula de suministro de agua.

setAlarm(): modo de emergencia: el controlador apaga el calentamiento, cierra la válvula y desconecta la bomba de agua.

setNextProgram(): pasa a la siguiente línea del programa (equivale al botón de la interfaz).

setPauseWithdrawal(value): pone en pausa (1) o reanuda (0) la extracción.

setCapacity(num): cambia el recipiente de extracción actual. Intervalo válido: 0…CAPACITY_NUM.

setLuaStatus(value): establece el estado del modo Lua. Si está ocupado, lanza el error «Lua_status busy».

setPumpPwm(pwm): control PWM de la salida de la bomba de agua (0…1023). Disponible con USE_WATER_PUMP.

setServoAngle(angle): establece el ángulo del servo (0…SERVO_ANGLE). Disponible con SERVO_PIN.

setTimer(num, sec): programa el temporizador num (1…10) durante sec segundos. Hay 10 temporizadores en total.

getTimer(num): el tiempo que queda en el temporizador, en milisegundos. Si el temporizador no está programado o ya ha vencido, devuelve 0.

getState(): el estado numérico del controlador.

Variables del controlador

getNumVariable(name) y setNumVariable(name, value): lectura y escritura de variables numéricas. Solo se pueden escribir las variables marcadas expresamente como modificables (véase la tabla más abajo). Ejemplo: setNumVariable("TankTemp", 85) fijará la temperatura de la caldera en 85 °C. Sin entender la lógica del Samovar, modificar variables es arriesgado: se pueden romper los algoritmos.

getStrVariable(name) y setStrVariable(name, value): lo mismo para las variables de cadena.

Los nombres de las variables disponibles desde un script se indican en la tabla de la sección siguiente.

Objetos: almacenamiento persistente

Las variables normales de Lua solo existen mientras el script se ejecuta y se pierden en el siguiente lanzamiento. Para pasar un valor de un lanzamiento a otro, use setObject y getObject:

setObject("name", value)     -- save
getObject("name")            -- read (returns "" if absent)
getObject("name", "NUMERIC") -- read as a number (returns 0 if absent)

Como máximo 32 claves (LUA_OBJECT_STORE_MAX_KEYS). Los valores se conservan hasta que se reinicia el controlador.

Registro, depuración, HTTP

sendMsg(msg, level): muestra un mensaje. Con level = -1, en el puerto COM y en la consola del navegador (para depuración). Con level = 0, 1, 2, en la consola y en Blynk.

http_request(url [, method, headers, body]): una petición HTTP. Resulta útil para la integración con servicios externos: enviar notificaciones de Telegram, registrar datos, webhooks. El tiempo de espera es menor que en una descarga normal y la llamada se ejecuta bajo un mutex compartido, de modo que las llamadas simultáneas desde distintos scripts no entran en conflicto.

Dispositivos I²C

check_I2C_device(address): comprueba si hay un dispositivo en el bus I²C en la dirección indicada. Devuelve 1 si el dispositivo respondió y 0 en caso contrario.

get_i2c_rele_state(relay) y set_i2c_rele_state(relay, state): estado y conmutación de un relé I²C. Números válidos: 1…4.

Módulo de motor paso a paso

set_stepper_by_time(speed, direction, seconds): hace funcionar el motor paso a paso a la velocidad indicada (0…65535) y en el sentido indicado (0 o 1) durante el tiempo especificado. Devuelve 1 si se realizó el arranque.

set_stepper_target(speed, direction, target): ejecuta el número de pasos indicado. Devuelve 1 si se realizó el arranque.

get_stepper_status(): el estado actual del módulo de motor paso a paso.

set_mixer_pump_target(target) y get_mixer_pump_status(): control de la bomba del agitador (0 o 1).

Bomba I²C

Disponible cuando use_I2C_dev = 2:

i2cpump_start(rate, ml)     -- start the pump at the given speed and volume
i2cpump_stop()              -- stop
i2cpump_get_speed()         -- current speed
i2cpump_get_target_ml()     -- target volume
i2cpump_get_remaining_ml()  -- remaining volume to pump
i2cpump_get_running()       -- 1 if running, otherwise 0

Los números incorrectos (NaN, Inf, ≤ 0) se ignoran en silencio, sin error.

Watchdog de corrutinas

En la versión para ESP32, el límite de llamadas C anidadas de Lua se reduce de 200 a 60 (LUAI_MAXCCALLS = 60), porque con la pila de 8 KB de la tarea do_lua_script el límite original es inalcanzable. Si su script usa coroutine.create / wrap / resume, no olvide llamar a armCoroutineWatchdog() en init.lua: de lo contrario, una corrutina agotará la pila antes de que actúe el límite.

Variables

A continuación se enumeran las variables disponibles desde un script. RO significa solo lectura; RW, que se puede cambiar mediante setNumVariable.

Lecturas de sensores (RO)

  • SteamTemp: la temperatura del sensor de vapor.
  • PipeTemp: la temperatura del sensor del tramo de columna.
  • WaterTemp: la temperatura del sensor de agua.
  • TankTemp: la temperatura del sensor de la caldera.
  • ACPTemp: la temperatura del sensor del TCA (tubo de venteo atmosférico).
  • pressure_value: la presión en el sistema.
  • alcohol: la graduación calculada en tiempo real.
  • alcohol_s: la graduación en estado estabilizado.

Estado y situaciones (RO)

  • PowerOn: 0/1, bandera de calentamiento encendido.
  • PauseOn: 0/1, bandera de pausa de la extracción.
  • pump_started: 0/1, la bomba de agua está funcionando.
  • valve_status: 0/1, la válvula de agua.
  • water_pump_speed: la velocidad actual de la bomba.
  • program_Wait: 0/1, el programa está en pausa.
  • SamSetup_Mode: el modo de funcionamiento del Samovar.
  • Samovar_Mode: el modo de proceso actual.
  • Samovar_CR_Mode: el modo actual según la clasificación interna.
  • target_power_volt: la tensión objetivo del regulador.
  • WFpulseCount, WFflowRate, WFtotalMilliLitres: lecturas del caudalímetro (si lo hay).

Los parámetros de la línea actual del programa (program_volume, program_speed, program_temp, program_power, program_time, program_capacity_num) y el recipiente de extracción actual (capacity_num) también son de solo lectura.

Parámetros que se pueden cambiar (RW)

  • acceleration_temp: la temperatura de la resistencia calefactora de aceleración.
  • boil_temp: la temperatura de ebullición.
  • loop_lua_fl: 0/1, desactiva o activa el lanzamiento del script una vez por segundo.
  • SetScriptOff: 0/1, apagado forzado de los scripts.
  • show_lua_script: 0/1, muestra el cuerpo del script y los valores de las variables en el puerto COM y en la consola (para depuración).
  • test_num_val: una variable numérica para depuración.
  • wp_count: el número de pulsos del caudalímetro (con USE_WATER_PUMP).

Variables de cadena

  • SamovarStatus (RW): el estado actual que se muestra en la interfaz.
  • program_type (RO): el tipo de programa en ejecución.
  • test_str_val (RW): una variable de cadena para depuración.
  • Msg (especial): la lectura avanza el cursor por el anillo de eventos; la escritura se hace con setStrVariable.

Sensores virtuales (SAMOVAR_LUA_SIMULATION)

Cuando la simulación está activada, están disponibles VirtualSteamTemp, VirtualPipeTemp, VirtualWaterTemp, VirtualTankTemp, VirtualACPTemp (todas RW). Se usan para depurar scripts sin hardware.

Reloj (RO)

YY, MM, DD, HH, MI, SS: el año, mes, día, hora, minuto y segundo actuales según el RTC del controlador.

Ejemplos

setObject y getObject

n = 156
setObject("MyObject", n)
print(n)            -- 156
n = n + 5
print(n)            -- 161
n = getObject("MyObject")
print(n)            -- 156 (read back the saved value)

Envío de la temperatura de la caldera a 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)

Sensor analógico de nivel y bomba de agua

El ejemplo lee un sensor analógico de nivel y enciende la bomba cuando el nivel entra en un intervalo dado. El estado de la bomba se conserva entre lanzamientos mediante 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

Seguridad y limitaciones

  • 10 temporizadores a la vez.
  • 32 claves en el almacén de objetos (LUA_OBJECT_STORE_MAX_KEYS).
  • El límite de llamadas C anidadas de Lua: 60 (LUAI_MAXCCALLS). Si usa coroutine.*, no olvide llamar a armCoroutineWatchdog().
  • Pines para pinMode: solo RELE_CHANNEL1…RELE_CHANNEL4 y LUA_PIN. El pin ocupado por el electrodo de pH del modo queso no se puede usar: la función devolverá un error.
  • Aplique como máximo 3,3 V a analogRead().
  • setBodyTemp funciona solo en el modo de rectificación; en los demás modos muestra una advertencia.
  • Al llamar a setAlarm(), el controlador detiene el calentamiento, cierra la válvula y apaga la bomba.
  • Los argumentos numéricos de i2cpump_start deben ser números positivos finitos; NaN/Inf/≤0 se ignoran en silencio.

Recursos adicionales

Deja una respuesta