«Самовар» поддерживает автоматизацию на скриптовом языке Lua. Скрипты управляют внешним оборудованием (клапаны, насосы, мешалка, нагреватели, реле), читают показания датчиков и меняют логику процесса под конкретную установку. Базовый функционал контроллера при этом не меняется.
Редактор скриптов встроен в веб-интерфейс: Настройки → Редактор. Поддерживается подсветка синтаксиса Lua, JS, CSS, HTML и JSON, проверка баланса скобок и блоков function … end, автодополнение по клавише Tab.
Интерпретатор: Lua 5.4.4. Базовый API из встроенной в ESP32 Arduino-библиотеки расширен функциями доступа к датчикам, нагреву, насосам, мешалке и реле.
Как включить
По умолчанию поддержка Lua в прошивке отключена. Чтобы включить, откройте файл Samovar_ini.h в каталоге прошивки и замените строку
//#define USE_LUA
на
#define USE_LUA
После этого пересоберите прошивку и загрузите её в контроллер.
Архитектура
При старте контроллера выполняется файл init.lua. Его задача — инициализировать переменные, которые будут использоваться в других скриптах, и включить или выключить ежесекундный цикл выполнения (setNumVariable("loop_lua_fl", 1)).
Если цикл включён, каждую секунду подряд выполняются два скрипта:
- Общий
script.lua— работает во всех режимах. - Скрипт текущего режима:
beer.lua(пиво),bk.lua(бражная колонна),nbk.lua(непрерывная бражная колонна),dist.lua(дистилляция),rectificat.lua(ректификация),cheese.lua(сыроварение) илиsuvid.lua(сувид).
Если до следующего запуска скрипт не успел отработать, текущий запуск пропускается: уже работающий скрипт не прерывается.
Разовый запуск всех скриптов доступен по адресу http://samovar.local/lua.
Кроме основных скриптов поддерживаются дополнительные кнопки режима — файлы вида btn_<режим>_buttonN.lua, например btn_rect_button1.lua. Они отображаются в веб-интерфейсе и позволяют вызывать пользовательский код по нажатию.
Важно. При обновлении прошивки веб-интерфейс загружает с сервера эталонные версии init.lua, script.lua, файлов режимов и btn_*.lua, но только если такого файла ещё нет в памяти контроллера. Пользовательские скрипты не затираются. Тем не менее храните собственные версии на компьютере — после сброса к заводским настройкам или переустановки веб-интерфейса они будут потеряны.
Внешнее оборудование
К скриптам можно подключить расширители портов по I²C:
- PCF8575 — расширитель на 16 цифровых портов (определяется флагом
USE_EXPANDER). К каждому порту подключается любое исполнительное устройство или «кнопочный» датчик. Все порты доступны и для чтения, и для записи. - PCF8591 — расширитель аналоговых портов (определяется флагом
USE_ANALOG_EXPANDER). Используется для аналоговых датчиков — например, pH-электрода в режиме сыроварения.
Сценариев работы с расширителями много: можно реализовать собственный датчик безопасности, который при срабатывании выключает «Самовар» из скрипта, или управлять дополнительным ТЭНом по температуре пара. Конкретная логика определяется оборудованием.
Функции
GPIO и Arduino-совместимые
pinMode(pin, mode) — режим работы входа/выхода. Доступны только пины RELE_CHANNEL1, RELE_CHANNEL2, RELE_CHANNEL3, RELE_CHANNEL4 и LUA_PIN. Числовые значения пинов зависят от пиновой карты и приведены в описании GPIO.
digitalWrite(pin, value) — подать HIGH (1) или LOW (0) на пин.
digitalRead(pin) — считать текущее значение пина (HIGH или LOW).
analogRead() — считать аналоговое значение пина LUA_PIN в диапазоне 0–4095. На этом пине висит pH-электрод или датчик давления MPX5010DP, поэтому не подавайте на него больше 3,3 В: это выведет ESP32 из строя.
delay(ms) — пауза в миллисекундах.
millis() — миллисекунды с момента запуска контроллера.
Расширители портов
exp_pinMode(pin, mode), exp_digitalWrite(pin, value), exp_digitalRead(pin) — то же самое, что GPIO-функции, но для портов PCF8575 (0–15).
exp_analogRead(), exp_analogWrite(value) — аналоговые операции на PCF8591. Доступны только при включённом USE_ANALOG_EXPANDER.
Управление процессом
setPower(power) — включить (1) или выключить (0) нагрев.
setCurrentPower(value) — задать напряжение на регуляторе мощности. Если регулятор управляется по мощности, аргумент задаёт мощность. Доступно только при SAMOVAR_USE_POWER.
setBodyTemp(temp) — установить температуру тела при ректификации. В других режимах функция только выводит сообщение в консоль и в Blynk.
setMixer(value) — включить (1) или выключить (0) мешалку.
openValve(value) — открыть (1) или закрыть (0) клапан подачи воды.
setAlarm() — аварийный режим: контроллер выключает нагрев, закрывает клапан и отключает насос воды.
setNextProgram() — перейти к следующей строке программы (аналог кнопки в интерфейсе).
setPauseWithdrawal(value) — поставить (1) или снять (0) отбор на паузу.
setCapacity(num) — переключить текущую ёмкость отбора. Допустимый диапазон: 0…CAPACITY_NUM.
setLuaStatus(value) — установить статус Lua-режима. В случае занятости выбрасывает ошибку «Lua_status busy».
setPumpPwm(pwm) — ШИМ-управление производительностью насоса воды (0…1023). Доступно при USE_WATER_PUMP.
setServoAngle(angle) — задать угол сервопривода (0…SERVO_ANGLE). Доступно при SERVO_PIN.
setTimer(num, sec) — установить таймер num (1…10) на sec секунд. Всего 10 таймеров.
getTimer(num) — оставшееся время таймера в миллисекундах. Если таймер не установлен или уже истёк, вернётся 0.
getState() — числовой статус контроллера.
Переменные контроллера
getNumVariable(name) и setNumVariable(name, value) — чтение и запись числовых переменных. Запись возможна только для тех переменных, которые явно помечены как доступные для изменения (см. таблицу ниже). Пример: setNumVariable("TankTemp", 85) установит температуру куба 85 °C. Без понимания логики «Самовара» менять переменные рискованно — это может нарушить работу алгоритмов.
getStrVariable(name) и setStrVariable(name, value) — то же самое для строковых переменных.
Имена переменных, доступных из скрипта, приведены в таблице в следующем разделе.
Объекты — постоянное хранилище
Обычные переменные Lua живут только в момент выполнения скрипта и при следующем запуске теряются. Чтобы передать значение между запусками, используйте setObject и getObject:
setObject("name", value) -- сохранить
getObject("name") -- прочитать (вернёт "" если нет)
getObject("name", "NUMERIC") -- прочитать как число (вернёт 0 если нет)
Максимум 32 ключа (LUA_OBJECT_STORE_MAX_KEYS). Значения сохраняются до перезагрузки контроллера.
Лог, отладка, HTTP
sendMsg(msg, level) — вывести сообщение. При level = -1 — в com-порт и консоль браузера (для отладки). При level = 0, 1, 2 — в консоль и в Blynk.
http_request(url [, method, headers, body]) — HTTP-запрос. Удобно для интеграции с внешними сервисами: отправки уведомлений в Telegram, логирования, вебхуков. Таймаут короче, чем у обычной загрузки, и работает под общим мьютексом, поэтому одновременные вызовы из разных скриптов не конфликтуют.
I²C-устройства
check_I2C_device(address) — проверить наличие устройства на I²C-шине по адресу. Возвращает 1, если устройство ответило, иначе 0.
get_i2c_rele_state(relay) и set_i2c_rele_state(relay, state) — состояние и переключение I²C-реле. Допустимые номера: 1…4.
Шаговый модуль
set_stepper_by_time(speed, direction, seconds) — запустить шаговый двигатель на заданной скорости (0…65535) и направлении (0 или 1) на указанное время. Возвращает 1, если запуск выполнен.
set_stepper_target(speed, direction, target) — отработать заданное число шагов. Возвращает 1, если запуск выполнен.
get_stepper_status() — текущее состояние шагового модуля.
set_mixer_pump_target(target) и get_mixer_pump_status() — управление мешалкой-насосом (0 или 1).
I²C-насос
Доступны при use_I2C_dev = 2:
i2cpump_start(rate, ml) -- запустить насос с заданной скоростью и объёмом
i2cpump_stop() -- остановить
i2cpump_get_speed() -- текущая скорость
i2cpump_get_target_ml() -- целевой объём
i2cpump_get_remaining_ml() -- осталось прокачать
i2cpump_get_running() -- 1 если крутится, иначе 0
Плохие числа (NaN, Inf, ≤ 0) молча игнорируются, без ошибки.
Сторож корутин
В ESP32-версии лимит вложенных C-вызовов Lua снижен с 200 до 60 (LUAI_MAXCCALLS = 60), потому что на стеке задачи do_lua_script размером 8 КБ исходный лимит недосягаем. Если в скрипте используются coroutine.create / wrap / resume, обязательно вызовите armCoroutineWatchdog() в init.lua: иначе корутина съест стек раньше, чем сработает лимит.
Переменные
В таблице ниже перечислены переменные, доступные из скрипта. RO — только чтение, RW — можно менять через setNumVariable.
Показания датчиков (RO)
SteamTemp— температура датчика пара.PipeTemp— температура датчика царги.WaterTemp— температура датчика воды.TankTemp— температура датчика куба.ACPTemp— температура датчика ТСА.pressure_value— давление в системе.alcohol— расчётная крепость в реальном времени.alcohol_s— крепость в стабилизированном состоянии.
Состояние и статусы (RO)
PowerOn— 0/1, признак включённого нагрева.PauseOn— 0/1, признак паузы отбора.pump_started— 0/1, насос воды работает.valve_status— 0/1, клапан воды.water_pump_speed— текущая скорость насоса.program_Wait— 0/1, программа стоит на паузе.SamSetup_Mode— режим работы «Самовара».Samovar_Mode— текущий режим процесса.Samovar_CR_Mode— текущий режим по внутренней классификации.target_power_volt— заданное напряжение регулятора.WFpulseCount,WFflowRate,WFtotalMilliLitres— показания расходомера (при наличии).
Параметры текущей строки программы (program_volume, program_speed, program_temp, program_power, program_time, program_capacity_num) и текущая ёмкость отбора (capacity_num) тоже доступны только для чтения.
Параметры, которые можно менять (RW)
acceleration_temp— температура разгонного ТЭНа.boil_temp— температура кипения.loop_lua_fl— 0/1, выключает или включает ежесекундный запуск скриптов.SetScriptOff— 0/1, принудительное отключение скриптов.show_lua_script— 0/1, выводить тело скрипта и значения переменных в com-порт и консоль (для отладки).test_num_val— числовая переменная для отладки.wp_count— количество импульсов расходомера (приUSE_WATER_PUMP).
Строковые переменные
SamovarStatus(RW) — текущий статус, отображаемый в интерфейсе.program_type(RO) — тип выполняемой программы.test_str_val(RW) — строковая переменная для отладки.Msg(спец.) — чтение продвигает курсор по кольцу событий; запись — черезsetStrVariable.
Виртуальные датчики (SAMOVAR_LUA_SIMULATION)
При включённой симуляции доступны VirtualSteamTemp, VirtualPipeTemp, VirtualWaterTemp, VirtualTankTemp, VirtualACPTemp (все RW). Используются для отладки скриптов без железа.
Часы (RO)
YY, MM, DD, HH, MI, SS — текущие год, месяц, день, час, минута, секунда по RTC контроллера.
Примеры
setObject и getObject
n = 156
setObject("MyObject", n)
print(n) -- 156
n = n + 5
print(n) -- 161
n = getObject("MyObject")
print(n) -- 156 (прочитали сохранённое)
Отправка температуры куба в 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 = "Текущая температура куба = "
.. getNumVariable("TankTemp")
SendTelegram(text)
Аналоговый датчик уровня и насос воды
Пример читает аналоговый датчик уровня и включает насос, когда уровень попадает в заданный диапазон. Состояние насоса сохраняется между запусками через 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
Безопасность и ограничения
- 10 таймеров одновременно.
- 32 ключа в хранилище объектов (
LUA_OBJECT_STORE_MAX_KEYS). - Лимит вложенных C-вызовов Lua: 60 (
LUAI_MAXCCALLS). Если используетеcoroutine.*, обязательно вызовитеarmCoroutineWatchdog(). - Пины для
pinMode: толькоRELE_CHANNEL1…RELE_CHANNEL4иLUA_PIN. Пин, занятый pH-электродом сыра, использовать нельзя — функция вернёт ошибку. - На
analogRead()подавайте не больше 3,3 В. setBodyTempработает только в режиме ректификации; в остальных режимах выводит предупреждение.- При вызове
setAlarm()контроллер прекращает нагрев, закрывает клапан и отключает насос. - Числовые аргументы
i2cpump_startдолжны быть конечными положительными числами;NaN/Inf/≤0 молча игнорируются.
Дополнительные материалы
- Официальное руководство по Lua 5.4
- «Программирование на языке Lua» (PDF, R. Ierusalimschy и др.)
- Blockly — визуальный генератор Lua-скриптов, если привычнее собирать логику блоками.