Пользовательские скрипты | Custom Scripts
Пользовательские скрипты (Custom Scripts)
Цель: использовать пользовательские Lua-скрипты как «миксерные» скрипты: принимать один или несколько входов, выполнять обработку и выдавать один или несколько выходов, которые можно выбирать в микшере как источники.
Содержание
Пользовательские миксерные скрипты
Что это такое
Пользовательские скрипты микшера (Custom (Mixes) Scripts) принимают один или несколько входных значений, выполняют обработку в Lua-коде и выдают одно или несколько выходных значений.
Каждая модель может иметь несколько миксерных скриптов, связанных с ней; эти скрипты выполняются периодически. По поведению они похожи на стандартные миксы EdgeTX, но при этом дают гораздо более гибкий и мощный инструмент.
Типовые сценарии
- замена сложных миксов, которые не являются критичными для функционирования модели;
- сложная обработка входов и реакция на их текущее состояние и/или историю;
- фильтрация телеметрических значений.
Критичное предупреждение
Если выход скрипта используется как mixer source, и скрипт по любой причине будет остановлен (killed), то вся строка микшера будет отключена. Будьте осторожны, используя такие скрипты для основных органов управления. Рекомендуется иметь резервную строку микшера, которая будет использоваться, если миксерный скрипт по какой-либо причине завершится.
Пример экрана

Экран Custom Scripts со слотами LUA1…LUA6.
↑ К оглавлениюВходы и выходы миксерных скриптов
Идея: входы/выходы как источники микшера
Ниже приведён пример миксерного скрипта, который принимает источник и константное значение и имеет два выхода. Эти выходы затем можно выбирать в микшере как источники.
Пример экрана настройки

Экран настройки скрипта: выбор файла, имя, входы и выходы.
Конспект урока по Custom Scripts
В этом уроке вы разберёте Custom Scripts в EdgeTX (миксерные Lua‑скрипты): как подключать их к модели, как устроены входы/выходы, почему важен fallback, и как быстро собрать 4 типовых «кирпичика» для реальных задач. В конце — практика с готовыми решениями и чек‑лист самопроверки.
Главная мысль: относитесь к Custom Scripts как к «виртуальным источникам микшера»: скрипт превращает ваши входы в новые источники LUA1a, LUA1b… — а микшер уже решает, как их применить. Для безопасности всегда держите резервный микс без Lua.
Содержание
- 1. Цели урока
- 2. Что такое Custom (Mixer) Scripts
- 3. Подключение: файлы, слоты LUA1…LUA6, входы/выходы
- 4. Безопасность: падение скрипта, fallback и производительность
- 5. Шаблон №1: SLEW.lua (мягкое движение серв/каналов)
- 6. Шаблоны №2–№4: LPFILT, Q6POS, PEAK (фильтр, дискретизация, пики)
- 7. Практика: типовые задачи (с решениями)
- 8. Чек‑лист самопроверки знаний (обязательный)
1. Цели урока
- Понять, что такое Custom (Mixer) Scripts и как они становятся источниками
LUA1a…в микшере. - Научиться подключать скрипт к слоту
LUA1…LUA6, настраивать входы и использовать выходы в Mixes. - Собрать безопасную схему с fallback: «обычный микс + Lua‑микс Replace поверх него».
- Забрать 4 готовых шаблона под реальные задачи:
SLEW,LPFILT,Q6POS,PEAK.
2. Что такое Custom (Mixer) Scripts
Определение и границы
Custom (Mixer) Script — это Lua‑скрипт, который выполняется периодически и ведёт себя как «продвинутый микс»: он получает один или несколько входов, обрабатывает их и возвращает один или несколько выходов. Эти выходы появляются в списке источников микшера как LUA1a, LUA1b и т. д.
-- Минимальный «скелет» миксерного скрипта
local input = {
{ "In", SOURCE }, -- пользователь выбирает источник
{ "K", VALUE, 0, 127, 50 }, -- константа (0..127), задаётся в настройке скрипта
}
local output = { "Out" }
local function init()
-- вызывается при загрузке модели
end
local function run(x, k)
-- вызывается периодически
local y = x * (k / 100)
return y
end
return { input=input, output=output, run=run, init=init }
Что часто путают (и почему это не то)
3. Подключение: файлы, слоты LUA1…LUA6, входы/выходы
Где лежат файлы и как выбрать скрипт
Скрипты этого типа загружаются с SD‑карты при выборе модели. Для миксерных скриптов используется папка /SCRIPTS/MIXES/. Имя файла (без расширения .lua) должно быть коротким — 6 символов или меньше.
SD:/SCRIPTS/MIXES/
SLEW.lua
LPFILT.lua
Q6POS.lua
PEAK.lua
Name внутри настроек слота LUA.Входы (SOURCE/VALUE) и выходы (LUA1a…)
Входы описываются таблицей input. Есть два главных типа: SOURCE (пользователь выбирает источник: стики, каналы, телеметрия, переключатели и т. п.) и VALUE (константа, задаётся числом в настройке скрипта). Типичный диапазон значений для SOURCE — -1024..+1024.
-- Пример входов/выходов: 1 источник + 1 константа, 2 выхода
local input = {
{ "Strength", SOURCE }, -- SOURCE: пользователь выбирает источник
{ "Interval", VALUE, 0, 100, 0 } -- VALUE: константа (0..100), default = 0
}
local output = { "Val1", "Val2" }
local function run(Strength, Interval)
local v1 = Strength
local v2 = Interval * 10
return v1, v2
end
return { input=input, output=output, run=run }
Типичные ошибки (блок 3)
- Скрипт не появляется в списке выбора: файл лежит не в
/SCRIPTS/MIXES/или имя длиннее 6 символов. - Ошибки “не те аргументы” в
run(): порядок параметровrun(a,b,c...)должен совпадать с порядком в таблицеinput. - Непредсказуемая шкала: вы вернули значения не в «ожидаемом» диапазоне микшера. Договоритесь с собой: возвращаем
-1024..+1024и только потом масштабируем в микшере.
4. Безопасность: падение скрипта, fallback и производительность
Fallback‑паттерн в Mixes (Replace поверх базовой строки)
В EdgeTX миксерные Lua‑скрипты могут быть остановлены/убиты из‑за ошибок, нехватки памяти и т. п. Важно: если выход скрипта используется как mixer source и скрипт «убит», то соответствующая строка микшера отключается. Это поведение удобно превращать в защиту: держите базовую строку микса (без Lua), а строку с Lua делайте как Multiplex=Replace поверх базовой — тогда при проблемах останется «обычная» управляемость.
Пример (идея):
CH7 (гимбал)
Line1 (fallback): Source=S1 Weight=100
Line2 (Lua): Source=LUA1a Weight=100 Multiplex=Replace
Как писать «лёгкие» скрипты (dt, getTime, минимум работы)
Скрипты выполняются периодически и с меньшим приоритетом, чем встроенные миксы; период выполнения примерно около 30 мс и не гарантирован. Поэтому: считайте dt через getTime(), избегайте тяжёлых циклов и больших аллокаций, и держите код максимально коротким.
-- dt по getTime(): getTime() возвращает «тики» по 10мс
local lastTime
local function init()
lastTime = getTime()
end
local function run(x)
local now = getTime()
local dt = (now - lastTime) * 0.01 -- секунды
lastTime = now
if dt <= 0 then
return x
end
-- дальше любая математика, учитывающая dt
return x
end
return { run=run, init=init }
Типичные ошибки (блок 4)
- Игнорировать
dt: алгоритм «плывёт», потому что реальный период вызовов не фиксирован. - Слишком тяжёлый код: большие циклы/таблицы внутри
run()→ задержки, фризы, возможный kill. - Нет fallback: всё управление завязано на
LUAxaи при падении скрипта модель теряет канал.
5. Шаблон №1: SLEW.lua (мягкое движение серв/каналов)
Идея и код
Slew‑rate limiter ограничивает скорость изменения сигнала. Это полезно, когда вы включаете закрылки тумблером, двигаете гимбал или хотите «успокоить» резкие движения на сервоканале. В отличие от простых задержек микшера, скрипт легко делает разные скорости «вверх» и «вниз».
-- SLEW.lua
-- Ограничитель скорости изменения: Up (%/sec) и Dn (%/sec)
local input = {
{ "In", SOURCE },
{ "Up", VALUE, 0, 127, 50 }, -- %/сек вверх
{ "Dn", VALUE, 0, 127, 50 }, -- %/сек вниз
}
local output = { "Out" }
local lastTime = nil
local y = 0
local function clamp(x, lo, hi)
if x < lo then return lo end
if x > hi then return hi end
return x
end
local function init()
lastTime = getTime()
y = 0
end
local function run(x, upRate, dnRate)
local now = getTime()
local dtTicks = now - (lastTime or now)
lastTime = now
local dt = dtTicks * 0.01
if dt <= 0 then
return y
end
-- 100% == 1024, значит 1% == 10.24
local maxUp = upRate * 10.24 * dt
local maxDn = dnRate * 10.24 * dt
local d = x - y
if d > 0 then
if d > maxUp then d = maxUp end
else
if -d > maxDn then d = -maxDn end
end
y = y + d
y = clamp(y, -1024, 1024)
return y
end
return { input=input, output=output, run=run, init=init }
Рецепты применения
Рецепт: закрылки на тумблере без «удара»
Подайте тумблер в In (он будет прыгать -100/0/+100), а на выходе получите плавный переход.
Custom Scripts (LUA1):
File=SLEW
In=SA
Up=20
Dn=35
Mixes (CH6 Flaps):
Line1 fallback: Src=SA Weight=100
Line2 Lua: Src=LUA1a Weight=100 Multiplex=Replace
Рецепт: «успокоить» крутилку
Если потенциометр шумит или вы хотите «медленнее» наводить камеру — ограничьте скорость изменения.
Custom Scripts (LUA1):
In=S1
Up=10
Dn=10
Mixes (CH7 Gimbal):
Line1 fallback: Src=S1
Line2 Lua: Src=LUA1a Replace
Типичные ошибки (SLEW)
- Up/Dn = 0: выход «застынет» и почти не будет догонять вход (это иногда полезно как блокировка, но чаще — баг).
- Нет начальной синхронизации: при старте
yможет быть 0, а вход уже не 0 → первое движение будет «догоняющим». Если это критично — инициализируйтеyтекущим входом вinit(). - Не учли масштаб: вы думаете в процентах, а считаете в
-1024..+1024. Договоритесь об одной шкале и придерживайтесь её.
6. Шаблоны №2–№4: LPFILT, Q6POS, PEAK (фильтр, дискретизация, пики)
Коды трёх шаблонов
LPFILT.lua — сглаживание (EMA low‑pass) + скорость изменения
Подходит для телеметрии (напряжение/RSSI/высота), чтобы пороги и озвучка не «дребезжали».
-- LPFILT.lua
-- EMA low-pass filter + производная (скорость изменения)
local input = {
{ "In", SOURCE },
{ "Tau", VALUE, 0, 127, 20 }, -- десятки секунд: 20 => 2.0s
}
local output = { "Flt", "Rate" }
local lastTime = nil
local y = 0
local lastY = 0
local function init()
lastTime = getTime()
y = 0
lastY = 0
end
local function run(x, tau10)
local now = getTime()
local dtTicks = now - (lastTime or now)
lastTime = now
local dt = dtTicks * 0.01
if dt <= 0 then
return y, 0
end
local tau = (tau10 or 0) * 0.1
local alpha
if tau <= 0 then
alpha = 1
else
alpha = dt / (tau + dt)
end
lastY = y
y = y + alpha * (x - y)
local rate = (y - lastY) / dt
return y, rate
end
return { input=input, output=output, run=run, init=init }
Q6POS.lua — крутилка → N дискретных позиций с гистерезисом
Делает «виртуальный 6‑позиционный переключатель» из потенциометра и убирает прыганье на границах.
-- Q6POS.lua
-- Quantizer: аналоговый вход -> N дискретных позиций с гистерезисом
local input = {
{ "In", SOURCE },
{ "N", VALUE, 2, 12, 6 },
{ "Hys", VALUE, 0, 50, 5 }, -- гистерезис в "%"
}
local output = { "Pos" }
local idx = 0
local levels = {}
local function clamp(x, lo, hi)
if x < lo then return lo end
if x > hi then return hi end
return x
end
local function buildLevels(n)
levels = {}
for i = 0, n - 1 do
local v = -1024 + (2048 * i) / (n - 1)
levels[i] = v
end
end
local function init()
idx = 0
buildLevels(6)
end
local function run(x, n, hysPct)
n = clamp(n or 6, 2, 12)
if #levels ~= n then
buildLevels(n)
idx = clamp(idx, 0, n - 1)
end
local hys = (hysPct or 0) * 10.24
while idx < n - 1 do
local midUp = (levels[idx] + levels[idx + 1]) / 2
if x > midUp + hys then
idx = idx + 1
else
break
end
end
while idx > 0 do
local midDn = (levels[idx - 1] + levels[idx]) / 2
if x < midDn - hys then
idx = idx - 1
else
break
end
end
return levels[idx]
end
return { input=input, output=output, run=run, init=init }
PEAK.lua — min/max + peak‑hold с затуханием
Позволяет хранить минимум/максимум за сессию и «пик, который медленно опускается» (удобно для “худших” значений).
-- PEAK.lua
-- min/max tracker + peak-hold with decay
local input = {
{ "In", SOURCE },
{ "Rst", SOURCE }, -- кнопка/тумблер сброса
{ "Dec", VALUE, 0, 127, 20 }, -- затухание пика (%/sec)
}
local output = { "Cur", "Max", "Min", "Pk" }
local lastTime = nil
local maxV = -1024
local minV = 1024
local peak = -1024
local function init()
lastTime = getTime()
maxV = -1024
minV = 1024
peak = -1024
end
local function run(x, rst, dec)
local now = getTime()
local dtTicks = now - (lastTime or now)
lastTime = now
local dt = dtTicks * 0.01
if (rst or 0) > 0 then
maxV = x
minV = x
peak = x
end
if x > maxV then maxV = x end
if x < minV then minV = x end
if x >= peak then
peak = x
else
local drop = (dec or 0) * 10.24 * dt
peak = peak - drop
if peak < x then peak = x end
end
return x, maxV, minV, peak
end
return { input=input, output=output, run=run, init=init }
Сравнение подходов и когда что выбирать
| Шаблон | Что решает | Когда выбирать | Типовые параметры |
|---|---|---|---|
LPFILT |
Сглаживание + оценка скорости изменения | Телеметрия шумит / пороги дрожат / голос «спамит» | Tau=10..40 (≈ 1–4s), по месту |
Q6POS |
Дискретные позиции из потенциометра | Нужно N режимов, но не хватает тумблеров | N=4..8, Hys=3..10 |
PEAK |
Min/Max/Peak‑hold | Нужно «худшее за полёт» (RSSI min, Vbat min, ток max) | Dec=5..30, Rst на кнопку |
Типичные ошибки (LPFILT/Q6POS/PEAK)
- LPFILT: поставить слишком большой
Tau→ значение «тупит» и опаздывает, предупреждения приходят поздно. - Q6POS: сделать
Hys=0→ на границах позиция прыгает туда‑сюда из‑за шума потенциометра. - PEAK: не продумать сброс
Rst→ min/max копятся “навсегда” и становятся бесполезными. Сбрасывайте по кнопке, при старте сессии или при ARM.
7. Практика: типовые задачи (с решениями)
Базовые задачи (подключение и проверка)
Задача 1: Подключите SLEW к гимбалу (CH7) с fallback
Сделайте так, чтобы CH7 управлялся крутилкой S1, но «мягко». При падении Lua управление должно остаться прямым от S1.
1) SD:
/SCRIPTS/MIXES/SLEW.lua
2) Model Settings → Custom Scripts → LUA1:
File: SLEW
Name: GIM_SLEW
In: S1
Up: 10
Dn: 10
3) Mixes → CH7:
Line1 (fallback): Src=S1 Weight=100
Line2 (Lua): Src=LUA1a Weight=100 Multiplex=Replace
Задача 2: Проверьте, что входы/выходы соответствуют (input → run → output)
Сделайте тестовый скрипт, который возвращает два выхода: «как есть» и «в 2 раза слабее». Убедитесь, что вы правильно читаете LUAxa и LUAxb.
-- TST2.lua (имя до 6 символов)
local input = { { "In", SOURCE } }
local output = { "Raw", "Half" }
local function run(x)
return x, x * 0.5
end
return { input=input, output=output, run=run }
Mixes (пример):
CH8:
Line1: Src=LUA2a Weight=100 (Raw)
CH9:
Line1: Src=LUA2b Weight=100 (Half)
Задача 3: Подключите Q6POS и сделайте «6 режимов» на CH10
Превратите S1 в 6 дискретных положений и отдайте это в CH10 (например, для выбора режима в полётном контроллере).
Custom Scripts → LUA3:
File: Q6POS
In: S1
N: 6
Hys: 6
Mixes → CH10:
Line1 fallback: Src=S1 Weight=100
Line2 Lua: Src=LUA3a Weight=100 Replace
Задачи «как в жизни» (телеметрия, режимы, сервис)
Задача 4: Сгладьте телеметрию RxBt через LPFILT и используйте для предупреждения
Цель: убрать ложные срабатывания на просадках. Сгладьте RxBt, затем сравнивайте порогом в Logical Switch и озвучивайте через Special Function.
Custom Scripts → LUA2:
File: LPFILT
In: RxBt
Tau: 20 (≈ 2.0s)
Logical Switches:
L01: (LUA2a < порог) -- LUA2a = Flt
Special Functions:
SF1: Trigger=L01 → Play Value (RxBt или LUA2a) Repeat=10s
Задача 5: Peak‑hold для RSSI (или напряжения) с кнопкой сброса
Отслеживайте минимум RSSI за полёт (или минимум напряжения). Сброс — на momentary‑кнопку.
Custom Scripts → LUA4:
File: PEAK
In: RSSI (или RxBt)
Rst: SH↓ (momentary)
Dec: 15
Использование:
LUA4c = Min
LUA4b = Max
LUA4d = Peak-hold
Идея:
- после полёта нажмите SH↓ и/или озвучьте LUA4c (минимум)
8. Чек‑лист самопроверки знаний
Отметьте пункты, которые вы действительно понимаете и можете применить без подсказок.
| ✓ | Навык | Проверка |
|---|---|---|
| Понимаю назначение Custom Scripts | Могу объяснить, что скрипт превращает входы в источники LUA1a… для микшера |
|
| Знаю ограничения миксерных скриптов | Могу перечислить ограничения (периодичность/приоритет/ошибки/риски) и почему нужен fallback | |
| Умею правильно размещать файлы | Могу положить скрипт в /SCRIPTS/MIXES/ и подобрать имя ≤ 6 символов |
|
| Умею описывать интерфейс скрипта | Могу написать input/output и вернуть таблицу интерфейса через return { ... } |
|
| Умею подключать скрипт к слоту LUA | Могу настроить входы SOURCE/VALUE в UI и увидеть выходы как LUAxa, LUAxb |
|
| Умею делать fallback в Mixes | Могу собрать «Line1 обычная + Line2 Lua Replace» и понимаю, что будет при kill скрипта | |
| Умею учитывать dt | Могу посчитать dt через getTime() и сделать алгоритм устойчивым к плавающему периоду |
|
| Могу применить SLEW | Могу настроить мягкое движение серв/канала с разными скоростями вверх/вниз | |
| Могу применить LPFILT | Могу сгладить телеметрию и использовать сглаженное значение в порогах/предупреждениях | |
| Могу применить Q6POS и PEAK | Могу сделать дискретные режимы из крутилки и считать min/max/peak‑hold с управляемым сбросом |
Похожее в категории "СОФТ"
-
Специальные функции | Special Functions
Цель: настроить дополнительные действия EdgeTX, которые выходят за рамки обычного управления моделью: тренер, звуки, подсветка, громкость, скриншоты, логирование на SD и т. п.
-
Телеметрия | Telemetry
Цель: настроить приём и отображение телеметрии: список датчиков (Sensors), пороги предупреждений приёма (Rx-Stats) и вариометр (Variometer).
