Алиасы, или «командные алиасы» — это способ выполнения сразу нескольких команд оболочки, как в 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" для выхода) В режиме редактирования можно просматривать содержимое ("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 — выводит список всех определённых алиасов Пример: список алиасов 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, как можно было понять из примера с баком воды в начале этого документа, предназначена для реагирования на события на пинах. Таких событий у нас два - нарастание напряжения (переход от логического 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:.. |