### [ На русском ] ↔ [ English ]

- Что это такое, вообще? Короткий пример.
- Алиасы : создание, редактирование и исполнение
- Отображение информации об алиасах
- Условия, события и действия: команда "if"
- Переодические действия: команда "every"
- Отображение информации об условиях IF/EVERY

## ⇮ЧТО ЭТО ТАКОЕ:

Алиасы, или «командные алиасы» — это способ выполнения сразу нескольких команд оболочки, как в shell-скрипте: либо явно, с помощью команды exec, либо неявно — как часть команды if. Алиасы имеют имена и вызываются по этим именам. Их можно представить как именованные контейнеры с командами — аналог файлов ".sh" или ".bat", только хранящихся в оперативной памяти.

Рассмотрим простую задачу автоматизации дома: управление водяным насосом. Она поможет вам в общих чертах понять систему алиасов и событий. Подробный список команд приведён в конце документа.

У нас есть ёмкость с водой и два датчика: GPIO2 для сигнала «Мало воды» и GPIO4 — «Слишком много воды».

                                 Залив
                               <=========+
                          |       |      |
      GPIO4 <-------------#       |      |
     "Слишком" sens       |~~~~~~~|      |
                          | .  o  |      |
                          |  . .  |      |
      GPIO2 <-------------# вода  |     ###<------------- GPIO20, "Start"
     "Мало" sens          | o .   |     #@#
                          |__   __|     ###<------------- GPIO21, "Drive"
                             \ /     Водяной насосик,
                             | |     с контроллером
                            Слив

Рис. 1: Схема подключения и общая идея

Датчики состоят из двух проводов, которые замыкаются водой в баке. Когда вода касается датчика, на его выходе появляется высокий уровень (HIGH), а когда уровень воды падает ниже датчика — он переходит в низкий (LOW).

Водяной насос включается и выключается последовательным управлением: сначала установить высокий уровень на GPIO20 на 3 секунды, затем — на GPIO21. GPIO20 используется для «старта» двигателя с повышенным током, а GPIO21 — для нормального режима работы.

Когда вода достигает верхнего датчика, напряжение на GPIO4 меняется с LOW на HIGH. Когда вода опускается ниже нижнего датчика — напряжение на GPIO2 падает с HIGH до LOW.

Как реализовать автоматическое управление насосом с помощью команд оболочки?

Сначала создадим алиасы "motor_on" и "motor_off" для управления насосом:

      esp32#>alias motor_on
      esp32-alias>

Команда "alias motor_on" создаёт новый алиас с именем "motor_on" и переводит оболочку в режим редактирования алиаса. В этом режиме любая введённая команда добавляется в алиас:

    esp32#>alias motor_on
    esp32-alias>pin 4 high delay 3000 low
    esp32-alias>pin 4 low 2 high
    esp32-alias>Hello World!

ПРИМЕЧАНИЕ: Чтобы выйти из режима редактирования, используется команда "quit", а не "exit", поскольку "exit" может быть частью самого алиаса.

В любой момент можно просмотреть содержимое алиаса командой "list":

      esp32-alias>list
      % Alias "motor_on":
      % 1: pin 4 high delay 3000 low
      % 2: pin 2 high 4 low
      % 3: Hello World!
      % --- END ---

Числа 1, 2, 3 — это номера строк. Их можно использовать с командой "delete" для удаления конкретной строки. Удалим, например, "Hello World!":

      esp32-alias>del 3
      esp32-alias>list
      % Alias "motor_on":
      % 1: pin 4 high delay 3000 low
      % 2: pin 4 low 2 high
      % --- END ---

ПРИМЕЧАНИЕ: команда "delete" без аргументов удаляет последнюю строку. "delete all" — очищает весь алиас.

Теперь создадим алиас "motor_off":

      esp32#>alias motor_off
      esp32-alias>pin 2 low 4 low
      esp32-alias>quit
      esp32#>

Теперь у нас есть два алиаса с командами. Их можно выполнять с помощью команды "exec":

      esp32#>exec motor_on
      esp32#>exec motor_off

GPIO2 — нижний датчик, GPIO4 — верхний. Когда вода доходит до верхнего, напряжение на GPIO4 повышается. Когда опускается ниже нижнего — напряжение на GPIO2 падает. Для таких событий (рост/падение сигнала) используется команда if:

      esp32#>if falling 2 exec motor_on
      esp32#>if rising 4 exec motor_off

Введеные условия (if) начинают работать сразу (если не было указанно обратное), отслеживая уровень сигнала на сенсорах и включая\выключая мотор в нужное время. Конечно, это очень упрощенная схема. В реальной придется указывать максимальную разрешенную частоту срабатывания событий.

## ⇮АЛИАСЫ: СОЗДАНИЕ, РЕДАКТИРОВАНИЕ, ВЫПОЛНЕНИЕ

Команда | Описание и примеры
alias ИМЯ | 1. Создаёт пустой алиас с именем ИМЯ и входит в режим редактирования ("quit" для выхода) 2. Если алиас уже существует — просто входит в режим редактирования В режиме редактирования можно просматривать содержимое ("list"), удалять строки ("delete") или выйти командой "quit". Поскольку "exit" может быть частью алиаса, используется специальная команда "quit". Все остальные команды просто добавляются в алиас как есть. Пример:
    esp32#>alias motor_off
    % Вход в режим редактирования алиаса. Для выхода — "quit"
    esp32-alias>pin 2 low 4 low
    esp32-alias>echo Setting pins 2 and 4 low
    esp32-alias>quit
    esp32#>
show alias [ИМЯ] | show alias — выводит список всех определённых алиасов show alias ИМЯ — показывает содержимое указанного алиаса Пример: список алиасов
    esp32#>show alias
    % Список алиасов:
    % 1. "motor_off"
    % 2. "motor_on"
    % 3. "fail_2"
    % 4. "test", пустой
    esp32#>
Пример: просмотр алиаса motor_on
    esp32#>sh al motor_on
    % Alias "motor_on":
    % 1: echo silent
    % 2: uart 0
    % 3: write Motor spin-up, wait...
    % 4: pin 20 high delay 3000
    % 5: write Done.\n
    % 6: pin 21 high 20 low
    % 7: write Pumping, wait...
    % 8: exit
    % 9: echo on
    % --- END ---
exec ИМЯ [ИМЯ1 ИМЯ2 ... ИМЯn] | Выполняет указанный алиас (или список алиасов) поочерёдно. Пример: выполнение алиаса motor_on
    esp32#>exec motor_on
    Motor spin-up, wait...Done.
    Pumping, wait...

## ⇮УСЛОВИЯ, СОБЫТИЯ И ДЕЙСТВИЯ: КОМАНДА "IF"

Команда if, как можно было понять из примера с баком воды в начале этого документа, предназначена для реагирования на события на пинах. Таких событий у нас два - нарастание напряжения (переход от логического 0 к логической 1, "rising") и падение напряжения ("1" → "0" , "falling"). Регистрация таких событий возложена на аппаратную часть - процессор генерирует прерывания, когда данные события происходят.

Простейшее условие, которое реагирует на момент падения напряжения на GPIO2, запуская какую-то реакцию\действие:

      esp32#>if falling 2 exec My_Alias

Изначальное условие, на которое реагирует шелл (rising или falling) может быть расширено дополнительными условиями: Добавим к условию срабатывания выше дополнительные условия: срабатывать только если GPIO4 находится в состоянии "0" а GPIO1 - в состоянии "1"

      esp32#>if falling 2 low 4 high 0 exec My_Alias

Эти дополнительные условия проверяются в момент срабатывания основного, и, если условия не совпадают с реальными значениями на пинах, то такое событие отбрасывается, а счетчик отброшенных событий увеличивается (см. команду "show if", колонка "Drops")

Можно ли вообще не указывать основное (rising/falling) событие, и что это будет означать?

Да, можно. Можно, например, написать следующее условие:

      esp32#>if low 2 exec My_Alias

Что произойдет в таком случае? ESPShell будет опрашивать состояние пина #2 раз в секунду, и, если условие совпадет, то будет выполнен алиас My_Alias. Такие типы условий называются переодическими или polling -условиями. Частоту опроса можно установить, указав ключевое слово poll:

      esp32#>if low 2 poll 10000 exec My_Alias ← опрашивать каждые 10 тысяч миллисекунд.

Ключевое слово poll имеет смысл только лишь для переодических условий. Если условие rising или falling, то параметр poll будет проигнорирован а на экран будет выведено соответствующее предупреждение.

Можно ли как-то ограничить количество раз, которое данное условие сработает? Да, для этого существует ключевое слово max-exec. Пример ниже ограничивает число срабатываний до пяти, а так же усложняет условие, добавив еще два пина:

      esp32#>if low 2 low 4 high 0 poll 10000 max-exec 5 exec My_Alias

Что будет происходить потом, после того, как условие отработает пять раз? Условие так и будет продолжать проверяться, но срабатывать не будет. Вместо этого будет увеличиваться счетчик отброшенных событий, который можно увидеть в выводе команды "show if" (в колонке Drops):

Рис. 2: вывод команды "show ifs", достигли ограничения max-exec

Обратите внимание на восклицательный знак рядом со счетчиком Hits, а так же на восклицательный знак (для монохромных мониторов): это указание на то, что условие выполнилось максимальное (max-exec) количество раз и более выполнятся не будет. Командой if clear (а так же every clear) можно "обнулить" счетчики условий, как если бы эти условия никогда не отрабатывали

При задании rising/falling условий следует обращать внимание на то, что эти условия проверяются в прерывании. Если никак не ограничить частоту прерываний, то мы рискуем захлебнуться в потоке событий и количестве запущенных команд My_Alias. Простой дребезг может сгенерировать тысячу прерываний за секунду, висящий пин ESP32, или какой-нибудь другой фактор приведут к тому, что изделие попросту повиснет.

Для борьбы с этой проблемой существует ключевое слово rate-limit (или просто rate, ключевые слова можно сокращать). У этого ключевого слова есть один параметр - расстояние, в миллисекундах, между срабатываниями условия. То-есть, указав rate-limit 500 мы гарантируем, что наше условие не будет срабатывать чем раз в пол-секунды (500 миллисекунд). События, которые будут приходить чаще, чем установленный лимит будут отбрасываться, а счетчик отброшенных событий - увеличиваться:

Рис. 2: Вывод команды "show ifs", rate-limit

Команда | Описание и примеры
if | if rising|falling PIN1 [low|high PINn]* [max-exec NUM] [rate-limit MILLIS] exec ALIAS_NAME Условие на событие rising или falling, с опциональными дополнительными low/high условиями: как только уровент на PIN1 начинает падать (или нарастать) таким образом, что это совпадет с условием - условие сработает Необязательный параметр max-exec задает максимальное количество срабатываний условия ("запускать не более, чем max-exec раз"); другой необязательный параметр, rate-limit, задает минимальный интервал, в миллисекундах, между срабатываниями условия. Пример: реагировать на падение напряжения на GPIO2, но только, если пины GPIO4 и GPIO0 находятся в состоянии "low":
      esp32#>if falling 2 low 0 low 4 exec my_action
Пример: то же, что и в примере выше, но ограничить частоту вызовов - не чаще одного раза в секунду
      esp32#>if falling 2 low 0 low 4 rate-limit 1000 exec my_action
if low|high PIN1 [low|high PINn]* [max-exec NUM] [poll MILLIS] exec ALIAS_NAME if delete [gpio] NUM if delete all if clear [gpio] NUM if clear all if enable NUM|all if disable NUMall

Так же можно написать условие и вовсе без условий: это будет простым переодическим событием, которое будет исполняться безусловно:

      esp32#>if poll 10000 exec My_Alias

Такая запись хоть и возможна, и она будет работать, не является правильным способом задать переодическое событие. Для переодических событий есть более удобная форма записи: команда every

## ⇮ПЕРЕОДИЧЕСКИЕ СОБЫТИЯ

Для задания переодического выполнения можно воспользоваться командой every: в качестве аргументов она принимает (помимо алиаса для выполнения) частоту выполнения и задержку первого выполнения

Если мы зададим такое простое переодическое условие,

      esp32#>every 1 day exec My_Alias

то первое выполнение произойдет сразу же, по нажатии на клавишу <Enter>, а вот последующие события будут происходить с интервалов в 24 часа. Как быть, если нам не подходит вариант с выполнением первого события сразу же? Можно использьзовать ключевое слово delay:

      esp32#>every 1 day delay 1 hour exec My_Alias

В таком варианте команды, первое выполнение произойдет через час, а все последующие - с интервалов в 24 часа.

Команда | Описание и примеры
every | every NUM milliseconds|seconds|minutes|hours|days [max-exec NUM] [delay MILLIS] exec ALIAS_NAME every delete NUM|all every clear NUM|all every enable NUM|all every disable NUMall TBD:..