Программирование ARM ESP-IDF: компонент консоли Tue, October 08 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

ESP-IDF: компонент консоли Печать
Добавил(а) microsin   

Среда разработки 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 не требует явной инициализации. Однако некоторые значения конфигурации по умолчанию может понадобиться поменять перед использованием основного функционала редактирования строки.

Очистит экран с помощью escape-последовательности, и установит курсор в левый верхний угол окна терминала.

Переключает консоль между однострочным и многострочным режимами редактирования сроки команды. В однострочном режиме (single line mode), если длина команды превышает длину строки на экране, терминала, то текст команды прокручивается влево, показывая конец команды (текст начала команды скрывается). Однострочный режим требует меньшего количества данных, отправляемых для обновления экрана с каждым нажатием на клавишу, так что редактирование строки вызывает меньше глюков по сравнению с многострочным режимом (multi line mode). Обратная сторона медали - в однострочном режиме редактирование и копирование текста команды сложнее, чем в многострочном. По умолчанию используется однострочный режим.

Устанавливает, как библиотека linenoise возвращает пустые строки - как строки нулевой длины (если true) или как NULL (если false). По умолчанию возвращаются строки нулевой длины.

Установит максимальную длину строки для библиотеки linenoise. По умолчанию предел в 4096 символов. Если необходимо оптимизировать расход RAM, то с помощью этой функции можно уменьшить эту величину.

Функции для главного цикла:

В большинстве случаев консольные приложения имеют некоторую форму цикла чтения/оценки. Функция linenoise() обрабатывает нажатия на клавиши и возвращает завершенную строку команды, когда нажата клавиша Enter. Таким образом, она делает черную работу, обрабатывая часть "чтение" цикла (часть "оценка" выполняется в коде пользователя через зарегистрированные функции).

Эта функция должна быть вызвана для освобождения буфера командной строки, полученного из функции linenoise().

Функции для подсказок и автозавершения команды:

Когда пользователь нажимает клавишу Tab, библиотека запускает специальную функцию обратного вызова подсказки завершения команды (completion callback). Она должна проинспектировать содержимое введенной команды, сравнив его с зарегистрированным списком команд, и возвратить список возможных завершений путем вызова функции linenoiseAddCompletion(). Функция linenoiseSetCompletionCallback() должна быть вызвана, чтобы зарегистрировать этот completion callback, если желательно использовать эту функцию.

Компонент console предоставляет готовую подобную функцию для предоставления завершений зарегистрированных команд, esp_console_get_completion() (см. далее).

Функция для вызова из completion callback, чтобы информировать библиотеку о возможных завершениях текущей введенной команды.

Всякий раз, когда меняется вводимая пользователем строка команды, библиотека linenoise запускает эту функцию обратного вызова формирования подсказок (hints callback). Эта callback-функция может инспектировать содержимое строки команд, введенное в настоящий момент, и предоставлять строку с подсказками (которые могут включать также и список аргументов команды, например). Затем библиотека отображает текст подсказки на той же строке, где происходит ввод, возможно другим цветом.

Если строка, которую возвратила hints callback, выделена в памяти динамически, или каким-либо образом требуется её повторное использование, то функция, которая выполняет такую очистку, должна быть зарегистрирована вызовом linenoiseSetFreeHintsCallback().

Функции истории команд:

Устанавливает количество самых последних запоминаемых команд. Пользователи могут прокручивать историю введенных ранее команд клавишами стрелок вверх/вниз.

Библиотека linenoise не добавляет автоматически команды в историю команд. Вместо этого приложения должны сами вызывать эту функцию, чтобы добавить строку введенной команды в историю. Обычно добавление в историю делается после успешного или безошибочного выполнения команды, неправильно введенные команды в историю не добавляются.

Функция сохраняет историю команд из RAM в текстовый файл, например на карту SD или в файловую систему на памяти flash.

Функция, комплементарная функции linenoiseHistorySave(), она загружает историю из файла.

Освобождает память, используемую для сохранения истории команд. Вызовите эту функцию, когда завершили работу с библиотекой 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.

[Справочник по API компонента console]

Заголовочный файл компонента консоли components/console/esp_console.h.

esp_err_t esp_console_init (const esp_console_config_t *config);

Инициализирует модуль консоли. Вызовите эту функцию один раз перед использованием других функций модуля консоли.

Параметры:

config – конфигурация консоли.

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_ERR_NO_MEM недостаточно памяти.
ESP_ERR_INVALID_STATE консоль уже инициализирована.
ESP_ERR_INVALID_ARG предоставлена некорректная конфигурация.

esp_err_t esp_console_deinit (void);

Отменяет инициализацию модуля консоли. Вызовите эту функцию, когда консоль больше не нужна.

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_ERR_INVALID_STATE консоль еще не инициализирована.

esp_err_t esp_console_cmd_register (const esp_console_cmd_t *cmd);

Регистрирует команду для консоли.

Параметры:

cmd – указатель на описание команды. Может указывать на временное значение.

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_ERR_NO_MEM недостаточно памяти.
ESP_ERR_INVALID_ARG если в описании команды есть недопустимые аргументы.

esp_err_t esp_console_run (const char *cmdline, int *cmd_ret);

Запустит командную строку.

Параметры:

cmdline – строка команды (имя команды, за которой опционально могут идти аргументы).
cmd_ret – [out] код возврата из команды (установится, если команда была запущена).

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_ERR_INVALID_ARG если строка команды пуста, или в ней только пробелы.
ESP_ERR_NOT_FOUND если команда с этим именем не зарегистрирована.
ESP_ERR_INVALID_STATE если не была вызвана функция esp_console_init.

size_t esp_console_split_argv (char *line, char **argv, size_t argv_size);

Разделит строку команды на массив аргументов. В массиве argv будет возвращено не более чем argv_size - 1 аргументов. Указатель после последнего аргумента в массиве (т. е. argv[argc]) устанавливается в значение NULL.

Параметры:

line – указатель на буфер для парсинга; он модифицируется по месту.
argv – массив, куда записываются указатели на аргументы.
argv_size – количество элементов в массиве argv (максимальное возможное количество аргументов).

Функция возвратит количество аргументов, найденных в командной строке (argc).

void esp_console_get_completion (const char *buf, linenoiseCompletions *lc);

Callback-функция, предоставляющая варианты завершения команды для библиотеки linenoise. Когда для редактирования строки используется linenoise, поддержка завершения команды может быть разрешена следующим образом:

linenoiseSetCompletionCallback(&esp_console_get_completion);

Параметры:

buf – строка, введенная пользователем.
lc – заполненная вариантами переменная linenoiseCompletions.

const char *esp_console_get_hint (const char *buf, int *color, int *bold);

Callback-функция, предоставляющие подсказки для библиотеки linenoise. Когда для редактирования строки используется linenoise, поддержка хинтов может быть разрешена следующим образом:

linenoiseSetHintsCallback((linenoiseHintsCallback*)&esp_console_get_hint);

Необходимо дополнительное приведение типов, потому что linenoiseHintsCallback определена как возвращающая char* вместо const char*.

Параметры:

buf – строка, введенная пользователем.
color – [out] код цвета ANSI, используемый для текста отображения хинта.
bold – [out] устанавливается в 1, если хинт должен быть отображен жирным шрифтом.

Функция возвратит строку, содержащую текст хинта. Эта строка должна постоянно присутствовать в памяти, и не должна быть освобождена (например, не должна быть использована функция linenoiseSetFreeHintsCallback).

esp_err_t esp_console_register_help_command (void);

Регистрирует команду help. По умолчанию команда help консоли выводит список команд вместе со строками хинтов и описаний команд.

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_ERR_INVALID_STATE если не была вызвана функция esp_console_init.

esp_err_t esp_console_new_repl_uart (const esp_console_dev_uart_config_t *dev_config,
                                     const esp_console_repl_config_t *repl_config,
                                     esp_console_repl_t **ret_repl);

Установит рабочее окружение REPL консоли поверх драйвера UART.

Внимание, эта функция предназначена для использования в примерах, чтобы сделать код более компактным. Приложения, использующие функции консоли, должны основываться на нижележащих функциях библиотек 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.

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_FAIL ошибка параметра.

esp_err_t esp_console_new_repl_usb_cdc (const esp_console_dev_usb_cdc_config_t *dev_config,
                                        const esp_console_repl_config_t *repl_config,
                                        esp_console_repl_t **ret_repl);

Установит рабочее окружение REPL консоли поверх драйвера USB CDC.

Внимание, эта функция предназначена для использования в примерах, чтобы сделать код более компактным. Приложения, использующие функции консоли, должны основываться на нижележащих функциях библиотек linenoise и esp_console.

Эта функция "все в одном", реализующая рабочее окружение, необходимое для REPL, включая следующее:

• Инициализирует библиотеку linenoise.
• Порождает новый поток для фоновой работы REPL.

Параметры:

dev_config – [in] конфигурация USB CDC.
repl_config – [in] конфигурация REPL.
ret_repl – [out] возвращаемый дескриптор REPL после успешного выполнения функции, иначе будет возвращен NULL.

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_FAIL ошибка параметра.

esp_err_t esp_console_start_repl (esp_console_repl_t *repl);

Запускает рабочее окружение REPL. Когда REPL запущен, он не может быть остановлен, пока не будет вызвана repl->del(repl) для уничтожения окружения REPL.

Параметры:

repl – [in] дескриптор REPL, полученный из возврата esp_console_new_repl_xxx.

Возвращаемые значения:

ESP_OK успешное завершение.
ESP_ERR_INVALID_STATE если REPL не запущен.

Структуры:

/**
 * @brief Предоставляет параметры для инициализации консоли.
 */
typedef struct {
   size_t max_cmdline_length; //!< длина буфера строки команды, в байтах
   size_t max_cmdline_args;   //!< максимальное количество аргументов в командной
                              //   строке для парсинга
   int hint_color;            //!< код цвета ASCII для текста хинта
   int hint_bold;             //!< если тут 1, то hint печатается жирным шрифтом
} esp_console_config_t;

/**
 * @brief Параметры REPL консоли (Read Eval Print Loop)
 */
typedef struct {
   uint32_t max_history_len;      //!< максимальная длина истории команд
   const char *history_save_path; //!< путь до файла для сохранения истории,
                                  //   NULL если не надо сохранять
   uint32_t task_stack_size;      //!< размер стека задачи REPL
   uint32_t task_priority;        //!< приоритет задачи REPL
   const char *prompt;            //!< приглашение командной строки (если NULL,
                                  //   то по умолчанию приглашение "esp> ")
   size_t max_cmdline_length;     //!< максимальная длина командной строки. Если 0,
                                  //   то по умолчанию используется 4K байт.
} esp_console_repl_config_t;

/**
 * @brief Параметры интерфейса UART консоли
 */
typedef struct {
   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;

Пока что эта структура пустая, она зарезервирована для использования в будущем.

/**
 * @brief Описание команды консоли
 */
typedef struct {
    /**
     * Имя команды. В нем не должно быть пробелов, и в этом параметре
     * не должен быть NULL. Указатель должен быть валидным до момента
     * вызова функции esp_console_deinit.
     */
    const char *command;
    /**
     * Текст помощи по команде, который покажет команда help.
     * Если этот параметр установлен, то он должен оставаться валидным
     * до момента вызова функции esp_console_deinit.
     * Если этот параметр не установлен (NULL), то помощь по команде
     * не будет отображена.
     */
    const char *help;
    /**
     * Текст подсказки (хинт), обычно перечисляет возможные аргументы
     * команды. Если этот параметр установлен в NULL, и поле argtable
     * не NULL, то хинт будет сгенерирован автоматически.
     */
    const char *hint;
    /**
     * Указатель на функцию, которая реализует действия команды.
     */
    esp_console_cmd_func_t func;
    /**
     * Массив или структура указателей на структуры arg_xxx, может быть NULL.
     * Используется для генерации текста хинта, если hint установлен в NULL.
     * Массив/структура, на который указывает это поле, должен завершаться
     * arg_end. Используется только на время вызова esp_console_cmd_register.
     */
    void *argtable;
} esp_console_cmd_t;

/**
 * @brief Базовая структура REPL
 */
struct esp_console_repl_s {
    /**
     * @brief Удалит окружение консоли REPL
     * @param[in] repl дескриптор REPL, возвращенный из esp_console_new_repl_xxx
     * @return
     *      - ESP_OK в случае успеха
     *      - ESP_FAIL в случае ошибок
     */
    esp_err_t (*del)(esp_console_repl_t *repl);
};

[Макросы]

ESP_CONSOLE_CONFIG_DEFAULT()
Значение конфигурации по умолчанию для консоли.

ESP_CONSOLE_REPL_CONFIG_DEFAULT()
Значение конфигурации по умолчанию для REPL консоли.

ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT()
Значение конфигурации по умолчанию для порта UART консоли.

ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT()
Значение конфигурации по умолчанию для порта USB CDC консоли.

[Определение обработчика команды]

typedef int (*esp_console_cmd_func_t)(int argc, char **argv);

Параметры:

argc - количество аргументов.
argv - массив указателей на строки аргументов команды, каждый элемент массива это указатель на ASCIIZ-строку, представляющую один аргумент команды.

Функция обработчика команды консоли возвратит в случае успеха 0.

[Ссылки]

1. ESP-IDF Console Component site:docs.espressif.com.
2. antirez / linenoise site:github.com.
3. What is Argtable site:argtable.org.
4. argtable: обработка командной строки.
5. argtable / argtable3 site:github.com.
6ESP VFS: виртуальная файловая система.
7ESP32 UART.

 

Добавить комментарий


Защитный код
Обновить

Top of Page