Як надрукувати декілька змінних у рядку?


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 - набагато безпечніший за спринт. Це дає вам рядки формату безкоштовно, що чудово. Класна хитрість. Дякую. (Проголосовано)
Duncan C

Одне запитання: чому у вашій serial_putcharфункції не зробити заяву про повернення return !Serial.write(c);? Хіба це не чистіше, ніж тринаціональний оператор для інвертування почуття булевого повернення?
Duncan 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

Зазвичай я використовував вкладки, щоб покращити роботу речей у серіалі. Такі речі, як я, дозволяють ардуїно стріляти якомога швидше, маючи можливість помітити певні зміни змінних.

Спробуйте щось подібне:

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 з роздуттям коду.
Klaus-Dieter Warzecha

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-х років. Це сказало, що я також його використовую, але це не для слабкого серця ....
Duncan C

Щоб уникнути перевиконання, використовуйте snprintf ... BTW Більшість сучасних IDE (НЕ Arduino IDE) перевірять формат рядка відповідно до наданих типів змінних та видадуть попередження.
наступний хак

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 замість того, щоб вносити його в оперативну пам'ять.

Зауважте, Streaming.h не створює жодних рядків як таких; він просто доставляє текст своїх <<аргументів до потоку. Клас PString в arduiniana може побудувати рядки з потоку входів, якщо рядки замість поточного виведення бажані й необхідні.


1

Використання буде залежати від типу даних ваших змінних.

Якби вони були int, то було б %dабо %i Якби вони були string, то було б%s

Обгортка для printf

Ви можете змінити ліміт, виходячи зі своїх вимог

#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 - послідовність відходу для стрічки каналу.

Східні послідовності використовуються для представлення певних спеціальних символів у рядкових літералах та символах.

Джерело: http://en.cppreference.com/w/cpp/language/escape

[EDIT] - Як зазначав @Juraj, він недоступний для більшості модулів AVR. Тому я додав згадку про ESP8266 та обгортку printf для загальних модулів AVR


це не правда. не існує серійного класу. printf був би в класі друку, але його немає в найбільш використовуваному пакеті AVR
Juraj

@Juraj Ви маєте рацію, я тестував його лише на ESP8266, у яких є ( посилання ), і я вважав, що це з ядра arduino. Відповідно
Ремі

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

це старе запитання, і я не можу судити про старі відповіді, тому що я не знаю, що було доступно в 2014 році, але зараз є бібліотеки для загортання потоку друку в потік 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-string з +оператором.
gre_gor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.