Перейти к содержимому
Пользовательские скрипты | Custom Scripts

Пользовательские скрипты | Custom Scripts

05 марта 2026
Хобби

Пользовательские скрипты (Custom Scripts)

Цель: использовать пользовательские Lua-скрипты как «миксерные» скрипты: принимать один или несколько входов, выполнять обработку и выдавать один или несколько выходов, которые можно выбирать в микшере как источники.

Содержание

Пользовательские миксерные скрипты

Что это такое

Пользовательские скрипты микшера (Custom (Mixes) Scripts) принимают один или несколько входных значений, выполняют обработку в Lua-коде и выдают одно или несколько выходных значений.

Каждая модель может иметь несколько миксерных скриптов, связанных с ней; эти скрипты выполняются периодически. По поведению они похожи на стандартные миксы EdgeTX, но при этом дают гораздо более гибкий и мощный инструмент.

Типовые сценарии

  • замена сложных миксов, которые не являются критичными для функционирования модели;
  • сложная обработка входов и реакция на их текущее состояние и/или историю;
  • фильтрация телеметрических значений.

Критичное предупреждение

Если выход скрипта используется как mixer source, и скрипт по любой причине будет остановлен (killed), то вся строка микшера будет отключена. Будьте осторожны, используя такие скрипты для основных органов управления. Рекомендуется иметь резервную строку микшера, которая будет использоваться, если миксерный скрипт по какой-либо причине завершится.

Пример экрана

05032026_5_f2766.png

Экран Custom Scripts со слотами LUA1LUA6.

↑ К оглавлению

Входы и выходы миксерных скриптов

Идея: входы/выходы как источники микшера

Ниже приведён пример миксерного скрипта, который принимает источник и константное значение и имеет два выхода. Эти выходы затем можно выбирать в микшере как источники.

Пример экрана настройки

05032026_6_d7c71.png

Экран настройки скрипта: выбор файла, имя, входы и выходы.

Конспект урока по Custom Scripts

В этом уроке вы разберёте Custom Scripts в EdgeTX (миксерные Lua‑скрипты): как подключать их к модели, как устроены входы/выходы, почему важен fallback, и как быстро собрать 4 типовых «кирпичика» для реальных задач. В конце — практика с готовыми решениями и чек‑лист самопроверки.

Главная мысль: относитесь к Custom Scripts как к «виртуальным источникам микшера»: скрипт превращает ваши входы в новые источники LUA1a, LUA1b… — а микшер уже решает, как их применить. Для безопасности всегда держите резервный микс без Lua.

Содержание

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 и т. д.

Практический вывод: Custom Scripts — это слой «математики/обработки сигналов», а не слой «действий» (звук/лог/скриншот). Действия лучше оставлять в Special Functions, а условия — в Logical Switches.
-- Минимальный «скелет» миксерного скрипта

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 }
Lua

Что часто путают (и почему это не то)

Запомните: Custom Scripts (Mix) — это не «скрипты из Tools» и не «скрипты из Special Functions». Скрипты, которые рисуют интерфейс (виджеты/телеметрийные экраны) или требуют ввода пользователя, относятся к другим типам Lua‑скриптов и не должны реализовываться как миксерные.
↑ К оглавлению

3. Подключение: файлы, слоты LUA1…LUA6, входы/выходы

Где лежат файлы и как выбрать скрипт

Скрипты этого типа загружаются с SD‑карты при выборе модели. Для миксерных скриптов используется папка /SCRIPTS/MIXES/. Имя файла (без расширения .lua) должно быть коротким — 6 символов или меньше.

SD:/SCRIPTS/MIXES/
  SLEW.lua
  LPFILT.lua
  Q6POS.lua
  PEAK.lua
Plain text
Рекомендация по именам: делайте имена «говорящими», но короткими (до 6 символов), а читаемость выносите в поле 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 }
Lua

Типичные ошибки (блок 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
Plain text
Запомните: не используйте Custom Scripts как единственный источник для критичных органов управления (газ, рули, ARM), если отказ скрипта может привести к аварии. Всегда проектируйте деградацию «в безопасную сторону».

Как писать «лёгкие» скрипты (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 }
Lua

Типичные ошибки (блок 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 }
Lua

Рецепты применения

Рецепт: закрылки на тумблере без «удара»

Подайте тумблер в 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
Plain text

Рецепт: «успокоить» крутилку

Если потенциометр шумит или вы хотите «медленнее» наводить камеру — ограничьте скорость изменения.

Custom Scripts (LUA1):
  In=S1
  Up=10
  Dn=10

Mixes (CH7 Gimbal):
  Line1 fallback: Src=S1
  Line2 Lua:      Src=LUA1a  Replace
Plain text

Типичные ошибки (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 }
Lua

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 }
Lua

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 }
Lua

Сравнение подходов и когда что выбирать

ШаблонЧто решаетКогда выбиратьТиповые параметры
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
Plain text

Задача 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 }
Lua
Mixes (пример):
CH8:
  Line1: Src=LUA2a Weight=100   (Raw)
CH9:
  Line1: Src=LUA2b Weight=100   (Half)
Plain text

Задача 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
Plain text

Задачи «как в жизни» (телеметрия, режимы, сервис)

Задача 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
Plain text

Задача 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 (минимум)
Plain text
↑ К оглавлению

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 с управляемым сбросом
↑ К оглавлению
Пользовательские скрипты | Custom Scripts
10

Похожее в категории "СОФТ"

  • Специальные функции | Special Functions

    Цель: настроить дополнительные действия EdgeTX, которые выходят за рамки обычного управления моделью: тренер, звуки, подсветка, громкость, скриншоты, логирование на SD и т. п.

  • Телеметрия | Telemetry

    Цель: настроить приём и отображение телеметрии: список датчиков (Sensors), пороги предупреждений приёма (Rx-Stats) и вариометр (Variometer).