Среда разработки ESP-IDF предоставляет компонент console, включающий в себя программные блоки, необходимые для разработки интерактивной консоли, работающей через последовательный порт. Этот компонент включает в себя следующие возможности:
• Редактирование строки, предоставляемое библиотекой linenoise. Это дает обработку клавиш backspace и стрелок, прокрутку истории команд (history), автозавершение команды (auto-completion) и подсказки по аргументам команды (argument hints). • Разделение командной строки на аргументы. • Парсинг аргументов, предоставляемый библиотекой argtable3. Эта библиотека включает API, используемое для парсинга аргументов командной строки в стиле GNU. • Функции для регистрации и диспетчеризации команд. • Функции для установки базового окружения REPL (Read-Evaluate-Print-Loop, т. е. цикл чтения ввода, оценка ввода, печать результатов выполнения команды).
Примечание: перечисленные возможности могут использоваться совместно или независимо друг от друга. Например, можно использовать функции редактирования строки и регистрации команд, но использовать для парсинга аргументов getopt или кустарный код вместо argtable3. Аналогично можно использовать более простые средства ввода команд (такие как fgets) вместе с остальными средствами для разделения команды и обработки аргументов.
[Редактирование строки]
Функция редактирования строки команды дает возможность пользователю создать команды путем ввода символов и их стирания клавишей Backspace, навигации по тексту команды клавишами влево/вправо, навигации по истории ранее введенных команд клавишами вверх/вниз, и выполнения автозавершения команды клавишей Tab.
Примечание: перечисленные функции редактирования основаны на поддержке последовательностей управляющих символов (ANSI escape sequence) в приложении терминала. Поэтому утилиты монитора последовательного порта, которые показывают сырые данные UART, не могут использоваться с библиотекой редактирования строки. Если на экране видны [6n или что-то похожее в ответ на escape-последовательность, когда запущен пример system/console вместо приглашения команд (например esp> ), то это значит, что утилита терминала не поддерживает escape-последовательности. Известно, что нормально работают программы GNU screen, minicom и idf_monitor.py (который можно запустить командой idf.py monitor из директории проекта). На операционной системе Windows с успехом может использоваться утилита терминала Putty.
Ниже дан краткий обзор библиотеки linenoise [2].
Конфигурация. Библиотека linenoise не требует явной инициализации. Однако некоторые значения конфигурации по умолчанию может понадобиться поменять перед использованием основного функционала редактирования строки.
Переключает консоль между однострочным и многострочным режимами редактирования сроки команды. В однострочном режиме (single line mode), если длина команды превышает длину строки на экране, терминала, то текст команды прокручивается влево, показывая конец команды (текст начала команды скрывается). Однострочный режим требует меньшего количества данных, отправляемых для обновления экрана с каждым нажатием на клавишу, так что редактирование строки вызывает меньше глюков по сравнению с многострочным режимом (multi line mode). Обратная сторона медали - в однострочном режиме редактирование и копирование текста команды сложнее, чем в многострочном. По умолчанию используется однострочный режим.
Устанавливает, как библиотека linenoise возвращает пустые строки - как строки нулевой длины (если true) или как NULL (если false). По умолчанию возвращаются строки нулевой длины.
Установит максимальную длину строки для библиотеки linenoise. По умолчанию предел в 4096 символов. Если необходимо оптимизировать расход RAM, то с помощью этой функции можно уменьшить эту величину.
В большинстве случаев консольные приложения имеют некоторую форму цикла чтения/оценки. Функция linenoise() обрабатывает нажатия на клавиши и возвращает завершенную строку команды, когда нажата клавиша Enter. Таким образом, она делает черную работу, обрабатывая часть "чтение" цикла (часть "оценка" выполняется в коде пользователя через зарегистрированные функции).
Когда пользователь нажимает клавишу Tab, библиотека запускает специальную функцию обратного вызова подсказки завершения команды (completion callback). Она должна проинспектировать содержимое введенной команды, сравнив его с зарегистрированным списком команд, и возвратить список возможных завершений путем вызова функции linenoiseAddCompletion(). Функция linenoiseSetCompletionCallback() должна быть вызвана, чтобы зарегистрировать этот completion callback, если желательно использовать эту функцию.
Компонент console предоставляет готовую подобную функцию для предоставления завершений зарегистрированных команд, esp_console_get_completion() (см. далее).
Всякий раз, когда меняется вводимая пользователем строка команды, библиотека linenoise запускает эту функцию обратного вызова формирования подсказок (hints callback). Эта callback-функция может инспектировать содержимое строки команд, введенное в настоящий момент, и предоставлять строку с подсказками (которые могут включать также и список аргументов команды, например). Затем библиотека отображает текст подсказки на той же строке, где происходит ввод, возможно другим цветом.
Если строка, которую возвратила hints callback, выделена в памяти динамически, или каким-либо образом требуется её повторное использование, то функция, которая выполняет такую очистку, должна быть зарегистрирована вызовом linenoiseSetFreeHintsCallback().
Устанавливает количество самых последних запоминаемых команд. Пользователи могут прокручивать историю введенных ранее команд клавишами стрелок вверх/вниз.
Библиотека linenoise не добавляет автоматически команды в историю команд. Вместо этого приложения должны сами вызывать эту функцию, чтобы добавить строку введенной команды в историю. Обычно добавление в историю делается после успешного или безошибочного выполнения команды, неправильно введенные команды в историю не добавляются.
Освобождает память, используемую для сохранения истории команд. Вызовите эту функцию, когда завершили работу с библиотекой linenoise.
[Разделение строки команд на аргументы]
Компонент console предоставляет функцию esp_console_split_argv() для разделения введенной строки команды на аргументы. Эта функция вернет количество найденных аргументов (argc), и заполнит массив указателей, который может быть передан в аргументе argv, в любую функцию, которая принимает аргументы в формате argc, argv.
Строка команды разделяется на аргументы по следующим правилам:
• Аргументы отделяются друг от друга пробелами. • Если требуются пробелы внутри аргументов, то они должны быть экранированы символом \ (backslash). • Другие escape-последовательности это \\ (приводит к символу backslash) и \" (дает символ двойной кавычки). • Аргументы могут быть заключены в двойные кавычки. В этом случае кавычки могут появляться только в начале и конце аргумента. Кавычки внутри аргумента должны быть экранированы через backslash, как было упомянуто выше. Функция esp_console_split_argv отбрасывает обрамляющие двойные кавычки аргумента.
Примеры разделения командной строки на аргументы:
abc def 1 20 .3 -> [ abc, def, 1, 20, .3 ]
abc "123 456" def -> [ abc, 123 456, def ]
`a\ b\\c\" -> [ a b\c" ]
[Парсинг аргументов]
Для анализа аргументов в компоненте console подключена библиотека argtable3 [3]. Вводное руководство см. в [4], примеры использования есть в репозитории Github [5].
[Регистрация команды и её обработка]
Компонент console содержит функции, которые обрабатывают регистрацию команд, проверяют введенные команды на совпадение с зарегистрированными, и вызывает обработку этих команд с аргументами, предоставленными в командной строке.
Приложение сначала инициализирует модуль регистрации команды вызовом esp_console_init(), и вызывает функцию esp_console_cmd_register() для регистрации обработчиков команд.
Для каждой команды приложение предоставляет следующую информацию (в форме структуры esp_console_cmd_t):
• Имя команды (строка без пробелов, идентифицирующая команду). • Текст help, объясняющий, что делает команда. • Опциональный текст подсказки hint, перечисляющий аргументы команды. Если приложение использует Argtable3 для парсинга аргументов, то текст hint может быть вместо этого сгенерирован автоматически, путем предоставления указателя на структуру определений аргументов. • Функция обработчика команды.
Также модуль регистрации команды предоставляет несколько других функций:
Эта функция берет строку команды, разделяет её на аргументы на список argc/argv с помощью esp_console_split_argv(), просматривает список зарегистрированных команд, и если найдено совпадение с зарегистрированной командой, то выполняет её обработчик.
Добавит команду help к списку зарегистрированных команд. Эта команда выведет список всех зарегистрированных команд вместе с их аргументами и текстом справки.
Callback-функция для использования вместе с linenoiseSetCompletionCallback() из библиотеки linenoise. Предоставляет завершения команды для linenoise, основываясь на списке зарегистрированных команд.
Callback-функция для использования вместе с linenoiseSetHintsCallback() из библиотеки linenoise. Предоставляет подсказки по аргументам для зарегистрированных команд.
[Инициализация окружения console REPL]
Для установки базового рабочего окружения REPL компонент console предоставляет несколько полезных API-функций. Далее описывается комбинирование этих функций.
В типовом приложении Вам только нужно вызвать esp_console_new_repl_uart() для инициализации рабочего окружения REPL на основе устройства UART [7], включая установку драйвера, базовую конфигурацию консоли, запуск потока для выполнения задачи REPL. После этого останется только зарегистрировать несколько полезных команд (например help), и далее можно зарегистрировать Ваши собственные команды вызовами функции esp_console_cmd_register(). Окружение REPL остается в состоянии инициализации до тех пор, пока не будет вызвана функция esp_console_start_repl().
[Пример приложения]
Как использовать компонент консоли, см. пример в папке system/console среди других примеров, поставляемых средой разработки ESP-IDF. Этот пример показывает, как инициализировать функции VFS [6] и UART, настроить библиотеку linenoise, прочитать и обработать команды из UART, и сохранить историю команд на память SPI flash. См. файл README.md в директории этого проекта для получения дополнительной информации.
Кроме этого в ESP-IDF есть несколько полезных примеров, использующих компонент console, которые можно считать "инструментами" (tools) при разработке приложений. Например, это peripherals/i2c/i2c_tools, wifi/iperf.
cmdline – строка команды (имя команды, за которой опционально могут идти аргументы). cmd_ret – [out] код возврата из команды (установится, если команда была запущена).
Возвращаемые значения:
ESP_OK успешное завершение. ESP_ERR_INVALID_ARG если строка команды пуста, или в ней только пробелы. ESP_ERR_NOT_FOUND если команда с этим именем не зарегистрирована. ESP_ERR_INVALID_STATE если не была вызвана функция esp_console_init.
Разделит строку команды на массив аргументов. В массиве argv будет возвращено не более чем argv_size - 1 аргументов. Указатель после последнего аргумента в массиве (т. е. argv[argc]) устанавливается в значение NULL.
Параметры:
line – указатель на буфер для парсинга; он модифицируется по месту. argv – массив, куда записываются указатели на аргументы. argv_size – количество элементов в массиве argv (максимальное возможное количество аргументов).
Функция возвратит количество аргументов, найденных в командной строке (argc).
Callback-функция, предоставляющая варианты завершения команды для библиотеки linenoise. Когда для редактирования строки используется linenoise, поддержка завершения команды может быть разрешена следующим образом:
Callback-функция, предоставляющие подсказки для библиотеки linenoise. Когда для редактирования строки используется linenoise, поддержка хинтов может быть разрешена следующим образом:
Необходимо дополнительное приведение типов, потому что linenoiseHintsCallback определена как возвращающая char* вместо const char*.
Параметры:
buf – строка, введенная пользователем. color – [out] код цвета ANSI, используемый для текста отображения хинта. bold – [out] устанавливается в 1, если хинт должен быть отображен жирным шрифтом.
Функция возвратит строку, содержащую текст хинта. Эта строка должна постоянно присутствовать в памяти, и не должна быть освобождена (например, не должна быть использована функция linenoiseSetFreeHintsCallback).
Внимание, эта функция предназначена для использования в примерах, чтобы сделать код более компактным. Приложения, использующие функции консоли, должны основываться на нижележащих функциях библиотек linenoise и esp_console.
Эта функция "все в одном", реализующая рабочее окружение, необходимое для REPL, включая следующее:
• Инсталлирует драйвер UART для консоли (с параметрами 8n1, 115200, источник тактирования REF_TICK). • Конфигурирует потоки stdin/stdout для прохождения через драйвер UART. • Инициализирует библиотеку linenoise. • Порождает новый поток для фоновой работы REPL.
Параметры:
dev_config – [in] конфигурация периферийного устройства UART. repl_config – [in] конфигурация REPL. ret_repl – [out] возвращаемый дескриптор REPL после успешного выполнения функции, иначе будет возвращен NULL.
Установит рабочее окружение REPL консоли поверх драйвера USB CDC.
Внимание, эта функция предназначена для использования в примерах, чтобы сделать код более компактным. Приложения, использующие функции консоли, должны основываться на нижележащих функциях библиотек linenoise и esp_console.
Эта функция "все в одном", реализующая рабочее окружение, необходимое для REPL, включая следующее:
• Инициализирует библиотеку linenoise. • Порождает новый поток для фоновой работы REPL.
Параметры:
dev_config – [in] конфигурация USB CDC. repl_config – [in] конфигурация REPL. ret_repl – [out] возвращаемый дескриптор REPL после успешного выполнения функции, иначе будет возвращен NULL.
Запускает рабочее окружение REPL. Когда REPL запущен, он не может быть остановлен, пока не будет вызвана repl->del(repl) для уничтожения окружения REPL.
Параметры:
repl – [in] дескриптор REPL, полученный из возврата esp_console_new_repl_xxx.
Возвращаемые значения:
ESP_OK успешное завершение. ESP_ERR_INVALID_STATE если REPL не запущен.
* @brief Предоставляет параметры для инициализации консоли.
*/
typedefstruct {
size_t max_cmdline_length; //!< длина буфера строки команды, в байтахsize_t max_cmdline_args; //!< максимальное количество аргументов в командной// строке для парсингаint hint_color; //!< код цвета ASCII для текста хинтаint hint_bold; //!< если тут 1, то hint печатается жирным шрифтом
} esp_console_config_t;
typedefstruct {
uint32_t max_history_len; //!< максимальная длина истории командconstchar*history_save_path; //!< путь до файла для сохранения истории,// NULL если не надо сохранятьuint32_t task_stack_size; //!< размер стека задачи REPLuint32_t task_priority; //!< приоритет задачи REPLconstchar*prompt; //!< приглашение командной строки (если NULL,// то по умолчанию приглашение "esp> ")size_t max_cmdline_length; //!< максимальная длина командной строки. Если 0,// то по умолчанию используется 4K байт.
} esp_console_repl_config_t;
typedefstruct {
int channel; //!< номер канала UART (начиная с 0)int baud_rate; //!< скорость обменаint tx_gpio_num; //!< номер ножки GPIO для TX, -1 означает использовать умолчаниеint rx_gpio_num; //!< номер ножки GPIO для RX, -1 означает использовать умолчание
} esp_console_dev_uart_config_t;
argc - количество аргументов. argv - массив указателей на строки аргументов команды, каждый элемент массива это указатель на ASCIIZ-строку, представляющую один аргумент команды.
Функция обработчика команды консоли возвратит в случае успеха 0.