Перейти к содержимому

ESP_LOG или Отладка через UART

Добрый день, уважаемый читатель! Сегодня поговорим о библиотеках – логгерах, основное назначение которых – отладка кода через текстовый монитор порта UART (его ещё иногда называют просто “монитор” или “монитор порта” или “терминал” или даже “консоль”).

Программисты “взрослых” настольных систем. как правило, имеют достаточно обширный инструментарий для отладки своего кода – это и всевозможные отладчики, и точки останова и т.д. и т.п. Программисты встраиваемых систем лишены почти всего этого, так как код выполняется на стороннем микроконтроллере и с компьютера невозможно выполнять непосредственное управление его потоком.

Самый простой, логичный и не требующий дополнительных затрат способ отладки – через вывод текстовой информации в основной COM-порт микроконтроллера. Затем с компьютера можно принять эти данные, вывести на экран, и по содержимому сообщений можно как-то понять, что происходит внутри микроконтроллера.

Вывод в терминал из тестового проекта. Ссылка на проект в конце статьи

Вывод в терминал из тестового проекта. Ссылка на проект в конце статьи

Конечно, я в курсе, что существуют специальные платы – отладчики JTAG, но они обычно стоят существенно дороже, да и не всегда применимы, особенно в устройствах постоянного пользования. Применяются такие платы обычно профессиональными разработчиками на этапе проектирования систем, а у любителей встречаются гораздо реже.

Давайте разберемся с библиотеками отладки на ESP32 (и не только для ESP-IDF, а для Arduino тоже).

 


Зачем нужны какие-то библиотеки?

Если вы начинали свое знакомство с микроконтроллерами в Arduino IDE, то, наверное помните, что для отладки через UART используется объект Serail:

Источник: https://alexgyver.ru/lessons/serial/

Источник: https://alexgyver.ru/lessons/serial/

Serial – это в общем то и есть универсальная библиотека текстовой отладки для Arduino IDE. И всё, в общем-то просто, и понятно. И удобно пользоваться. Если бы не одно но…

Предположим одну вполне обычную ситуацию: вы собираете какое-то устройство. Пишете для него “прошивку”, расставляя везде и всюду те самые Serial.println(). Затем, посчитав что всё хорошо и всё работает как надо, упаковываете приборчик в корпус и отправляете его на постоянное место работы. Нужна вам теперь отладка? Нет! А она есть (ну прямо как суслик). Полезные при отладке, но бесполезные при постоянной работе Serial.println() и иже с ними попусту тратят процессорное время, стек, память, и увеличивают размер двоичного файла.

Ха! – скажете вы, самый простой вариант в этом случае – закоментировать отладочный вывод перед финальной прошивкой. Отлично! Потратив энное количество времени, вы успешно вылавливаете весь отладочный вывод в терминал и удаляете его. Компилируете оптимизированный таким образом код, радуетесь уменьшению его размера, заливаете в устройство….. Но проходит пару дней-недель-месяцев, и устройство иногда начинает вести себя странно. К вам закрадывается мысль, что вы где-то немного накосячили, но вот где? Отладочный вывод вам бы сейчас очень пригодился, но…. вам придется его восстанавливать – раскоментировать или вводить заново.

В этом случае логично использовать макросы препроцессора для выключения отладки “по условию”:

ESP_LOG или Отладка через UART

Этот прием отлично работает для отладочных сообщений в основном коде. Но плохо подходит для отладки в библиотеках, так как макрос LOG_ENABLED нужно определить так, чтобы о нем было известно компилятору до начала компиляции всех библиотек.

А ещё бы желательно, чтобы можно было выбрать “уровень” отладки – например “только ошибки” или “все подряд, чтобы мало не показалось”… Но не стоит изобретать велосипед, всё давно уже придумано до нас. В esspessif давно уже озаботились этой проблемой и создали специальную библиотеку логирования, которая доступна не только в Espressif IoT Development Framework, но и в ESP32 Arduino Framework (но с некоторыми ограничениями). Называется она esp_log.h – прошу любить и жаловать…

 


Использование esp_log.h

Esp_log – это одна из базовых библиотек ESP-IDF, и она “автоматически” доступна как из всей ESP-IDF, так и из ваших приложений. Для того, чтобы начать пользоваться ей в ваших программах, просто добавь воды просто подключите её посредством #include “esp_log.h”.

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

Библиотека логирования предоставляет нам несколько стандартных функций (на самом деле это макросы препроцессора, но нам не суть важно) с разными уровнями детализации событий:

  • ESP_LOGE – ошибка (минимальный уровень детализации)
  • ESP_LOGW – предупреждение
  • ESP_LOGI – информация
  • ESP_LOGD – отладочные сообщения
  • ESP_LOGV – всё подряд (максимальный уровень детализации)

Все эти функции имеют одинаковый шаблон ESP_LOGx (tag, format, …)

где:

  • tag – текстовая метка модуля / библиотеки, из которой был сделан вызов ESP_LOGx. Служит для облегчения идентификации сообщений в общем потоке монитора терминала.
  • formatстрока форматирования (подробнее ниже).
  • – параметры форматирования (не обязательно).

Существуют специальные версии для специальных случаев:

  • ESP_EARLY_LOGx – для вызова из стартового кода. Я, честно, говоря, пока плохо представляю, что такое “стартовый” код. Но жираф большой, ему видней.
  • ESP_DRAM_LOGx – для вызова из прерываний. Если вы попытаетесь вызывать обычную вервию лога из обработчика прерываний, то получите перезагрузку микроконтроллера по WDT – будьте внимательны.

Используются они похожим образом, поэтому отдельно рассматривать смысла нет никакого.

 

TAG – текстовая метка модуля

Как я уже упомянул, тег (метка) служит для упрощения навигации по сообщениям в общем потоке.

Я выделил теги не во всех строках, просто мне так удобнее

Я выделил теги не во всех строках, просто мне так удобнее

Я рекомендую придерживаться рекомендация справочной системы и объявлять тег следующим образом:

static const char* TAG = “MyModule“;

Можно, конечно, не выпендриваться, а написать просто

ESP_LOGx (“MyModule”, “Text, text, text”);

Также будет работать. Но я настоятельно не рекомендую так делать. Более того, исходя из опыта – я рекомендую использовать во всех библиотеках и файлах всегда использовать одно и то же имя переменной – TAG или logTAG – не важно, важно чтобы везде оно было одно и то же. Это сэкономит вам кучу времени, когда вы будете копипастить эти логи из одного файла в другой. Объявление константы в одном месте подправили – и дело в шляпе.

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

Форматирование сообщений

Если взглянуть на второй параметр функций ESP_LOGx, то становится ясно, что тут может быть только строка const char* и ничего более. А как же числа – спросите вы? В ардуине Serial предоставляет гораздо больше методов – возможностей. Что же теперь, всегда предварительно преобразовывать числа в текст? Совсем нет!

Дело в том, что в при выводе строк в логах используются форматные стандартные Си функции vprintf и иже с ними. Это гораздо удобнее, чем пользоваться объектом Serial. Например, для вывода в лог в одной строке сообщения и числа, нам бы пришлось использовать 2-3 команды:

Пример для Arduino

Пример для Arduino

либо вручную преобразовывать значения и “собирать” строку:

Пример для Arduino

Пример для Arduino

В ESP-IDF это делается гораздо короче и удобнее:

Пример форматного вывода для ESP-IDF

Пример форматного вывода для ESP-IDF

Если вы ранее пользовались форматным выводом, то, наверное, понимаете, насколько это удобно. Со спецификациями форматирования строк вы можете ознакомиться по ссылке: cинтаксис спецификации формата: функции printf и wprintflearn.microsoft.com

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

Предупреждение! vprintf любит кушать много ресурсов и стека, поэтому даже однократное использование esp_log в задаче приводит к необходимости значительного увеличения стека (без логирования – от 1024 байт, с логированием – от 2048 байт). Кроме того, библиотечные vprintf – довольно медленные, поэтому применение ESP_LOGx в обработчиках прерываний скорее всего приведет к краху. Вместо этого в обработчиках прерываний следует использовать специальную версию логов – ESP_DRAM_LOGx, в которой применяется “облегченная” версия vprintf, но с меньшими возможностями.

 


Выбор уровня детализации

Теперь давайте посмотрим, как изменить уровень детализации логов, то есть отключить или включить заново какие-либо отладочные сообщения.

Можно установить один из следующих уровней детализации:

  • ESP_LOG_NONEсообщения не выводятся, совсем
  • ESP_LOG_ERRORкритические ошибки, программный модуль не может восстановиться самостоятельно
  • ESP_LOG_WARNошибки, при которых были приняты меры по восстановлению работоспособности
  • ESP_LOG_INFOинформационные сообщения, описывающие нормальный ход событий
  • ESP_LOG_DEBUGдополнительная информация, которая не требуется для нормального использования (значения, указатели, размеры и т.д.)
  • ESP_LOG_VERBOSEбольшие фрагменты отладочной информации или частые сообщения, которые потенциально могут затопить вывод

Библиотека esp_log предоставляет два способа выбора уровня детализации журнала:

  • Во время компиляции: в menuconfig установите уровень детализации с помощью опции CONFIG_LOG_DEFAULT_LEVEL (уровень по умолчанию). О том, как настроить menuconfig, читайте в статье: настройка ESP-IDF проекта.

ESP_LOG или Отладка через UART

  • При желании также в menuconfig установите максимальный уровень детализации с помощью опции CONFIG_LOG_MAXIMUM_LEVEL. По умолчанию это то же самое, что и CONFIG_LOG_DEFAULT_LEVEL, но его можно установить выше, чтобы добавить в прошивку больше необязательных сообщений.
  • Можно переопределить уровень детализации по умолчанию для отдельных модулей или файлов, используя макрос LOG_LOCAL_LEVEL. Его можно определить прямо в файле перед подключением esp_log.h:

ESP_LOG или Отладка через UART

При этом все отладочные сообщения с уровнем детализации выше заданного в CONFIG_LOG_MAXIMUM_LEVEL, будут игнорированы при компиляции и не попадут в прошивку. Профит!

 

  • Во время выполнения: все журналы для уровней детализации ниже CONFIG_LOG_DEFAULT_LEVEL включены по умолчанию. Для установки уровня детализации для каждого модуля отдельно можно использовать функцию esp_log_level_set(). Для этого используется тег, который мы обсудили выше.

ESP_LOG или Отладка через UART

Обратите внимание, что эта функция не может поднять уровень журнала выше уровня, установленного с помощью параметра CONFIG_LOG_MAXIMUM_LEVEL в menuconfig. Чтобы поднять уровень журнала выше уровня по умолчанию для данного файла, определите LOG_LOCAL_LEVEL, прежде чем включать esp_log.h в этот файл.

 


Оформление логов разными цветами

В ESP-IDF можно включить “раскрашивание” сообщений разными цветами в зависимости от их уровня.

Пример раскрашивания сообщений разного уровня

Пример раскрашивания сообщений разного уровня

Для этого в menuconfig задействуйте опцию Use ANSI terminal colors in log output:

ESP_LOG или Отладка через UART

После перекомпиляции проекта теоретически вы должны увидеть цветные строчки в вашем терминале. Но! Не всё так просто, оказывается.

Во первых, на PlatformIO по умолчанию цветные логи вы не увидите – вместо них будут спецсимволы в начале и конца сообщений:

Вот эти непонятные символы в начале и конце каждой строки и есть команды установки цвета текста

Вот эти непонятные символы в начале и конце каждой строки и есть команды установки цвета текста

Чтобы включить поддержку раскрашивания сообщений в VSCode + PlatformIO, вам потребуется добавить опцию direct в monitor_filters:

ESP_LOG или Отладка через UART

Во-вторых – на Windows 7 вывод цветных сообщений в VSCode + PlatformIO не работает. От слова совсем. То есть если вы ещё работаете на устаревшей ОС – цветные логи можно увидеть только через PUTTY, например.


Дополнительные опции PlatformIO

В дополнение к стандартным функциям ESP-IDF PlatformIO позволяет применять несколько фильтров к потокам ввода и вывода монитора устройства с помощью команды или параметра monitor_filters.

ESP_LOG или Отладка через UART

Давайте рассмотрим подробнее:

  • default – Удалить типичные коды управления терминалом из ввода
  • colorize – Применение разных цветов для полученных сообщений. Несмотря на название, это почему-то не работает
  • debug – Распечатывать отправленное и полученное
  • direct – Ничего не делать: пересылать все данные без обработок
  • hexlify – Показать шестнадцатеричное представление данных (код для каждого символа)
  • log2file – Записывать данные в файл «platformio-device-monitor-%date%.log», расположенный в текущем рабочем каталоге
  • nocontrol – Удалить все контрольные коды, в т.ч. CR+LF
  • printable – Показать десятичный код для всех символов, отличных от ASCII, и заменить большинство управляющих кодов
  • time – Добавить временную метку с миллисекундами для каждой новой строки
  • send_on_enter – Отправить текст на устройство на ENTER
  • esp32_exception_decoder – Пользовательский фильтр для Espressif 32, который декодирует адреса возникновения исключений
  • esp8266_exception_decoder – Пользовательский фильтр для Espressif 8266, который декодирует адреса возникновения исключений

Самым полезными для меня представляются фильтры direct, log2file и esp32_exception_decoder.

Фильтр direct я уже применял выше.

Фильтр log2file я часто использую, когда требуется отловить ошибку, которая проявляется очень редко (например раз в сутки) – подключаю ноут к устройству, включаю сохранение в файл и оставляю включенным.

Фильтр esp32_exception_decoder представляется достаточно удобным и полезным, но “гладко было на бумаге, но забыли про овраги”… Дело в том, что этот фильтр достаточно сильно влияет на скорость вывода сообщений в мониторе порта. Поэтому пришлось от него отказаться. Как я дешифрую адреса исключений, я рассказывал в одной из других статей: расшифровка адресов backtrace в коммандной строке windows

 


Особенности применение esp_log на ESP32 Arduino Framework

Практически всё то же самое относится и к ESP32 Arduino Framework – с одним единственным исключением – в ESP32 Arduino Framework не доступен menuconfig. То есть вы не сможете самостоятельно изменить уровень отладки по умолчанию через меню. Но есть обходной путь – через использование файла defconfig.common. Как это сделать, читайте в статье справки: Compile Arduino libs with ESP_LOGx.


Моя библиотека rLog

В своих проектах я пока использую свою библиотеку, которая называется rLog. Её вы можете посмотреть на GitHub-е.

По сути, это сильно упрощенная версия esp_log без возможности изменения уровня отладки во время выполнения. Только через файл конфигурации.

Для чего она мне нужна? На самом деле сейчас это уже довольно спорный вопрос, я уже сам сомневаюсь в необходимости её использования.

Дело в том, что несколько лет назад, когда я начинал знакомство с PlatformIO, мне очень понравилась esp_log, но тогда я ещё продолжал программировать и под Arduino-контроллеры. А на Arduino, она, разумеется, не работает. Вот я и “скрестил ежа и ужа”, создав свой клон esp_log.h с возможностью применения на других микроконтроллерах.

Потом я постепенно понял, что не хочу более “распыляться” по разным платформам и MCU, и сосредоточил все свои усилия на ESP32 и ESP-IDF. Но сейчас rLog “защита” почти во всех моих библиотеках и проектах, поэтому скорее – по традиции. Так что, вероятнее всего, в проектах для ESP-IDF я постепенно заменю её на “стандартную” версию, а rLog останется только для проектов Arduino.

Её использование практически ничем не отличается от стандартной esp_log, только нужно использовать другие макросы:

ESP_LOG или Отладка через UART

Да, я знаю, что макросы принято писать ПРОПИСНЫМИ БУКВАМИ, но банально лень переделывать.

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

rlog_e("WiFi", "Не удалось подключиться к точке доступа %s, код ошибки %d", ap_name, err);

получим сообщение:

12:34:10 [E] WiFi: Не удалось подключиться к точке доступа test, код ошибки -201

То есть временная метка вынесена в начало строки, а тип сообщения – уже после.

Настройки уровня детализации сделаны по аналогии:

ESP_LOG или Отладка через UART

Задать его можно с помощью макроса CONFIG_RLOG_PROJECT_LEVEL. Если вас заинтересует этот проект, вы можете подробнее познакомиться с ним на GitHub-е, ссылка на который находится ниже.

 


Ссылки

Как всегда, скачать тестовый проект можно по ссылке:

На этом пока всё, до встречи на сайте и на dzen-канале!


Пожалуйста, оцените статью:
[ 0 из 5, всего 0 оценок ]

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *