Как напечатать несколько переменных в строке?


46

Скажем, у меня есть некоторые переменные, которые я хочу распечатать на терминале, какой самый простой способ вывести их в строку?

В настоящее время я делаю что-то вроде этого:

Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);

Есть лучший способ сделать это?


Идея, но я не знаю, сработает ли она, это какая-то модификация этого ... Опять же, я не знаю, поддерживается ли это в Arduino: stackoverflow.com/questions/804288/…
apnorton

Ответы:


37

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 .


3
Хорошая идея, хотя я чувствовал, что она может быть более минималистичной, поэтому я переписал эту версию на другую без буферизации. Любой желающий может проверить суть: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

Обычно я не ставлю два ответа на вопрос, но я только сегодня нашел это , где вы можете использовать printf без буфера.

// Function that printf and related will use to print
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);

    // Set up stdout
    fdev_setup_stream(&serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);
    stdout = &serial_stdout;

    printf("My favorite number is %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);    
  }
}

Это все еще имеет ограничение с плавающей запятой.

редактировать: я думал, что я бы немного протестировать это, и это работает довольно хорошо. Я добавил лучший тест в цикл с форматированным выводом.


Ох, это круто. printf намного безопаснее, чем sprintf. Это дает вам форматирование строк бесплатно, и это здорово. Прикольный трюк. Благодарю. (Проголосовано)
Дункан С

Один вопрос: serial_putcharпочему бы не сделать оператор return return !Serial.write(c);? Разве это не чище тривиального оператора для инвертирования смысла возвращаемого логического значения?
Дункан C

Это хороший момент, и мне это нравится. Код был не мой, и я вставил его, как нашел.
Мадивад

Спасибо за serial_putcharфункцию. Это работает удовольствие. :-) Можете ли вы исправить ограничение с плавающей запятой ?
Greenonline

4

Это, вероятно, не лучше, просто другое. Вы можете использовать объект 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]);
}

4
Очевидно, что важно быть осторожным с ограничениями памяти. Множество конкатенаций и других строковых операций в одном месте могут занимать удивительное количество места.
Питер Блумфилд

@ PeterR.Bloomfield Абсолютно верно! Вот почему я упомянул, что этот вариант не лучше;)
Клаус-Дитер Варзеча

4

Обычно я использовал вкладки, чтобы улучшить последовательность в последовательном. Выстраивание вещей, как я, позволяет 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") и обычно избегаю наворотов кода String и свистков объекта.
Клаус-Дитер Варзеча

1
@KlausWarzecha, я редко даю имя переменной, так как они в хороших столбцах. Также упростите просмотр случайных распечаток, не соответствующих этому синтаксису
Steven10172

4

Я использую это только для отладки, но:

int a = 10;
int b = 20;
Serial.println("a = " + String(a) + " and b = " + String(b));

что такое String $?
Юрай

LMFTFM (Позвольте мне исправить это для меня).
linhartr22

2

Я новичок в мире 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...);
}

....

// somewhere in your code
myprint("type: ", results.decode_type, 
        "\t value: ", results.value, 
        "\t addr: ", results.address,
        "\t cmd: ", results.command);

Приятно, что здесь не используется дополнительная память и дополнительная обработка.


1

Я обычно (мучительно) придерживаюсь нескольких строк, Serial.printно когда это становится запутанным, я возвращаюсь к sprintf. Это раздражает тем, что у вас должен быть доступный буфер для этого.

Использование так же просто (??) как:

char buffer[35]; // you have to be aware of how long your data can be
                 // not forgetting unprintable and null term chars
sprintf(buffer,"var1:%i\tvar2:%i\tvar3:%i",var1,var2,var3);
Serial.println(buffer);

Предупреждение, хотя (по умолчанию) не поддерживает плавающие типы.


1
Спринтф это ужасная мерзость. Не безопасно, легко переполнять буферы и т. Д. И т. Д. Это инструмент 1960-х годов. Тем не менее, я тоже использую это, но это не для слабонервных ....
Дункан C

Чтобы избежать переполнения, используйте snprintf ... Кстати, большинство IDE модеров (НЕ IDE Arduino) проверит формат строки по предоставленным типам переменных и выдаст предупреждение.
следующий взлом

1

Использование 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;

Определение <<в Streaming.hфактически переводит , что в серию обычных Serial.print()вызовов. То есть <<является синтаксическим сахаром, реализованным без увеличения размера кода.

Если вы не Streaming.hустановили, получите Streaming5.zipот arduiniana.org . Разархивируйте его в директорию вашей библиотеки, например в ~/sketchbook/libraries. Добавьте линию #include <Streaming.h>в эскизы, где вы используете <<в качестве оператора потока.

Предоставляются спецификаторы преобразования базы _HEX, _DEC, _OCT и _BIN, а также функция _FLOAT (с количеством десятичных знаков) и endl. Например, чтобы вывести значения широты и долготы в форме типа «Ваши координаты -23.123, 135.4567», можно написать:

Serial << "Your coordinates are " << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;

Это также может быть написано как

Serial << F("Your coordinates are ") << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;

который будет хранить более длинную строку в PROGMEM вместо того, чтобы переносить ее в RAM.

Обратите внимание, Streaming.h что не создает никаких строк как таковых; он просто доставляет текст своих <<аргументов в поток. Класс PString в arduiniana может построить строки из потока входов, если строки вместо поточного вывода желательны и необходимы.


1

Использование будет зависеть от типа данных ваших переменных.

Если бы они были int, это было бы, %dили %i если бы они были string, это было бы%s

Упаковщик для печати

Вы можете изменить лимит в зависимости от ваших требований

#include <stdarg.h>
void p(char *fmt, ... ){
    char buf[128]; // resulting string limited to 128 chars
    va_list args;
    va_start (args, fmt );
    vsnprintf(buf, 128, fmt, args);
    va_end (args);
    Serial.print(buf); // Output result to Serial
}

Источник: https://playground.arduino.cc/Main/Printf

Примеры использования:

p("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3); // strings
p("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3); // numbers

ESP8266

Его встроенный в Serialкласс каркас. Нет необходимости в дополнительной библиотеке или функции.

// strings
Serial.printf("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3);
// numbers
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 будет в классе Print, но не в наиболее часто используемом пакете AVR
Juraj

@Juraj, вы правы, я только протестировал его на ESP8266, у которого он есть ( ссылка ), и подумал, что это ядро ​​Arduino. Буду обновлять мой ответ соответственно
Remi

для функции p я бы добавил еще одно понижение, если бы это было возможно.
Юрай

это старый вопрос, и я не могу судить о старых ответах, потому что я не знаю, что было доступно в 2014 году. Но сейчас есть библиотеки для обтекания потока Print в поток Print с реализацией printf.
Юрай


-1

С 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

1
Почему кто-то хочет сделать это вместо того, чтобы просто использовать printf()себя ?
Эдгар Бонет

-3
int Money_amount = 55;

Serial.print(String("New amount: $" + Money_amount));

Вы увидите на терминале:

New amount: $55

1
Вы не можете объединить int в c-строку с +оператором.
gre_gor
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.