Как вывести несколько переменных в строке?
Допустим, у меня есть несколько переменных, которые я хочу распечатать на терминале, и каков самый простой способ напечатать их в виде строки?
В настоящее время я делаю что-то вроде этого:
Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);
Есть ли лучший способ сделать это?
@sachleen, 👍55
Обсуждение13 ответов
Лучший ответ:
ardprintf
-это функция, которую я взломал вместе, которая имитирует printf
по последовательному соединению. Эту функцию (приведенную внизу) можно вставить в начало файлов, в которых эта функция необходима. Это не должно создавать никаких конфликтов.
Его можно назвать похожим на printf
. Посмотрите на это в действии в следующем примере:
void setup()
{
Serial.begin(9600);
}
void loop()
{
int l=2;
char *j = "test";
long k = 123456789;
char s = 'g';
float f = 2.3;
ardprintf("test %d %l %c %s %f", l, k, s, j, f);
delay(5000);
}
Результат, как и ожидалось, будет следующим:
test 2 123456789 g test 2.30
Прототипом функции является:
int ardprintf(char *, ...);
Он возвращает количество аргументов, обнаруженных в вызове функции.
Это определение функции:
#ifndef ARDPRINTF
#define ARDPRINTF
#define ARDBUFFER 16
#include <stdarg.h>
#include <Arduino.h>
int ardprintf(char *str, ...)
{
int i, count=0, j=0, flag=0;
char temp[ARDBUFFER+1];
for(i=0; str[i]!='\0';i++) if(str[i]=='%') count++;
va_list argv;
va_start(argv, count);
for(i=0,j=0; str[i]!='\0';i++)
{
if(str[i]=='%')
{
temp[j] = '\0';
Serial.print(temp);
j=0;
temp[0] = '\0';
switch(str[++i])
{
case 'd': Serial.print(va_arg(argv, int));
break;
case 'l': Serial.print(va_arg(argv, long));
break;
case 'f': Serial.print(va_arg(argv, double));
break;
case 'c': Serial.print((char)va_arg(argv, int));
break;
case 's': Serial.print(va_arg(argv, char *));
break;
default: ;
};
}
else
{
temp[j] = str[i];
j = (j+1)%ARDBUFFER;
if(j==0)
{
temp[ARDBUFFER] = '\0';
Serial.print(temp);
temp[0]='\0';
}
}
};
Serial.println();
return count + 1;
}
#undef ARDBUFFER
#endif
**Для печати символа %
используйте %%
.*
Теперь доступно на Github gists.
Хорошая идея, хотя я чувствовал, что она могла бы быть более минималистичной, поэтому я переписал эту версию на одну без буферизации. Любой желающий может ознакомиться с сутью: https://gist.github.com/EleotleCram/eb586037e2976a8d9884, @eleotlecram
в одной строке вы можете просто сделать это следующим образом Serial.println((Строка)"x:"+x+" y:"+y); Источник : https://stackoverflow.com/a/52749055/5374995, @Vikas Kandari
Это, наверное, не лучше, просто по-другому. Вы можете использовать объект String для вывода. Эти объекты допускают объединение и поддерживают автоматическую типизацию.
Serial.begin(9600);
String label = "Var";
const byte nValues = 3;
int var[nValues] = {36, 72, 49};
for (int i = 0; i < nValues; i++) {
String stuff = label + i + ": ";
Serial.println(stuff + var[i]);
}
Очевидно, что важно быть осторожным с ограничениями памяти. Множество конкатенаций и других строковых операций в одном месте могут занимать удивительно много места., @Peter Bloomfield
@PeterR.Bloomfield Абсолютно верно! Вот почему я упомянул, что этот вариант не лучше ;), @Klaus-Dieter Warzecha
Я обычно использовал вкладки, чтобы сделать все лучше в сериале. Когда вещи выстраиваются так, как я, это позволяет arduino работать как можно быстрее, при этом замечая определенные изменения в переменных.
Попробуйте что-нибудь вроде этого:
Serial.println("Var 1:\tVar 2tVar 3:");
Serial.print("\t");
Serial.print(var1);
Serial.print("\t");
Serial.print(var2);
Serial.print("\t");
Serial.print(var3);
Serial.println();
Или что-то вроде этого:
Serial.print("Var 1:");Serial.println(var1);
Serial.print("\tVar 2:");Serial.println(var2);
Serial.print("\tVar 3:");Serial.println(var3);
Честно говоря, я делаю то же самое ("\t" и "\n") и обычно избегаю колоколов и свистков строкового объекта, раздувающего код., @Klaus-Dieter Warzecha
@KlausWarzecha, я редко даю имя переменной, так как они находятся в хороших столбцах. Также облегчите просмотр случайных распечаток, которые не соответствуют этому синтаксису, @Steven10172
Я обычно (болезненно) придерживаюсь нескольких строк серийной печати
, но когда она становится запутанной, я возвращаюсь к sprintf
. Это раздражает тем, что для этого у вас должен быть доступный буфер.
Использование так же просто (??), как:
char buffer[35]; // вы должны знать, как долго могут храниться ваши данные
// не забывайте, что символы непечатаемых и нулевых терминов
sprintf(buffer,"var1:%i\tvar2:%i\tvar3:%i",var1,var2,var3);
Serial.println(buffer);
Однако хочу предупредить, что он (по умолчанию) не поддерживает плавающие типы.
sprintf-ужасная мерзость. Небезопасно для ввода, легко переполнять ваши буферы и т. Д. и т. Д. Это инструмент 1960-х годов. Тем не менее, я тоже им пользуюсь, но это не для слабонервных...., @Duncan C
Чтобы избежать переполнения, используйте snprintf... КСТАТИ, большинство IDE модера (НЕ IDE Arduino) проверят формат строки на соответствие предоставленным типам переменных и выдадут предупреждение., @next-hack
Я обычно не ставлю два ответа на вопрос, но я только сегодня нашел это, где вы можете использовать printf без буфера.
// Функция, которая printf и связанная с ней будет использоваться для печати
int serial_putchar(char c, FILE* f) {
if (c == '\n') serial_putchar('\r', f);
return Serial.write(c) == 1? 0 : 1;
}
FILE serial_stdout;
void setup(){
Serial.begin(9600);
// Настройка stdout
fdev_setup_stream(&serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);
stdout = &serial_stdout;
printf(""Мое любимое число %6d!\n", 12);
}
void loop() {
static long counter = 0;
if (millis()%300==0){
printf("millis(): %ld\tcounter: %ld (%02X)\n", millis(), counter, counter++);
delay(1);
}
}
Это все еще имеет ограничение с плавающей точкой.
edit: Я подумал, что мог бы провести небольшое тестирование на этом, и это работает довольно хорошо. Я добавил лучший тест в цикл с форматированным выводом.
О, чувак, это круто. printf намного безопаснее, чем sprintf. Это дает вам бесплатные форматные строки, и это здорово. Классный трюк. Спасибо. (Проголосовали), @Duncan C
Один вопрос: в вашей функции serial_putchar
почему бы не сделать оператор return return !Серийный номер.запись(c);
? Разве это не чище, чем триинарный оператор для инвертирования смысла логического возвращаемого значения?, @Duncan C
Это хорошее замечание, и оно мне нравится. Код был не мой, и я вставил его так, как нашел., @Madivad
Спасибо за функцию "serial_putchar". Это доставляет удовольствие. :-) Можете ли вы исправить ограничение точки плавания_?, @Greenonline
Используя Streaming.h
, вместо
Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);
можно написать
Serial << "Var 1:" << var1) << " Var 2:" << var2 << " Var 3:" << var3 << endl;
Определение <<
в потоковой передаче.h
фактически переводит это в серию обычных вызовов Serial.print ()
. То есть < <
- это синтаксический сахар, реализованный без увеличения размера кода.
Если у вас не установлен Streaming.h
, получите Streaming5.zip
от arduiniana.org. Распакуйте его в каталог библиотек, например, в ~/sketchbook/библиотеки
. Добавьте строку #включить <Потоковое.h><Потоковое.h>
в скетчи, где вы используете <<
в качестве оператора потока.
Предусмотрены спецификаторы базового преобразования _HEX, _DEC, _OCT и _BIN, а также функция _FLOAT (с количеством знаков после запятой) и endl
. Например, чтобы напечатать значения широты и долготы в форме типа "Ваши координаты -23.123, 135.4567”, можно написать:
Serial << "Your coordinates are " << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;
Это также можно было бы записать как
Serial << F("Ваши координаты ") << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;
что позволило бы сохранить более длинную строку в PROGMEM вместо того, чтобы переносить ее в оперативную память.
Обратите внимание, что Streaming.h
не создает никаких строк как таковых; он просто доставляет текст своих <<
-аргументов в поток. Класс PString в arduiniana может создавать строки из входных данных потока, если требуются или необходимы строки вместо выходных данных потока.
От http://playground.arduino.cc/Main/Printf Я заметил, что это отлично работает на mega2560
Вот и все, что просто сработало, не нужно vsnprintf_P или PROGMEM ...
#include "Arduino.h"
void local_printf(const char *format, ...)
{
static char line[80];
va_list args;
va_start(args, format);
int len = vsnprintf(line, sizeof(line), format, args);
va_end(args);
for (char *p = &line[0]; *p; p++) {
if (*p == '\n') {
Serial.write('\r');
}
Serial.write(*p);
}
if (len >= sizeof(line))
Serial.write('$');
}
void setup()
{
Serial.begin(115200);
local_printf("%s:%d: %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
}
void loop()
{
static int count=0;
local_printf("%s:%d: %s %d\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, count++);
delay(1*1000);
}
// src/main.c:24: void setup()
// src/main.c:30: void loop() 0
// src/main.c:30: void loop() 1
Почему кто-то хочет сделать это вместо того, чтобы просто использовать printf()
сам по себе?, @Edgar Bonet
int Money_amount = 55;
Serial.print(String("New amount: $" + Money_amount));
Вы увидите на терминале:
New amount: $55
Вы не можете объединить int в строку c с помощью оператора`+'., @gre_gor
Использование будет зависеть от типа данных ваших переменных.
Если они являются int
, то это будет %d
или %i
Если они являются строковыми
, то это будет %s
Оболочка для печати
Вы можете изменить лимит в зависимости от ваших требований
#include <stdarg.h>
void p(char *fmt, ... ){
char buf[128]; // результирующая строка ограничена 128 символами
va_list args;
va_start (args, fmt );
vsnprintf(buf, 128, fmt, args);
va_end (args);
Serial.print(buf); // Вывод результата в последовательный порт
}
Источник: https://playground.arduino.cc/Main/Printf
Примеры использования:
p("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3); // строки
p("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3); // числа
ESP8266
Он встроен в последовательный
класс фреймворка. Нет необходимости в дополнительной библиотеке или функции.
// строки
Serial.printf("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3);
// числа
Serial.printf("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3);
Дополнительные сведения о советах по форматированию на справочной странице по формату printf : http://www.cplusplus.com/reference/cstdio/printf/
\n
-это escape - последовательность для перевода строки.
Escape-последовательности используются для представления определенных специальных символов в строковых и символьных литералах.
Источник: http://en.cppreference.com/w/cpp/language/escape
[ПРАВИТЬ] - Как упоминал @Juraj, он недоступен в большинстве модулей AVR. Поэтому я добавил упоминание ESP8266 и обертку printf для обычных AVR-модулей
это неправда. последовательного класса не существует. printf будет находиться в классе печати, но он не входит в наиболее используемый пакет AVR, @Juraj
@Juraj, вы правы, я тестировал его только на ESP8266, у которых он есть ([ссылка](https://github.com/esp8266/Arduino/search?q=printf&unscoped_q=printf)) и подумал, что это из ядра arduino. Обновлю свой ответ соответствующим образом, @Remi
для функции p я бы добавил еще одно понижающее значение, если бы это было возможно., @Juraj
это старый вопрос, и я не могу судить о старых ответах, потому что я не знаю, что было доступно в 2014 году. но теперь существуют библиотеки для упаковки потока печати в поток печати с реализацией printf., @Juraj
Я использую это только для отладки, но:
int a = 10;
int b = 20;
Serial.println("a = " + String(a) + " and b = " + String(b));
что такое строка$?, @Juraj
LMFTFM (Позвольте мне исправить это для меня)., @linhartr22
Я новичок в мире Arduino, но недавно обнаружил, что это обычный C++ (без исключений и, вероятно, полиморфизма). Но вы все еще можете наслаждаться шаблонами. Поэтому мое решение состоит в том, чтобы использовать следующие шаблоны:
void myprint(void)
{
Serial.println("");
}
template<typename ...Args>
void myprint(const uint64_t & val, Args && ...args)
{
serialPrintUint64(val);
myprint(args...);
}
template<typename T, typename ...Args>
void myprint(const T & t, Args && ...args)
{
Serial.print(t);
myprint(args...);
}
....
// где-то в вашем коде
myprint("type: ", results.decode_type,
"\t value: ", results.value,
"\t addr: ", results.address,
"\t cmd: ", results.command);
Хорошо, что здесь не используется дополнительная память и дополнительная обработка.
Одним из возможных решений является:
Serial.println((Строка)"Вар 1:" + переменная 1 + " Вар 2:" + переменная 2 + " Вар 3:" + переменная 3);
Приведенное выше решение от Iain увеличивает размер кода.
Мне нравится этот метод, и я часто им пользуюсь. Я ставлю тебе плюс 1., @Randy Skretka
Я использую PrintEx уже много лет, и он отлично работает. См. Описание ссылки ввода здесь
Я также использую PrintEX в течение многих лет, и я согласен, что он почти пуленепробиваемый. Тем не менее, вы должны знать, что микроконтроллеры Teensy 3.x/4.x из магазина Пола Стоффрегена (https://www.pjrc.com/store/teensy32.html) имеют Serial.printf, встроенный в их пакет TeensyDuino. Платы Teensy 3.2 стоят так же или даже дешевле, чем платы Arduino, и работают НАМНОГО быстрее. У меня есть десятки таких микроконтроллеров, запрограммированных на различные проекты по дому, и у меня было очень мало проблем с ними., @user3765883
- Как разделить входящую строку?
- форматирование строк в Arduino для вывода
- Очень простая операция Arduino Uno Serial.readString()
- Arduino Преобразование std:string в String
- Как прочитать входящие ШЕСТНАДЦАТИРИЧНОЕ значение из serial метод read ()?
- Arduino Serial.ReadString() проблема
- Очистка строкового буфера с помощью memset после последовательного чтения
- Скорость передачи устанавливается на 9600 автоматически, даже если указано другое значение.
Идея, но я не знаю, сработает ли она, является некоторой модификацией этого... Опять же, я не знаю, поддерживается ли это на Arduino: http://stackoverflow.com/questions/804288/creating-c-formatted-strings-not-printing-them, @apnorton