Zum Inhalt springen
Startseite » Artikel » Unterstützung für Lua-Skripte

Unterstützung für Lua-Skripte

Der Samovar unterstützt die Automatisierung mit der Skriptsprache Lua. Mit Skripten steuern Sie externe Geräte (Ventile, Pumpen, einen Rührer, Heizungen, Relais), lesen Sensorwerte aus und passen die Prozesslogik an Ihren Aufbau an. Die Grundfunktionen des Controllers bleiben dabei unverändert.

Der Skript-Editor ist in die Weboberfläche eingebaut: Settings → Editor. Er bietet Syntaxhervorhebung für Lua, JS, CSS, HTML und JSON, prüft die Klammern und die Paarung der Blöcke function … end und vervollständigt Code mit der Taste Tab.

Interpreter: Lua 5.4.4. Die Basis-API aus der in den ESP32 eingebauten Arduino-Bibliothek ist um Funktionen für den Zugriff auf Sensoren, Heizung, Pumpen, Rührer und Relais erweitert.

So aktivieren Sie Lua

In der Firmware ist die Lua-Unterstützung standardmäßig abgeschaltet. Um sie zu aktivieren, öffnen Sie im Firmware-Verzeichnis die Datei Samovar_ini.h und ersetzen Sie die Zeile

//#define USE_LUA

durch

#define USE_LUA

Danach bauen Sie die Firmware neu und laden sie auf den Controller hoch.

Aufbau

Beim Start des Controllers wird die Datei init.lua ausgeführt. Sie initialisiert die Variablen, die in den anderen Skripten gebraucht werden, und schaltet die Ausführungsschleife im Sekundentakt ein oder aus (setNumVariable("loop_lua_fl", 1)).

Ist die Schleife eingeschaltet, werden jede Sekunde zwei Skripte nacheinander ausgeführt:

  1. Das gemeinsame script.lua — läuft in allen Modi.
  2. Das Skript des aktuellen Modus: beer.lua (Bier), bk.lua (Maischekolonne), nbk.lua (kontinuierliche Maischekolonne), dist.lua (Destillation), rectificat.lua (Rektifikation), cheese.lua (Käseherstellung) oder suvid.lua (Sous-vide).

Ist ein Skript beim nächsten Start noch nicht fertig, wird dieser Start übersprungen: Ein bereits laufendes Skript wird nicht unterbrochen.

Ein einmaliger Lauf aller Skripte ist unter http://samovar.local/lua möglich.

Neben den Hauptskripten werden zusätzliche Modus-Schaltflächen unterstützt — Dateien der Form btn_<mode>_buttonN.lua, zum Beispiel btn_rect_button1.lua. Sie erscheinen in der Weboberfläche und führen beim Drücken Ihren eigenen Code aus.

Wichtig. Bei einem Firmware-Update lädt die Weboberfläche die Referenzversionen von init.lua, script.lua, den Moduldateien und btn_*.lua vom Server herunter, aber nur, wenn eine solche Datei im Speicher des Controllers noch nicht existiert. Eigene Skripte werden nicht überschrieben. Bewahren Sie Ihre Versionen trotzdem auf Ihrem Computer auf — nach einem Zurücksetzen auf Werkseinstellungen oder einer Neuinstallation der Weboberfläche gehen sie verloren.

Externe Geräte

An die Skripte lassen sich I²C-Port-Expander anschließen:

  • PCF8575 — ein Expander mit 16 digitalen Ports (über das Flag USE_EXPANDER festgelegt). An jeden Port lässt sich ein beliebiger Aktor oder ein Sensor vom Typ „Taster“ anschließen. Alle Ports stehen zum Lesen und zum Schreiben zur Verfügung.
  • PCF8591 — ein Expander für analoge Ports (über das Flag USE_ANALOG_EXPANDER festgelegt). Er dient für analoge Sensoren — zum Beispiel für eine pH-Elektrode im Käsemodus.

Mit Expandern lässt sich vieles machen: Sie können einen eigenen Sicherheitssensor bauen, der den Samovar per Skript abschaltet, sobald er auslöst, oder einen zusätzlichen Heizstab abhängig von der Dampftemperatur steuern. Die konkrete Logik hängt von Ihrer Ausrüstung ab.

Funktionen

GPIO und Arduino-kompatible Funktionen

pinMode(pin, mode) — Eingangs-/Ausgangsmodus. Verfügbar sind nur die Pins RELE_CHANNEL1, RELE_CHANNEL2, RELE_CHANNEL3, RELE_CHANNEL4 und LUA_PIN. Die numerischen Pin-Werte hängen von der Pin-Belegung ab und stehen in der GPIO-Beschreibung.

digitalWrite(pin, value) — gibt HIGH (1) oder LOW (0) an einem Pin aus.

digitalRead(pin) — liest den aktuellen Wert eines Pins (HIGH oder LOW).

analogRead() — liest den Analogwert des Pins LUA_PIN im Bereich 0–4095. An diesem Pin hängt eine pH-Elektrode oder ein Drucksensor MPX5010DP, legen Sie daher nie mehr als 3,3 V an: Das zerstört den ESP32.

delay(ms) — eine Pause in Millisekunden.

millis() — Millisekunden seit dem Start des Controllers.

Port-Expander

exp_pinMode(pin, mode), exp_digitalWrite(pin, value), exp_digitalRead(pin) — dasselbe wie die GPIO-Funktionen, nur für die Ports des PCF8575 (0–15).

exp_analogRead(), exp_analogWrite(value) — analoge Operationen am PCF8591. Nur verfügbar, wenn USE_ANALOG_EXPANDER aktiviert ist.

Prozesssteuerung

setPower(power) — schaltet die Heizung ein (1) oder aus (0).

setCurrentPower(value) — stellt die Spannung am Leistungsregler ein. Wird der Regler über die Leistung gesteuert, legt das Argument die Leistung fest. Nur mit SAMOVAR_USE_POWER verfügbar.

setBodyTemp(temp) — stellt die Temperatur des Mittellaufs (Herzstück) bei der Rektifikation ein. In anderen Modi gibt die Funktion nur eine Meldung in der Konsole und in Blynk aus.

setMixer(value) — schaltet den Rührer ein (1) oder aus (0).

openValve(value) — öffnet (1) oder schließt (0) das Wasserzulaufventil.

setAlarm() — Notfallmodus: Der Controller schaltet die Heizung ab, schließt das Ventil und schaltet die Wasserpumpe aus.

setNextProgram() — wechselt zur nächsten Programmzeile (entspricht der Schaltfläche in der Oberfläche).

setPauseWithdrawal(value) — hält die Abnahme an (1) oder setzt sie fort (0).

setCapacity(num) — wechselt den aktuellen Auffangbehälter. Gültiger Bereich: 0…CAPACITY_NUM.

setLuaStatus(value) — setzt den Status des Lua-Modus. Ist er belegt, wird der Fehler „Lua_status busy“ ausgelöst.

setPumpPwm(pwm) — PWM-Steuerung des Ausgangs der Wasserpumpe (0…1023). Verfügbar mit USE_WATER_PUMP.

setServoAngle(angle) — stellt den Servowinkel ein (0…SERVO_ANGLE). Verfügbar mit SERVO_PIN.

setTimer(num, sec) — stellt den Timer num (1…10) auf sec Sekunden. Insgesamt gibt es 10 Timer.

getTimer(num) — die Restzeit des Timers in Millisekunden. Ist der Timer nicht gestellt oder bereits abgelaufen, wird 0 zurückgegeben.

getState() — der numerische Status des Controllers.

Variablen des Controllers

getNumVariable(name) und setNumVariable(name, value) — Lesen und Schreiben numerischer Variablen. Schreiben ist nur bei Variablen möglich, die ausdrücklich als beschreibbar gekennzeichnet sind (siehe die Tabelle unten). Beispiel: setNumVariable("TankTemp", 85) setzt die Temperatur der Brennblase auf 85 °C. Ohne Verständnis der Samovar-Logik ist das Ändern von Variablen riskant — es kann die Algorithmen durcheinanderbringen.

getStrVariable(name) und setStrVariable(name, value) — dasselbe für Textvariablen.

Die Namen der aus einem Skript verfügbaren Variablen stehen in der Tabelle im nächsten Abschnitt.

Objekte — dauerhafter Speicher

Gewöhnliche Lua-Variablen leben nur, solange das Skript läuft, und gehen beim nächsten Start verloren. Um einen Wert zwischen Starts weiterzugeben, nutzen Sie setObject und getObject:

setObject("name", value)     -- speichern
getObject("name")            -- lesen (gibt "" zurück, wenn nicht vorhanden)
getObject("name", "NUMERIC") -- als Zahl lesen (gibt 0 zurück, wenn nicht vorhanden)

Höchstens 32 Schlüssel (LUA_OBJECT_STORE_MAX_KEYS). Die Werte bleiben bis zum Neustart des Controllers erhalten.

Protokoll, Fehlersuche, HTTP

sendMsg(msg, level) — gibt eine Meldung aus. Bei level = -1 — an den COM-Port und die Browser-Konsole (zur Fehlersuche). Bei level = 0, 1, 2 — an die Konsole und an Blynk.

http_request(url [, method, headers, body]) — eine HTTP-Anfrage. Praktisch für die Anbindung externer Dienste: Telegram-Benachrichtigungen, Protokollierung, Webhooks. Das Timeout ist kürzer als bei einem normalen Download, und der Aufruf läuft unter einem gemeinsamen Mutex, sodass gleichzeitige Aufrufe aus verschiedenen Skripten einander nicht stören.

I²C-Geräte

check_I2C_device(address) — prüft, ob unter der angegebenen Adresse ein Gerät am I²C-Bus vorhanden ist. Gibt 1 zurück, wenn das Gerät geantwortet hat, sonst 0.

get_i2c_rele_state(relay) und set_i2c_rele_state(relay, state) — Zustand und Schalten eines I²C-Relais. Gültige Nummern: 1…4.

Schrittmotor-Modul

set_stepper_by_time(speed, direction, seconds) — lässt den Schrittmotor mit der angegebenen Geschwindigkeit (0…65535) und Richtung (0 oder 1) für die angegebene Zeit laufen. Gibt 1 zurück, wenn der Start ausgeführt wurde.

set_stepper_target(speed, direction, target) — führt die angegebene Anzahl von Schritten aus. Gibt 1 zurück, wenn der Start ausgeführt wurde.

get_stepper_status() — der aktuelle Zustand des Schrittmotor-Moduls.

set_mixer_pump_target(target) und get_mixer_pump_status() — Steuerung der Rührerpumpe (0 oder 1).

I²C-Pumpe

Verfügbar bei use_I2C_dev = 2:

i2cpump_start(rate, ml)     -- Pumpe mit der angegebenen Geschwindigkeit und Menge starten
i2cpump_stop()              -- stoppen
i2cpump_get_speed()         -- aktuelle Geschwindigkeit
i2cpump_get_target_ml()     -- Zielmenge
i2cpump_get_remaining_ml()  -- noch zu fördernde Restmenge
i2cpump_get_running()       -- 1, wenn sie läuft, sonst 0

Ungültige Zahlen (NaN, Inf, ≤ 0) werden stillschweigend ignoriert, ohne Fehlermeldung.

Coroutine-Watchdog

In der ESP32-Version ist die Grenze für verschachtelte C-Aufrufe in Lua von 200 auf 60 gesenkt (LUAI_MAXCCALLS = 60), weil das ursprüngliche Limit auf dem 8-KB-Stack der Task do_lua_script nicht erreichbar ist. Wenn Ihr Skript coroutine.create / wrap / resume verwendet, rufen Sie unbedingt armCoroutineWatchdog() in init.lua auf: Sonst frisst eine Coroutine den Stack auf, bevor die Grenze greift.

Variablen

Die folgende Aufstellung zeigt die aus einem Skript verfügbaren Variablen. RO bedeutet „nur lesen“, RW bedeutet, dass sich die Variable über setNumVariable ändern lässt.

Sensorwerte (RO)

  • SteamTemp — die Temperatur des Dampfsensors.
  • PipeTemp — die Temperatur des Sensors am Kolonnenschuss.
  • WaterTemp — die Temperatur des Wassersensors.
  • TankTemp — die Temperatur des Brennblasen-Sensors.
  • ACPTemp — die Temperatur des TCA-Sensors (Atmosphärenentlüftungsrohr).
  • pressure_value — der Druck im System.
  • alcohol — der berechnete Alkoholgehalt in Echtzeit.
  • alcohol_s — der Alkoholgehalt im stabilisierten Zustand.

Zustand und Status (RO)

  • PowerOn — 0/1, das Flag „Heizung ein“.
  • PauseOn — 0/1, das Flag „Abnahme pausiert“.
  • pump_started — 0/1, die Wasserpumpe läuft.
  • valve_status — 0/1, das Wasserventil.
  • water_pump_speed — die aktuelle Pumpengeschwindigkeit.
  • program_Wait — 0/1, das Programm ist angehalten.
  • SamSetup_Mode — der Betriebsmodus des Samovar.
  • Samovar_Mode — der aktuelle Prozessmodus.
  • Samovar_CR_Mode — der aktuelle Modus nach der internen Klassifizierung.
  • target_power_volt — die Zielspannung des Reglers.
  • WFpulseCount, WFflowRate, WFtotalMilliLitres — Werte des Durchflusszählers (falls vorhanden).

Die Parameter der aktuellen Programmzeile (program_volume, program_speed, program_temp, program_power, program_time, program_capacity_num) und des aktuellen Auffangbehälters (capacity_num) sind ebenfalls nur lesbar.

Änderbare Parameter (RW)

  • acceleration_temp — die Temperatur des Beschleunigungs-Heizstabs.
  • boil_temp — die Siedetemperatur.
  • loop_lua_fl — 0/1, schaltet den Skriptstart im Sekundentakt aus oder ein.
  • SetScriptOff — 0/1, erzwungenes Abschalten der Skripte.
  • show_lua_script — 0/1, gibt den Skripttext und die Variablenwerte an den COM-Port und die Konsole aus (zur Fehlersuche).
  • test_num_val — eine numerische Variable zur Fehlersuche.
  • wp_count — die Anzahl der Impulse des Durchflusszählers (mit USE_WATER_PUMP).

Textvariablen

  • SamovarStatus (RW) — der aktuelle Status, der in der Oberfläche angezeigt wird.
  • program_type (RO) — der Typ des laufenden Programms.
  • test_str_val (RW) — eine Textvariable zur Fehlersuche.
  • Msg (Sonderfall) — Lesen schiebt den Zeiger im Ereignisring weiter; geschrieben wird über setStrVariable.

Virtuelle Sensoren (SAMOVAR_LUA_SIMULATION)

Bei aktivierter Simulation stehen VirtualSteamTemp, VirtualPipeTemp, VirtualWaterTemp, VirtualTankTemp, VirtualACPTemp zur Verfügung (alle RW). Mit ihnen lassen sich Skripte ohne Hardware testen.

Uhr (RO)

YY, MM, DD, HH, MI, SS — das aktuelle Jahr, der Monat, der Tag, die Stunde, die Minute und die Sekunde laut RTC des Controllers.

Beispiele

setObject und getObject

n = 156
setObject("MyObject", n)
print(n)            -- 156
n = n + 5
print(n)            -- 161
n = getObject("MyObject")
print(n)            -- 156 (der gespeicherte Wert wurde zurückgelesen)

Die Temperatur der Brennblase an Telegram senden

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 = "Aktuelle Temperatur der Brennblase = "
  .. getNumVariable("TankTemp")
SendTelegram(text)

Analoger Füllstandssensor und Wasserpumpe

Das Beispiel liest einen analogen Füllstandssensor aus und schaltet die Pumpe ein, wenn der Füllstand in einen bestimmten Bereich fällt. Der Zustand der Pumpe wird über setObject zwischen den Starts gespeichert.

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

Sicherheit und Einschränkungen

  • 10 Timer gleichzeitig.
  • 32 Schlüssel im Objektspeicher (LUA_OBJECT_STORE_MAX_KEYS).
  • Die Grenze für verschachtelte C-Aufrufe in Lua: 60 (LUAI_MAXCCALLS). Wenn Sie coroutine.* verwenden, rufen Sie unbedingt armCoroutineWatchdog() auf.
  • Pins für pinMode: nur RELE_CHANNEL1…RELE_CHANNEL4 und LUA_PIN. Der Pin, den die pH-Elektrode im Käsemodus belegt, darf nicht verwendet werden — die Funktion gibt einen Fehler zurück.
  • Legen Sie an analogRead() höchstens 3,3 V an.
  • setBodyTemp funktioniert nur im Rektifikationsmodus; in anderen Modi gibt die Funktion eine Warnung aus.
  • Beim Aufruf von setAlarm() stoppt der Controller die Heizung, schließt das Ventil und schaltet die Pumpe aus.
  • Die numerischen Argumente von i2cpump_start müssen endliche positive Zahlen sein; NaN/Inf/≤0 werden stillschweigend ignoriert.

Weitere Quellen

Schreibe einen Kommentar