PDF herunterladen
        SAMOVAR 
×
Menü

Über die Skriptsprache Lua.

 
Samovar unterstützt Automatisierung auf Basis der Skript-Programmiersprache Lua. Die Lua-Unterstützung wird beim Erstellen der Firmware aktiviert: im Konfigurator mit dem Kontrollkästchen „Lua verwenden“ («Использовать Lua») (Abschnitt „Hardware“ («Оборудование»)), in PlatformIO mit der Samovar_lua-Umgebung, beim manuellen Erstellen mit der Zeile #define USE_LUA in der Datei user_config_override.h. Der Skripteditor wird mit der Schaltfläche „Editor“ («Редактор») auf der Seite „Einstellungen“ geöffnet (Adresse http://samovar.local/edit). Benutzername und Passwort werden nicht abgefragt.
Die aktuelle Lua-Version ist 5.4.4
 
Beim Start von Samovar wird die Datei init.lua ausgeführt. Sie dient dazu, die sekündliche Skriptschleife ein- oder auszuschalten und Variablen zu initialisieren, die in anderen Skripten verwendet werden. Im Auslieferungszustand schaltet init.lua die Schleife aus: setNumVariable("loop_lua_fl",0).
Ist die Ausführung der Skripte in einer Schleife aktiviert, laufen jede Sekunde zwei Skripte: zuerst script.lua, dann das Skript, dessen Name vom Betriebsmodus abhängt: rectificat.lua (Rektifikation), dist.lua (Destillation), beer.lua (Bier), bk.lua (Maischekolonne), nbk.lua (NBK), suvid.lua (Sous-vide), cheese.lua (Käseherstellung). Im „Lua-Modus“ («Lua-режим») (ein Eintrag in der Modusliste der Einstellungen, nur in einer Firmware mit Lua sichtbar) gibt es kein Modusskript – die gesamte Moduslogik steht in script.lua. Ein Skriptpaar lässt sich auch einmalig über http://samovar.local/lua starten, eine einzelne Datei über http://samovar.local/lua?script=Dateiname.
Die Skripte laufen nacheinander in einer einzigen Task: Während eines läuft, wartet das andere. Ein einzelner Skriptlauf darf nicht länger als 20 Sekunden dauern – danach wird er mit der Meldung „Lua: Ausführung des Chunks wegen Zeitüberschreitung abgebrochen“ («Lua: выполнение чанка прервано по таймауту») abgebrochen. Endet ein Modusskript 5-mal hintereinander mit einem Fehler, stoppt die sekündliche Schleife (loop_lua_fl wird auf 0 gesetzt) und eine zusammenfassende Meldung erscheint in der Konsole; script.lua wird nach 5 Fehlern in Folge nicht mehr ausgeführt, bis die Skripte neu geladen werden, ohne dass die Schleife stoppt. Skriptfehler werden in die Browserkonsole und in den Portmonitor geschrieben.
 
Sie können der Oberfläche auch Schaltflächen zum Ausführen von Dateien hinzufügen. Dateiname: btn_<mode>_button<N>.lua, wobei mode rect, dist, beer, bk, nbk, suvid oder cheese ist und N die Nummer der Schaltfläche. Zum Beispiel btn_rect_button1.lua, btn_beer_button2.lua. Die Schaltflächen erscheinen auf der Registerkarte „Zusätzlich“ («Дополнительно») der Hauptseite des Modus, für den sie erstellt wurden, im Lua-Block; dort gibt es außerdem das Feld „Lua:“ («Lua:») mit der Schaltfläche „Lua ausführen“ («Выполнить Lua») zum Ausführen von ein oder zwei Codezeilen sowie „Lua-Status“ («Статус Lua») – eine Zeile, die das Skript mit der Funktion setLuaStatus setzt.
Der Name der Schaltfläche wird in der ersten Zeile der Datei durch einen Kommentar der Form --|Name^ festgelegt (zum Beispiel --|Start^). Fehlt diese Zeile, heißen die Schaltflächen LUA1, LUA2 usw. in der Reihenfolge der gefundenen Dateien.
Im Auslieferungszustand hat jeder Modus zwei Schaltflächen: „Starten“ («Начать») (btn_*_button1.lua – schaltet die sekündliche Schleife ein) und „Stoppen“ («Остановить») (btn_*_button2.lua – schaltet sie aus).
Hauptzweck der Schaltflächen ist es, die Ausführung von Skripten in der Schleife anzustoßen (zum Beispiel durch Setzen eines Variablenwerts, der dann im Schleifenskript gelesen werden kann) oder ein Stellglied (zum Beispiel eine Pumpe) ein- oder auszuschalten.
 
Die Architektur mit Schaltflächen sieht also so aus (als Beispiel für den Destillationsmodus):
  1. Die gesteuerte Hauptschleife: einmal pro Sekunde script.lua => dist.lua => script.lua => dist.lua =>... (gesteuert in dem Sinne, dass sie aus jedem Skript über eine globale Variable programmatisch gestartet und gestoppt werden kann)
  2. Das Skript script.lua ist für alle Modi gemeinsam; hier können Sie Code unterbringen, der in jedem Modus laufen muss (Rektifikation, Bier, Sous-vide usw.)
  3. Das Skript dist.lua wird nur im Destillationsmodus verwendet; darin können Sie die gesamte Logik unterbringen, die einmal pro Sekunde laufen muss, abhängig von Parametern, die das Skript aus den globalen Samovar-Variablen erhält, zum Beispiel getNumVariable("TankTemp"), oder aus Variablen, die ein beliebiges Skript im gemeinsamen Namensraum gespeichert hat, zum Beispiel getObject("StartTankFilling"). Im Code dieses Skripts können Sie also zum Beispiel folgende Bedingung unterbringen:
    if (StartTankFilling == "true" and TankFillingPercent < 80) then StartPump() else StopPump() end
    und jede Sekunde prüft das Skript, ob das Kennzeichen zum Befüllen der Brennblase gesetzt ist, und falls ja, ob sie schon zu 80 % gefüllt ist und die zuvor gestartete Pumpe jetzt gestoppt werden soll.
  4. Das Schaltflächenskript btn_dist_button1.lua setzt beim Drücken der Schaltfläche über setObject("StartTankFilling", "true") das Kennzeichen, dass die Brennblase befüllt werden muss. Im nächsten Zyklus eine Sekunde später liest das Skript dist.lua dieses Kennzeichen und schaltet die Pumpe ein (falls die Brennblase noch nicht gefüllt ist). So teilen sich alle Skripte einen gemeinsamen Namensraum, der über setObject/getObject erreichbar ist und den Datenaustausch zwischen ihnen ermöglicht.
  5. Das Schaltflächenskript btn_dist_button2.lua kann beim Drücken der Schaltfläche die gesteuerte Hauptschleife über setNumVariable("loop_lua_fl", 0) stoppen, zum Beispiel wenn mit den Skripten etwas schiefläuft.
 
Programmzeile vom Typ L (Lua-Stufe). In den Programmen aller Modi außer NBK (Rektifikation, Destillation, Maischekolonne, Bier, Käseherstellung) gibt es eine Zeile vom Typ L – eine von Lua gesteuerte Stufe. Wählen Sie diesen Typ im Programmeditor, öffnet sich das Fenster „Lua-Stufe“ («Lua-этап»), in dem Sie Folgendes festlegen:
Erreicht das Programm eine L-Zeile, führt Samovar die gewählte Datei aus und wartet auf einen Aufruf von setNextProgram() – das ist der Übergang zur nächsten Zeile. Ein Skriptfehler („Lua wurde mit einem Fehler beendet“ («Lua завершилась с ошибкой»)) oder eine Zeitüberschreitung stoppt den Modus. Die übrigen Felder der Zeile (Temperatur, Geschwindigkeit, Behälter, Gerät) werden für den Typ L nicht gesetzt. In Textform wird eine solche Zeile als Datei und Parameter, getrennt durch das Zeichen ^, gespeichert, zum Beispiel L;120;hops.lua^3^45;0;0;0 für die Rektifikation.
Ein Beispiel für das Skript dose.lua, das das Relais mit der Nummer aus dem ersten Parameter für die Zeit aus dem zweiten einschaltet (im Programm: Datei dose.lua, Parameter 3 und 1500):
set_i2c_rele_state(tonumber(arg[1]), 1)
delay(tonumber(arg[2]))
set_i2c_rele_state(tonumber(arg[1]), 0)
setNextProgram()
Laden Sie während des Betriebs über den Dateieditor eine neue Version der Datei hoch, die gerade von einer L-Zeile ausgeführt wird, liest Samovar das Skript neu ein, ohne das Programm neu zu starten.
 
Samovar-Funktionen
Aus Skripten heraus können Sie auf die internen Variablen von Samovar zugreifen (einige davon lassen sich nicht ändern, sie sind schreibgeschützt) und Samovar-Funktionen aufrufen. Während ein Moduswechsel läuft, enden Funktionen, die den Zustand von Samovar ändern, mit dem Fehler "mode switch blocks state changes".
Die Pin-Nummern hängen von der Platine ab. Hier und im Folgenden übergeben Sie deren Zahlenwert:
In Skripten sind die Konstanten INPUT, OUTPUT, LOW, HIGH sowie die Ergebniscodes von Befehlen ACTUATOR_COMMAND_ACCEPTED, ACTUATOR_COMMAND_PENDING, ACTUATOR_COMMAND_APPLIED, ACTUATOR_COMMAND_FAILED definiert.
 
pinMode(pin, mode) – wie die gleichnamige Arduino-Funktion, legt die Betriebsart des angegebenen Ein-/Ausgangs (Pin) als Eingang oder Ausgang fest. Die Betriebsart lässt sich nur für die Ports RELE_CHANNEL1–RELE_CHANNEL4 und LUA_PIN ändern. Wenn Sie also den Port RELE_CHANNEL4 der DEVKIT-Platine als Eingang festlegen möchten, rufen Sie pinMode(13, INPUT) auf, als Ausgang pinMode(13, OUTPUT).
digitalWrite(pin, Value) – wie die gleichnamige Arduino-Funktion, legt an einen digitalen Ausgang (Pin) den Wert HIGH oder LOW an. Verfügbar sind die Ports RELE_CHANNEL1–RELE_CHANNEL4, WATER_PUMP_PIN, LUA_PIN, ALARM_BTN_PIN und BTN_PIN, die übrigen werden ignoriert. Wenn Sie also am Ausgang RELE_CHANNEL4 einen hohen Pegel setzen möchten, rufen Sie digitalWrite(13, 1) auf. Bei WATER_PUMP_PIN mit einer PWM-Pumpe schaltet der Wert 1 die Pumpe mit voller Drehzahl ein, 0 stoppt sie. Hat der Notfallschutz der Heizung ausgelöst, wird auf die Heizkanäle (RELE_CHANNEL1, RELE_CHANNEL4) nicht geschrieben.
digitalRead(pin) – wie die gleichnamige Arduino-Funktion, liest den Wert vom angegebenen Eingang – HIGH oder LOW. Wenn Sie also den am Ein- oder Ausgang RELE_CHANNEL4 anliegenden Wert lesen möchten, rufen Sie digitalRead(13) auf
analogRead() – wie die gleichnamige Arduino-Funktion, liest den Wert vom Analogeingang LUA_PIN (ohne Parameter). Die am Analogeingang anliegende Spannung wird in einen Wert von 0 bis 4095 umgewandelt. ACHTUNG! Eine Spannung über 3,3 Volt am Eingang kann den Port oder den gesamten ESP32 beschädigen. Im Modus Käseherstellung ist LUA_PIN durch den pH-Sensor belegt: pinMode und digitalWrite enden dafür mit einem Fehler.
exp_pinMode(pin, mode) – wie pinMode(pin, mode), aber zur Steuerung des Portexpanders PCF8575; pin ist die Portnummer des Expanders (0–15), mode ist INPUT, OUTPUT oder INPUT_PULLUP. Alle Ports des Expanders können wahlweise zum Lesen oder zur Ausgabe verwendet werden
exp_digitalWrite(pin, Value) – wie digitalWrite(pin, Value), aber zur Steuerung des Portexpanders PCF8575; pin ist die Portnummer des Expanders (0–15), Value ist 1 oder 0. Alle Ports des Expanders sind zum Schreiben verfügbar
exp_digitalRead(pin) – wie digitalRead(pin), aber zur Steuerung des Portexpanders PCF8575; pin ist die Portnummer des Expanders (0–15). Alle Ports des Expanders sind zum Lesen verfügbar
exp_analogWrite(Value) – wie analogWrite(pin, Value), aber zur Steuerung des Portexpanders PCF8591, Value ist ein Wert von 0 bis 255. Der Expander hat einen Port für die Ausgabe.
exp_analogRead(pin) – wie analogRead(), aber zur Steuerung des Portexpanders PCF8591; pin ist die Eingangsnummer des Expanders (0–3), liefert einen Wert von 0 bis 255.
Die exp_*-Funktionen gibt es nur in einer Firmware, die mit USE_EXPANDER (PCF8575) und USE_ANALOG_EXPANDER (PCF8591) erstellt wurde.
delay(ms) – wie die gleichnamige Arduino-Funktion, hält die Programmausführung für die im Parameter angegebene Anzahl Millisekunden an (1000 Millisekunden entsprechen 1 Sekunde). Höchstens 1000.
millis() – wie die gleichnamige Arduino-Funktion, liefert die Anzahl der Millisekunden seit dem Start von Samovar
setTimer(Num, Sec) – setzt den Timer Nummer Num auf Sec Sekunden (bis 65535). Es gibt 10 Timer, von 1 bis 10. Skripte können also gleichzeitig mit höchstens 10 Timern arbeiten
getTimer(Num) – liefert die Restzeit des Timers Nummer Num in Sekunden. Ist der Timer nicht gesetzt oder abgelaufen, liefert die Funktion 0
sendMsg(Msg, Level) – Ist Level = -1, wird die Meldung Msg am COM-Port und in der Browserkonsole ausgegeben, praktisch zum Debuggen. Ist Level 0, 1 oder 2, wird die Meldung als Systemmeldung gesendet (0 – Alarm, 1 – Warnung, 2 – Hinweis): an das Display, die Weboberfläche, die Apps und die Website.
setPower(Power) – schaltet Samovar ein/aus. setPower(0) schaltet aus, setPower(1) schaltet ein (wie die Heizungsschaltfläche des aktuellen Modus)
setCurrentPower(Value) – setzt den Wert Value am Regler. Wird ein Regler mit Leistungsregelung (SEM/AVR) verwendet, ist Value die einzustellende Leistung in Watt, sonst die Spannung mit einer Genauigkeit von 0,1 V. Die Funktion gibt es nur in einer Firmware mit Leistungsregler.
setBodyTemp() – setzt die Mittellauftemperatur bei der Rektifikation. Funktioniert nur im Rektifikationsmodus. In anderen Modi gibt sie in der Konsole eine Meldung aus, dass die Mittellauftemperatur nicht gesetzt werden kann
setMixer(Val) – schaltet das Rührwerk ein/aus. setMixer(0) – aus, setMixer(1) – ein
openValve(Val) – öffnet/schließt das Wasserventil. openValve(0) – schließen, openValve(1) – öffnen
setPumpPwm(Val) – setzt die Drehzahl der Wasserpumpe, Val von 0 bis 1023. Gibt es nur in einer Firmware mit PWM-Pumpe (USE_WATER_PUMP).
setMixer, openValve, setCurrentPower und setPumpPwm liefern den Ergebniscode des Befehls (die Konstanten ACTUATOR_COMMAND_*).
setAlarm() – setzt den Alarmmodus. Samovar schaltet die Leistung ab, schließt das Wasserventil und schaltet die Wasserpumpe aus
setNextProgram() – wechselt zum nächsten Programm. Wie das Drücken der Schaltfläche „Nächstes Programm“ («Следующая программа») in der Oberfläche. Funktioniert nur bei eingeschalteter Heizung.
setPauseWithdrawal(Val) – pausiert die Abnahme bzw. setzt sie fort. setPauseWithdrawal(0) – fortsetzen, setPauseWithdrawal(1) – pausieren
setCapacity(Num) – dreht den Servo zum Behälter Nummer Num (0 – Ablauf, ab 1 – Behälter).
setServoAngle(Angle) – dreht den Servo auf Angle Grad (0–180). Liefert 1, wenn die Firmware einen Servoausgang (SERVO_PIN) hat, sonst 0. Wird für selbstgebaute Zusatzdosierer verwendet (siehe Zusatzdosierer); der Servo wird mit setCapacity geteilt, daher muss nach dem Drehen bei der Rektifikation der Behälter erneut gesetzt werden.
getState() – liefert den Status von Samovar (eine Zahl), praktisch, um den Zustand von Samovar zu bestimmen: 0 – Leerlauf; Rektifikation: 10 – Abnahme läuft, 15 – automatische Pause zwischen Programmzeilen, 20 – Programm abgeschlossen, 30 – Kalibrierung der Abnahmepumpe, 50 – Aufheizen, 51 – Stabilisierung, 52 – Stabilisierung abgeschlossen; 40 – manuelle Pause (in jedem Modus); 1000 – Destillation läuft, 2000 – Bier, 3000 – Maischekolonne, 4000 – NBK, 5000 – Käseherstellung.
setNumVariable("Variable", Val) – setzt eine interne numerische Variable von Samovar. Nicht alle Variablen lassen sich setzen (Liste unten). setNumVariable("loop_lua_fl", 1) schaltet die sekündliche Skriptschleife ein. Achtung! Wenn Sie die Funktionsweise von Samovar nicht verstehen, verwenden Sie diese Funktion besser nicht, da Sie den Betrieb von Samovar stören könnten
setStrVariable("Variable", Val) – setzt eine interne Zeichenkettenvariable von Samovar. Nicht alle Variablen lassen sich setzen. setStrVariable("SamovarStatus", "Test Samovar status")
getNumVariable("Variable") – liest eine interne numerische Variable von Samovar. TankTemp = getNumVariable("TankTemp") weist der Skriptvariablen TankTemp den Wert der Brennblasentemperatur zu
getStrVariable("Variable") – liest eine interne Zeichenkettenvariable von Samovar.
program_type = getStrVariable("program_type")– weist der Skriptvariablen program_type den Typ des gerade laufenden Programms zu.
 
Skriptvariablen existieren nur, solange das Skript läuft. Beim nächsten Lauf bleiben ihre Werte nicht erhalten. Manchmal muss man sich den Wert einer Variablen in einem Skriptlauf merken und in einem anderen lesen. Dafür gibt es zwei Funktionen – setObject("Object",Val) und getObject("Object")/getObject("Object","NUMERIC"). Die gesetzten Werte bleiben bis zum Neustart erhalten. Es kann höchstens 32 verschiedene Objekte geben; wird diese Zahl überschritten, endet setObject mit einem Fehler.
setLuaStatus(Status) – zeigt den Skriptstatus in der Oberfläche an („Lua-Status“ («Статус Lua») auf der Registerkarte „Zusätzlich“ («Дополнительно»)). Zum Beispiel, um die Skriptausführung zu verfolgen:
i = getObject("cnt", "NUMERIC")
i = i + 1
setLuaStatus("Zähler cnt = "..i)
setObject("cnt", i)
setObject("Object",Val) – speichert das Objekt Object mit dem Wert Val im Speicher von Samovar.
getObject("Object") – liest den zuvor gespeicherten Wert des Objekts Object. Wurde kein solches Objekt gespeichert, wird eine leere Zeichenkette zurückgegeben. Versuchen Sie, eine leere Zeichenkette als Zahl zu verwenden, erhält sie den Wert nil – undefiniert. Um die Arbeit mit Zahlenwerten zu erleichtern und die Prüfung auf nil zu vermeiden, können Sie diese Funktion mit dem zusätzlichen Parameter "NUMERIC" aufrufen; in diesem Fall wird 0 zurückgegeben, wenn das Objekt noch nicht initialisiert wurde.
Ein Beispiel für die Arbeit mit setObject/getObject:
n = 156
setObject("MyObject", n)
print (n)
n = n + 5
print (n)
n = getObject("MyObject")
print (n)
Wenn Sie dieses Skript ausführen, gibt es im Arduino-Portmonitor Folgendes aus
156
161
156
 
http_request(Url) – führt eine GET-Anfrage an die Adresse Url aus und liefert den Antworttext (oder die Zeichenkette "error"). So können Sie zum Beispiel Messwerte an Ihren eigenen Webdienst senden. Sie können auch eine POST-Anfrage senden, indem Sie vier Parameter übergeben: die Adresse, die Methode, den Header Content-Type und den Body:
http_request("http://test.com:80/post?foo1=bar1", "POST", "Content-Type: text/text; charset=utf-8", "body")
Ein Beispiel für ein Skript, das die Brennblasentemperatur an einen externen Webdienst sendet (ersetzen Sie Adresse und Parameter durch Ihre eigenen):
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" -- Zugangsschlüssel zu Ihrem Dienst
  http_request("http://example.com/notify?key=" .. key .. "&text=" .. urlencode(text))
end
 
local text = "Aktuelle Brennblasentemperatur = ".. getNumVariable("TankTemp")
SendToServer(text)
 
Die Funktionen der Platine I2CStepper (Rührwerk, Dosierpumpe, Relais) – 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 – sind auf der Seite I2CStepper beschrieben.
 
Variablen
Liste der internen Variablen, die in einem Skript standardmäßig verfügbar sind (ihre Werte werden vor jedem Skriptlauf gefüllt, eine Änderung per Zuweisung ist zwecklos):
 
Über getNumVariable verfügbare Variablen (nur lesbar): WFpulseCount, pump_started, valve_status, SamSetup_Mode, Samovar_Mode, Samovar_CR_Mode (der aktuelle und der umzuschaltende Modus, Nummern wie bei 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 (Alkoholgehalt in der Brennblase nach Temperatur), alcohol_s (Alkoholgehalt nach Dampf), water_pump_speed (Drehzahl der Wasserpumpe 0–1023), pressure_value (Druck in der Brennblase), YY, MM, DD, HH, MI, SS (Jahr, Monat, Tag, Stunden, Minuten, Sekunden).
Über getNumVariable und setNumVariable verfügbare Variablen: acceleration_temp, boil_temp (die gespeicherte Siedetemperatur), wp_count, test_num_val, loop_lua_fl, SetScriptOff, show_lua_script. Nur über setNumVariable: pmpKp, pmpKi, pmpKd – die PID-Koeffizienten der Wasserpumpe.
Zeichenkettenvariablen (getStrVariable/setStrVariable): SamovarStatus – der Statustext auf der Hauptseite (Lesen und Schreiben), test_str_val (Lesen und Schreiben), program_type (nur Lesen), Msg – liefert beim Lesen die nächste ungelesene Systemmeldung oder eine leere Zeichenkette, fügt beim Schreiben eine Meldung hinzu.
 
Variablen zur Steuerung der Skripte:
loop_lua_fl – Werte 0 oder 1. Bei 0 wird die sekündliche Skriptschleife nicht ausgeführt, bei 1 wird sie ausgeführt.
SetScriptOff – Werte 0 oder 1. Bei 1 wird die sekündliche Schleife gestoppt (setzt loop_lua_fl zurück). Samovar setzt den Wert am Ende eines Programms selbst auf 1.
show_lua_script – Werte 0 oder 1. Bei 0 wird das ausgeführte Skript nicht in der Konsole und im Portmonitor angezeigt, bei 1 wird es angezeigt. Kann zum Debuggen verwendet werden. Dabei werden auch die Werte aller für dieses Skript gesetzten Variablen angezeigt.
Wenn Sie zum Beispiel diese beiden Funktionen im Skript init.lua aufrufen, laufen jede Sekunde zwei Skripte (script.lua und das vom aktuellen Samovar-Modus bestimmte), und jedes Skript wird im Portmonitor und in der Browserkonsole ausgegeben
setNumVariable("loop_lua_fl",1)
setNumVariable("show_lua_script",1)