ESPShell for Arduino :: Basic concepts and command index

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

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

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

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

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

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

Water Tank Scheme
Рис. 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):

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

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

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

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

show ifs command output
Рис. 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:..