Официальный сайт компании Arduino по адресу arduino.cc
Форматированный вывод в Serial
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Пнд, 31/08/2015 - 19:05
Начинающий коллега увидел у меня в коде форматированный по типу printf отладочный вывод в Serial (когда вместо:
Serial.print("U="); Serial.print(u); Serial.print("V f="); Serial.print(f); Serial.println("KHz");
пишется
SerialPrintf("U=%dV f=%dKHz\n, v, f);
попросил дать ему код и посоветовал опубликовать здесь, т.к., по его мнению, многие с удовольствием воспользуются.
В последнем сомневаюсь, но, на всякий случай, публикую, может и правда кому сгодится. Вывод реализован одной функцией. Параметры у неё как у printf. Текст функции и пример использования:
/* * SerialPrintf * Реализует функциональность printf в Serial.print * Применяется для отладочной печати * Параметры как у printf * Возвращает * 0 - ошибка формата * отрицательное чило - нехватка памяти, модуль числа равен запрашиваемой памяти * положительное число - количество символов, выведенное в Serial */ const size_t SerialPrintf (const char *szFormat, ...) { va_list argptr; va_start(argptr, szFormat); char *szBuffer = 0; const size_t nBufferLength = vsnprintf(szBuffer, 0, szFormat, argptr) + 1; if (nBufferLength == 1) return 0; szBuffer = (char *) malloc(nBufferLength); if (! szBuffer) return - nBufferLength; vsnprintf(szBuffer, nBufferLength, szFormat, argptr); Serial.print(szBuffer); free(szBuffer); return nBufferLength - 1; } /* * Пример использования SerialPrintf */ void setup() { Serial.begin(19200); while(!Serial) yield(); SerialPrintf("Fun begins!\n"); SerialPrintf("%d%s = 0x%04X%s\n", 321, "(decimal)", 321, "(hexadecimal)"); } void loop() { }
Спасибо.
Для меня это полезная и удобная вещь.
Не могли бы Вы в дополнение пояснить, как (где) узнать про другие функции, которые не описаны в Arduino Language Reference, но реально работают?
Не могли бы Вы в дополнение пояснить, как (где) узнать про другие функции, которые не описаны в Arduino Language Reference, но реально работают?
Ты бы хоть копирайт свой оставил, чтобы страна помнила своих героев.
1. Хотельсь бы знать, зачем в строке 15 дополнительно прибавляется 1. vsprintf сам по себе возвращает длину с учетом терминирующего символа.
2. При использовании данной процедуры необходимо помнить, что она использует кучу, уменьшая при этом размер стека, причем на заранее неизвестную глубину. Что в условиях существенно ораниченного объема памяти Arduino может оказаться весьма существенным.
спасибо, полезная штучка.
1. Хотельсь бы знать, зачем в строке 15 дополнительно прибавляется 1. vsprintf сам по себе возвращает длину с учетом терминирующего символа.
2. При использовании данной процедуры необходимо помнить, что она использует кучу, уменьшая при этом размер стека, причем на заранее неизвестную глубину. Что в условиях существенно ораниченного объема памяти Arduino может оказаться весьма существенным.
А Вы добавьте из предложенной Вами функции вывод в Serial значения, возвращаемого vsnprintf, и посмотрите, что она напечатает.
Хотя, в принципе, если существующая реализация сделана не постандарту, то, согласен, лучше перестраховаться и добавить - на случай, когда поправят реализацию.
нужно применять осторожно.
Ваше "осторожно" напомнило книгу "Физики продолжают шутить". Там был фразеологический словарь для перевода научных статей на нормальный язык. В частности были такие определения:
"Во время эксперимента прибор был слегка повреждён" - перевод: "Уронили на пол"
"С прибором обращались с исключительной осторожностью" - перевод: "Не роняли на пол" :)
Случайно натолкнулся на этот очередной "велосипед".
Всё время это делалось стандартным образом:
Преимущества:
- Расход ОЗУ меньше, вывод идёт сразу в UART.
- Можно использовать строковые константы из флеш.
- меньше лишнего кода
- По аналогии, можно связать printf с буферизированным UART(ами) и LCD.
PS: Впрочем, за рабочий "велосипед" - зачет. Ардуино для этого и предназначена.
Преимущества:
- Расход ОЗУ меньше, вывод идёт сразу в UART.
- Можно использовать строковые константы из флеш.
- меньше лишнего кода
- По аналогии, можно связать printf с буферизированным UART(ами) и LCD.
Недостатки
Отжирает лишние 900 байтов памяти программы. Пруф. ниже
Но за хороший учебный материал, безсусловно, зачёт. Дополнение очень в струю. Жаль нельзя добавить в первоначальный топик.
Могу показать свой логгер, который использую везде в коде: TelnetServer
Там файлы: Logger.cpp и Logger.h
Используется так:
Если кто захочет использовать, придётся доработать под себя. У меня для каждой строчки выводится дата и время, т.к. есть поддержка времени в коде программы.
Могу показать свой логгер, который использую везде в коде: TelnetServer
Спасибо!
Тема изначально учебная, поэтому здорово, что появляются посты типа Вашего и _kp. Больше примеров, хороших и разных!
Сам я для отладочной печати использую потоковую нотацию, очень компактно, но, разумеется без printf'овского форматирования. Но там есть засада. Параметр передаётс по значению, поэтому, например, если печатать String, то создаётся новый экземпляр со всеми вытекающими.
1.
Отлично! Будем считать, первый вариант оптимизированным, например чиcто для отладочного вывода. Когда от библиотеки stdio нужен только printf.
2.
С первым вариантом есть проблема с перерасходом ОЗУ.
При вызове например SerialPrintf("Fun begins!\n"); Строковая константа "Fun begins!\n" будет размещена и во флеш, и в ОЗУ(а для AVR это сверх зло).
Плюс выделяется дополнительный временный буфер.
Несколько десятков "отладочных printf", и для основной программы совсем нехватит ОЗУ.
На AVR обычно не пользуются стандартным printf/sprintf, а используют printf_P/sprintf_P
3.
На минималистичной программе, сравнение размеров кода не объективно, практическая разница будет всего 120..150 байт. Но зато единообразно работает ввод/вывод и UART, и с LCD, и с SdCard.
4,
При переделке SerialPrintf для экономии ОЗУ, при замене vsnprintf на vsnprintf_P, размер минималистичных программ заметно распухает, и не менее заметно сокращается разница в размера кода для обоих вариантов, приближаясь к рассчитанной выше..
О, Господи, не лезьте в драку, это учебный материал. Я показал как использовать sprintf, Вы показали как перенаправить поток - прекрасно, что показано и то, и другое.
тем более, что
На AVR обычно не пользуются стандартным printf/sprintf, а используют printf_P/sprintf_P
Господь с Вами, в сколько-нибудь серьёзных программах никто не использует ни того, ни другого.
И снова привет.
На станице http://playground.arduino.cc/Main/Printf говорится, о том что можно добавить метод printf в базовый класс Print.
Фича в том, что метод xxx.printf появится не только у Serial, но и у всех классов, его использующих, то есть LCD и TFT дисплеи.
Там же есть и пример, показывающий что именно надо подправить в фирменной библиотеке.
Но, как обычно, там вариант с временным текстовым буфером. Что для AVR крайне вредно, и буфера всё равно мало.
Вот сделал почти тоже самое, но с существенно меньшим расходом ОЗУ, и работающее быстрее.
То есть, это вариант добавления printf в базовый класс Print.
После чего можно писать примерно так:
Для установки нужно изменить оригинальные библиотеки Arduino, предварительно сохранив оригиналы.
файл: arduino-1.6.11\hardware\arduino\avr\cores\arduino\Print.h
// Добавляем в описание класса это:
файл: arduino-1.6.11\hardware\arduino\avr\cores\arduino\Print.cpp
// Добавляем в файл это:
Результаты тестов.
1. avr-g++ гененерирует компактнее код для статических классов, по сравнению с передачей кучи указателей в функции в стиле си.
То есть добавление printf в базовый класс Print выгоднее, по сравнению с fprintf(stream,format,...). Объём кода меньше.
При объёме кода за 20кБ этот вариант, printf в классе даёт самый компактный код.
2. При неиспользовании printf, лишнего кода во флеш нет.
3. Даже при неиспользовании printf, отъедается 12 байт ОЗУ, под структуру FILE,
которую можно перенести в методы print, но нецелесообразно из за быстродействия.
При вызове Serial.print, думаю разницы в быстродействии не увидеть, а при выводе на TFT LCD быстродействие и так на грани возможного.
4. Расход ОЗУ минимален. 12 байт под структуру FILE, 11+1 временный буфер в libc.vprintf плюс стек под переменные.
5. Ну и о недостатках. Библиотеки vprintf/printf/sprintf в Arduino без поддержки float.
Штатными методами не лечится.
- Можно подменить библиотеку, но так каждый проэкт будет больше, что нехорошо.
- Можно использовать arduino-makefile. Кроме того что новичкам надо разбираться, проблем нет.
- Можно для форматирования float использовать - dtostrf
- Ну и конечно, не "злоупотреблять" float'ами на AVR.
ps: я так понял, что файл то тут приложить ни как?
ps: я так понял, что файл то тут приложить ни как?
файл - нет.
код - да.
Ты бы хоть копирайт свой оставил, чтобы страна помнила своих героев.
Коллега - копираст?
Добрый день.
Взял чей то вполне рабочий скетч, немного переделал под себя, пока все устравало. Но ТЗ расширилось и вместо вывода в порт чисел типа byte, понадобилось выволдить числа double.
Привожу первый рабочий вариант
Значения перед выводом хранятся в строковом буферe посредством snprintf . Вот ее описание:
Она идентична функции sprintf() за исключением того, что в массиве, адресуемом указателем buf, будет сохранено максимум num-1символов. По окончании работы функции этот массив будет завершаться символом конца строки (нуль-символом). Таким образом, функция snprintf() позволяет предотвратить переполнение буфера buf.
Нерабочий
В первом варианте спецификатор d - для целого знакового десятичного числа, во втором f - для число с плавающей точкой. Подскажите в чем не прав
Чтобы легче понять, вот их основные различия. Во втором коде в порт выводятся не числа а знаки ?.
В том, что sprintf с плавающей точкой ниработаить
А чем это заменить чтобы заработало?
dtostrf()
strtod
преобразовывает строкуstring
вdouble
. Да уже понялStrtod это перевести строку в double
Да нашел, спасибо
http://geekmatic.in.ua/arduino_otobrazhenie_dannyih_float
А взат, из числа в строку - dstrtof()
BuonanotteMasha,
1.
кажется, где-то в опциях линкера можно было включить поддержку флоатов в штатной библиотеке, но точно не помню и не уверен.
2.
Если есть лишние полкило памяти программ, то можно обойтись без печати в буфер, а связать штатный printf с потоком и выводить в любой поток просто штатным printf. ПРимеры я тут в разных темах много раз выкладывал.
Да спасибо, уже работает
Только не пойму, почему в порт только одно число выводится
Тут ошибок не наблюдаю
BuonanotteMasha,
1.
кажется, где-то в опциях линкера можно было включить поддержку флоатов в штатной библиотеке, но точно не помню и не уверен.
я пыталса анажды найти, как в спринтф включать плавающую точку, прям из атмеловской документации пытался повторить, неделю нерничал, пил по чёрному, но так и не смог.
Танцы с бубном не помогли
Внимательно пока не смотрел, но попробуйте поставить после строки 92 Serial.flush();
Виноват, поправил на 92 - в общем, после печати сразу. А то в старом скетче это было 90.
Нет не помогло, пробую с floatToString. Поменял как вы сказали на 92 тоже нет
Покажите текущий код (в котором не помогло)
Евгений спасибо вам за помощь, но у меня самого получилось
проблема в указателях
Окончательный рабочий код
А, ну понятно, в строках 79-80 замените 16 на 15 - у Вас терминальный ноль не помещается.
А лучше заменять не на 15, а на sizeof(charbuf1)-1 и sizeof(charbuf2)-1
Я бы на Вашем месте остался бы на dtostrf. Возьмите код из поста #36 и поправьте, что я сказал, там делов-то.
Отжирает лишние 900 байтов памяти программы. Пруф. ниже
/*
Дак Вы вызывали printf, а не свою SerialPrintf функцию. При вызове SerialPrintf, скетч использует аж 3 700 байт;)
Сколько кто отжирает приведено в том посте, так что ля-ля не надо. А "свою", "не свою" - это Вы мимо. Они все мои. И использование scanf - тоже (я и его пользую).
Данный топик не для вбросов, а для решения задачи. Есть хорошее предложение? Выкладываейте, будет одним больше. Нет - не вбрасывайте, срача всё равно не будет. С моим участием, по крайней мере, точно не будет.
Какой вброс? Вы о чем? Я лишь указал на ошибку в ваших тестах. Нужно уметь признавать свои ошибки.
Да, действительно, только сейчас заметил. Извините.
Здравствуйте, ТС и форумчане. Имею скетч, пока занимает 774 (37%) оперативы и 16 794 байт (52%) флэш. Но скетч постоянно дополняется и улучшается и предстоит еще немало доработать. Поэтому если не составит труда подскажите как элементы структуры ниже с помощью PROGMЕM перенести во флэш чтобы потом не возникало желания перейти на другую плату помощнее, uno вполне устраивает
Делал со строками в коде таким образом, но здесь такое не прокатит
BuonanotteMash,
если Вы не заметили, это тема о форматированном выводе в поток, и Ваш вопрос здесь - оффтопик и флуд. Потрудитесь, пожалуйста, найти подходящую тему или создайте свою.
ЕвгенийП, извините, сразу не подумал. Последую вашему совету