Прикручиваем printf к serial и не только

Тема в разделе "Флудилка", создана пользователем DetSimen, 28 янв 2020.

  1. DetSimen

    DetSimen Guest

    Новичкам, задолбавшимся печатать бесконечные Serial.print() да Serial.println() c каждой переменной, которую они хочут вывести, могу посоветовать открыть файл Print.h и в описание класса в конец вставить вот это
    Код (C++):
    size_t printf(const char *format, ...) {
        const uint8_t MAX_STRING_SIZE = 64;      
        char buf[MAX_STRING_SIZE];

        va_list args;
        va_start(args, format);
        vsnprintf(buf, MAX_STRING_SIZE, format, args);
        va_end(args);
        return print(buf);
    }
    и тогда все свои переменные можно писать в одну строчку со всеми плюшками стандартной функции printf()

    Описание форматов можно почитать у DIHALT: http://we.easyelectronics.ru/Soft/formatnyy-vyvod-na-si-dlya-mikrokontrollerov.html

    Теперь лехко можно писать

    Код (C++):
    const uint8_t ARRAY_SIZE = 100;

    int Array[ARRAY_SIZE];
    for (uint8_t i = 0; i < ARRAY_SIZE; i++) Array[i] = 10*i;

    const char *Prompt = "Line";

    for (uint8_t i = 0; i < ARRAY_SIZE; i++) {
        Serial.printf("%s%-3d: Array[%d] = \t%d\n", Prompt, i + 1, i, Array[i]);
    }
    Если у каво вдруг возникнут трудности с прикручиванием, пишите, я покажу куда.
     
    SergeiL, Daniil, arkadyf и 5 другим нравится это.
  2. Vovka

    Vovka Гик

    Добавлю свои 5 копеек.
    Этот же принцип можно использовать и в других классах, например, для вывода текста на дисплей...
    Единственное, что нужно помнить, так это то, что "вдруг" увеличился размер прошивки... :)
     
  3. DetSimen

    DetSimen Guest

    тут выбирать надо, удобно, или размер прошивки.
    Хотя, я это в define заворачиваю, ибо сериал пользую только для отладки. В готовой поделке никакова вывода printf-ом нет.
     
    Andrey12 и Vovka нравится это.
  4. DetSimen

    DetSimen Guest

    для этого в заголовке темы и стоит фраза "и не только". Все, што унаследуешь от Print, подхватит это прям "искаропки"
     
    Andrey12 и Vovka нравится это.
  5. AlexU

    AlexU Гуру

    Статью полностью осилить так и не смог? (Понимаю, что Дед выпилися из форума, поэтому вопрос скорее риторический)

    Что касается представленного "велосипеда" -- тут нужно оговориться, что тот, кто будет пользоваться данным советом, должен в голове держать мысль: "длина результирующей строки не должна превышать 63 символа". Потому как автор использует буфер размером MAX_STRING_SIZE = 64. Конечно же ни кто не мешает этот буфер увеличить. Но только всё равно задача определения размера буфера ложится на плечи программиста. При чём задача эта не такая уж и простая, как может показаться на первый взгляд. Вот к примеру, простой формат "%s - %d". Какого размера должен быть буфер? Ответ будет только тогда, когда программист определится какие строки и какие числа будут выводиться при помощи форматированного вывода.

    Эта была критика, а теперь к делу.
    В контексте задачи вывода форматированного текста в последовательный порт, более красивым решением будет переопределение стандартного вывода. В статье DiHALT'а про это подробно написано, повторяться не буду. Но на чём стоит заострить внимание -- код приведённый в статье будет работать только при использовании исходников на языке C с соответствующим компилятором, с языком C++ при компилировании получите ошибку: "sorry, unimplemented: non-trivial designated initializers not supported". Суть в том, что C++ не умеет обрабатывать декларации структур типа:
    Код (C++):
    FILE mystdout = {
            .put = p,
            .get = g,
            .flags = f,
            .udata = 0,
        }
    Внутри блока инициализации должны быть перечислены все поля структуры в правильном порядке. А в данной декларации перечислены только нужные поля.
    Казалось бы "да что тут такого?". Проблема в том, что многие пользуются Arduino IDE, а в этой среде C++ используется по умолчанию (правда, он там немного доработан и некоторые его называют языком Wiring).
    И что же делать?

    Можно применить не большой костыль. Костыль этот относится к разряду "говнокода", но всё же не такого вонючего как предложил автор темы.
    Суть в следующем -- сначала определяем структуру типа FILE, а потом назначаем нужные поля и переопределяем стандартный поток ввода/вывода:
    Код (C++):
    // Определяем структуру, которая будет использоваться для замены стандартного потока вывода
    FILE mystdout;

    // Функция вывода символа, будет использоваться для вывода символа в наш поток вывода
    static int my_putchar(char c, FILE *stream) {
        Serial.write(c);
        return 0;
    }

    //The setup function is called once at startup of the sketch
    void setup()
    {
        Serial.begin(115200);

        // А вот здесь начинается наш костыль.
        // Костыль потому, что внутреннее устройство структуры FILE может поменяться без предупреждения
        // и наш код перестанет компилироваться на какой-нибудь новой версии компилятора.
       
        // Назначаем функцию 'my_putchar', как функцию вывода символов в нашем потоке вывода
        mystdout.put = my_putchar;
        // Говорим. что наш поток будет работать только на вывод
        mystdout.flags = _FDEV_SETUP_WRITE;
       
        // Костыль закончен
       
        // Переопределяем стандартный поток вывода нашим потоком
        stdout = &mystdout;

        // Всё.
        // Можно использовать стандартные функции типа printf, для вывода в последовательный интерфейс
        // Все строки после форматирования будут посимвольно попадать в нашу функцию 'my_putchar',
        // которая будет их отправлять в Serial
        printf("Serial:\n");
    }
    Пример использования в loop():
    Код (C++):
    void loop()
    {
        int d = SPI.transfer(0);

    // Раньше было так:
    //    Serial.print("Received: ");
    //    Serial.println(d);

    // Сейчас так:
        printf("Received: %d\n", d);

        delay(500);
    }
    Ни каких буферов и лишних раздумий о размерах этих буферов, а так же не надо вмешиваться в потроха ардуиновских библиотек.

    PS: Правда стоит оговориться, применение данного подхода более затратно в плане потребления памяти микроконтроллера. Точных замеров не проводил, но где-то лишний килобайт Flash-а.
     
    parovoZZ нравится это.