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

rLog :: универсальный логгер для ESPxx и Arduino

Представляю Вашему вниманию еще одну маленькую библиотечку. Для чего она нужна?

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

HTTPClient https;
if (https.begin(wifiClient, httpRequest)) { 
  int httpCode = https.GET();
  if (httpCode > 0) {
   if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
     result = true;
     Serial.printf("TLG :: Send \"%s\": OK (%d)\n", message.c_str(), httpCode);
   }
   else {
    Serial.printf("TLG :: Send \"%s\": FAILED (%d) %s\n", message.c_str(), httpCode, https.errorToString(httpCode).c_str());
   };
  } 
  else {
    Serial.println(F("TLG :: Send FAILED: Unable to connect!"));
  };
  https.end();
} 
else {
 Serial.println(F("TLG :: Send FAILED: Unable to create HTTPS client!"));
};

Приведенный выше код содержит множество отладочных Serial.println(…), позволяющих через COM-порт определить, на каком этапе что пошло не так. Все это просто и хорошо, но ровно до того момента, когда устройство оказывается полностью отлажено и собрано, то есть готово “в продакшен”. В “боевой” обстановке устройство обычно работает вдали от компьютера и вывод в serial только мешает – занимает и процессорные ресурсы, и память. Хорошо бы иметь возможность отключать вывод отладочных сообщений, а при возможных доработках проекта – включать.

Именно такой принцип заложен в библиотеку, которая “встроена” в Framework Arduino для ESP32: “esp32-hal-log.h” (в фреймворке ESP-IDF есть более “навороченная” версия – “esp_log.h”). Причем отключать “отладку” можно “частями” – чем ниже заданный уровень – тем меньше сообщений Вы увидите. Например: ничего, только ошибки, ошибки + предупреждения, ошибки + предупреждения + сообщения и т.д. Большая часть построена на макросах, а это означает, что сообщения выше заданного уровня будут полностью исключены из кода. Очень удобно. И достаточно просто.

 


Так зачем же еще изобретать велосипед? Есть несколько “но”:

  • Во-первых, включив уровень “DEBUG” в настройках проекта, Вы увидите не только “свои” сообщения, но и кучу отладочных сообщений из всех подключенных библиотек фреймворка. Хотелось бы как-то разделить все-таки. То есть подавить “системные” сообщения, оставив только нужные.
  • Во-вторых, эти библиотеки не будут работать на ESP8266, и уж тем более на AVR. А из-за этого код какой-нибудь “своей” библиотеки перестает работать на другом контроллере только из-за отладки. Конечно, можно нагородить леса типа с помощью макросов #if … #endif, но это сильно загромождает код и усложняет написание программы. Хотелось бы какого-то однообразия.
  • Ну и наконец – Вы не сможете изменить настройки sdkconfig.h, если используется только “framework-arduinoespressif32”, он поставляется уже сконфигурированным. То есть Вы не сможете самостоятельно изменить уровень отладки.

Исходя из этого мне ничего не оставалось, кроме как самым наглейшим образом передрать код “esp32-hal-log.h”, портировать его на ESP8266 и AVR, и сделать отдельные настройки для всего этого. Вот так и получилась эта библиотека. Использовать ее можно на ESP8266, ESP32, AVR и других контроллерах, в Arduino IDE и PlatformIO, в том числе в фреймворке ESP-IDF.

Библиотека бесплатная, Open source, скачать ее можно с GitHub с помощью кнопки ниже.

Как использовать

Есть несколько уровней отладочных сообщений:

  • RLOG_LEVEL_NONE – Сообщения не выводятся
  • RLOG_LEVEL_ERROR – Критические ошибки, программный модуль не может восстановиться самостоятельно
  • RLOG_LEVEL_WARN – Состояния ошибки, из которых были приняты меры по устранению
  • RLOG_LEVEL_INFO – Информационные сообщения, описывающие нормальный ход событий
  • RLOG_LEVEL_DEBUG – Дополнительная информация, которая не требуется для нормального использования (значения, указатели, размеры и т.д.).
  • RLOG_LEVEL_VERBOSE – Большие фрагменты отладочной информации или частые сообщения, которые потенциально могут переполнить вывод.

Им соответствуют несколько функций для вывода этих сообщений:

  • rlog_e(tag, format, …) – Сообщения уровня RLOG_LEVEL_ERROR
  • rlog_w(tag, format, …) – Сообщения уровня RLOG_LEVEL_WARN
  • rlog_i(tag, format, …) – Сообщения уровня RLOG_LEVEL_INFO
  • rlog_d(tag, format, …) – Сообщения уровня RLOG_LEVEL_DEBUG
  • rlog_v(tag, format, …) – Сообщения уровня RLOG_LEVEL_VERBOSE

где: tag – идентификатор, с помощью которого удобно обозначать модуль, откуда пришло сообщение (например “mqtt” или “telegram”) format – собственно сообщение с символами форматирования (или без них) – переменные, передаваемые в строку форматирования (например “Value = %d\n”, 10). Их может и не быть вовсе.

Соответственно, чем выше уровень отладки в Вашем проекте, тем больше сообщений Вы увидите.
Уровень отладки задается с помощью определения #define CONFIG_RLOG_PROJECT_LEVEL RLOG_LEVEL_XXXX, где RLOG_LEVEL_XXXX – этот самый уровень и есть. Кроме этого, есть возможность включить вывод системного времени и цветных ASCII-кодов в сообщения. Эти опции также включаются с помощью макросов.
Например при CONFIG_RLOG_PROJECT_LEVEL == RLOG_LEVEL_INFO в код попадут только макросы-функции rlog_e, rlog_w, rlog_i, а остальные будут игнорированы.
Для CONFIG_RLOG_PROJECT_LEVEL == RLOG_LEVEL_DEBUG будут отображаться ещё и rlog_d.
А при CONFIG_RLOG_PROJECT_LEVEL == RLOG_LEVEL_NONE все отладочные сообщения будет исключены.

Настройка уровня вывода

Осталось придумать, как определить этот самый CONFIG_RLOG_PROJECT_LEVEL

Если весь Ваш код содержится в одном файле – то с этим не будет никаких проблем. Просто добавьте воды #define CONFIG_RLOG_PROJECT_LEVEL RLOG_LEVEL_INFO в начале файла и проблема будет решена.

Сложнее, если Вы будете дробить код по разным файлам. В этом случае целесообразно вынести определения макросов в отдельный файл, например “project_config.h”. Если все файлы проекта находятся в одной папке, то проблем не возникнет.

Проблема будет, если Вы будете подключать какие-то библиотеки, которые находятся в каталогах, отличных от каталога проекта (общие библиотеки, которые используется в нескольких проектах одновременно). В этом случае компилятор при сборке не найдет Ваш “project_config.h”. Нужно ему подсказать, где искать. В PlatformIO это можно сделать с помощью опции -Iкаталог, например так:

[env]
build_flags = -Isrc 

Вуаля. При компиляции библиотек компилятор обязательно заглянет в каталог src, найдет Ваш файл “project_config.h” и подключит его к коду.

А вот в Arduino IDE я не нашел другого выхода, кроме как свалить все в одну кучу и хорошенько перемешать, то есть скопировать все библиотеки в каталог проекта. Впрочем, я не сильно и старался.

 


Примеры

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

#include "Arduino.h"
#include "project_config.h"
#include "rLog.h"

void setup() {
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println();

  rlog_v("DEMO", "Test message, level: %s", "VERBOSE");
  rlog_d("DEMO", "Test message, level: %s", "DEBUG");
  rlog_i("DEMO", "Test message, level: %s", "INFORMATION");
  rlog_w("DEMO", "Test message, level: %s", "WARNING");
  rlog_e("DEMO", "Test message, level: %s", "ERROR");
}

void loop() {
  // put your main code here, to run repeatedly:
  // nothing
}

Результаты компиляции для различных платформ:

Arduino Nano + Framework Arduino

Результаты компиляции для Arduini Nano + Framework Arduino

Тестовый вывод для Arduini Nano + Framework Arduino

ESP8266 + Framework Arduino

Результаты компиляции для ESP8266 + Framework Arduino

Тестовый вывод для ESP8266 + Framework Arduino

ESP32 + Framework Arduino

Результаты компиляции для ESP32 + Framework Arduino

Тестовый вывод для ESP32 + Framework Arduino

ESP32 + Framework ESP-IDF

Результаты компиляции для ESP32 + Framework ESP-IDF

Тестовый вывод для ESP32 + Framework ESP-IDF


 


Скачать библиотеку:

Библиотека бесплатная, Open source, скачать ее можно с GitHub с помощью кнопки ниже. В архиве Вы также найдете примеры скетча для Arduino IDE и PlatformIO – загляните в папку examples.

Значок
rLog :: библиотека и демо-проекты для Arduino & PlatformIO

Открыть на GitHub


✳️ Другие мои библиотеки для PlatformIO и ArduinoIDE

Благодарю за внимание.


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

9 комментариев для “rLog :: универсальный логгер для ESPxx и Arduino”

  1. Валентин

    Отличная библиотека, взял на вооружение.
    Но как напечатать ip адрес, маску, mac – адрес?

    1. Добрый день. Зависит от того, откуда вы берете данные.
      Например можно так:

      esp_netif_ip_info_t local_ip = wifiLocalIP();
      if (local_ip.ip.addr != 0) {
      uint8_t * loc_ip = (uint8_t*)&(local_ip.ip);
      rlog_d(logTAG, “%d.%d.%d.%d”, loc_ip[0], loc_ip[1], loc_ip[2], loc_ip[3]);
      }

  2. Столкнулся со странной проблемой. Никак не хочет библиотека показывать число типа float.
    Переменная “a” несколько раз преобразуется по ходу вычислений. Вывожу лог:
    rlog_d(“func()”, “1 a = %f”, a);
    Serial.print(“1 a = “);Serial.println(a);

    00:00:00 [D] func() :: 1 a = ?
    1 a = 9108906.00
    00:00:00 [D] func() :: 2 a = ?
    2 a = -9.00
    00:00:00 [D] func() :: 3 a = ?
    3 a = -0.09
    Arduino IDE 1.8.19, плата Arduino Nano (Atmega 328p)

    1. Хм, много раз выводил float и double – никаких проблем не наблюдалось. Может у вас все таки не float, а что-то иное?

    1. Serial.print использует принципиально другой метод преобразования числа в строку. Они НЕ МОГУТ быть одинаковыми.

      Данная библиотека использует универсальную форматную vsnprintf, которая, кстати, входит в стандарт Си – https://learn.microsoft.com/ru-ru/cpp/c-runtime-library/vprintf-functions?view=msvc-160
      Я подозреваю, что для вашего МК она может быть реализована криво – вопросы не ко мне, а к производителю фреймворка для вашего МК. На ESP32 она работает отлично.

      Я так же подозреваю, что вы использовали какой-то недопустимый тип данных под переменную. Serial.print пофиг на типы данных, она проглотит все. А вот vsnprintf – далеко не без разницы.

      Я сотни раз использовал вывод чисел с плавающей запятой – и все всегда работало. Впрочем, у меня сейчас даже нет указанного вами контроллера, чтобы проверить ваши слова – я все раздал друзьям или использовал в проектах. Остались только ESP. Да и нет там разницы особой – можете сами заглянуть в исходники:

      // And finally we can print a message
      #if __RLOG_SERIAL__
      Serial.print(temp);
      #else
      printf(“%s”, temp);
      #endif

  3. Я не понимаю, что такое “недопустимый тип данных”. Ну вот кусок кода, который с проблемой.

    uint32_t sumX = 0;
    uint32_t sumY = 0;
    uint32_t sumX2 = 0;
    uint32_t sumXY = 0;

    for (int i = 0; i < 6; i++) { // для всех элементов массива
    sumX += _time_array[i];//=15 сумма значений оси X
    sumY += _pressure_array[i];//сумма значений оси Y
    sumX2 += _time_array[i] * _time_array[i];//квадрат суммы значений оси X
    sumXY += _time_array[i] * _pressure_array[i];
    }

    rlog_v("func()", "sumX = %d", sumX);
    rlog_v("func()", "sumY = %lu", sumY);
    rlog_v("func()", "sumX2 = %d", sumX2);
    rlog_v("func()", "sumXY = %lu", sumXY);

    float a = 0;
    a = 6.0 * (float)sumXY; // расчёт коэффициента наклона прямой
    rlog_d("func()", "1 a = %f", a);
    Serial.print("1 a = ");Serial.println(a);

    a = a – (float)sumX * (float)sumY;
    rlog_d("func()", "2 a = %f", a);
    Serial.print("2 a = ");Serial.println(a);

    a = a / (6.0 * (float)sumX2 – (float)sumX * (float)sumX);
    rlog_d("func()", "3 a = %f", a);
    Serial.print("3 a = ");Serial.println(a);

    int16_t delta = (int)floor(a * 6); // расчёт изменения давления
    rlog_d("func()", "delta = %d", delta);

    Вывод лога взял чуть заранее, чтоб было понято что за числа.

    00:00:00 [D] func() :: _pressure_array[0] = 102075
    00:00:00 [D] func() :: _pressure_array[1] = 102075
    00:00:00 [D] func() :: _pressure_array[2] = 102075
    00:00:00 [D] func() :: _pressure_array[3] = 102075
    00:00:00 [D] func() :: _pressure_array[4] = 102075
    00:00:00 [D] func() :: _pressure_array[5] = 102072
    00:00:00 [V] func() :: sumX = 15
    00:00:00 [V] func() :: sumY = 612447
    00:00:00 [V] func() :: sumX2 = 55
    00:00:00 [V] func() :: sumXY = 1531110
    00:00:00 [D] func() :: 1 a = ?
    1 a = 9186660.00
    00:00:00 [D] func() :: 2 a = ?
    2 a = -45.00
    00:00:00 [D] func() :: 3 a = ?
    3 a = -0.43
    00:00:00 [D] func() :: delta = -3

    Что я делаю не так?

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

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