Wie drucke ich mehrere Variablen in einer Zeichenfolge?


46

Angenommen, ich habe einige Variablen, die ich auf dem Terminal ausdrucken möchte. Wie kann ich sie am einfachsten in einer Zeichenfolge ausdrucken?

Momentan mache ich so etwas:

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

Gibt es einen besseren Weg, dies zu tun?


Eine Idee, aber ich weiß nicht, ob es funktionieren würde, ist eine Modifikation davon ... Auch hier weiß ich nicht, ob dies von Arduino unterstützt wird: stackoverflow.com/questions/804288/…
apnorton

Antworten:


37

ardprintfist eine Funktion, die ich zusammen gehackt habe und die printfüber die serielle Verbindung simuliert . Diese Funktion (unten angegeben) kann am Anfang der Dateien eingefügt werden, in denen die Funktion benötigt wird. Es sollten keine Konflikte entstehen.

Man kann es ähnlich nennen wie printf. Sehen Sie es in Aktion in diesem Beispiel:

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);

}

Die erwartete Ausgabe ist:

test 2 123456789 g test 2.30

Der Funktionsprototyp ist:

int ardprintf(char *, ...);

Es gibt die Anzahl der im Funktionsaufruf erkannten Argumente zurück.

Dies ist die Funktionsdefinition:

#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

** %Verwenden Sie zum Drucken des Zeichens %%. *


Jetzt bei Github erhältlich .


3
Gute Idee, obwohl ich der Meinung war, dass es minimalistischer sein könnte, also habe ich diese Version ohne Pufferung auf eine umgeschrieben. Alle Interessierten können sich die Liste ansehen: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

Normalerweise würde ich auf eine Frage keine zwei Antworten geben, aber ich habe dies erst heute gefunden, wo Sie printf ohne Puffer verwenden können.

// 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);    
  }
}

Dies hat immer noch die Gleitkomma-Begrenzung.

edit: Ich dachte, ich würde ein wenig testen, und es funktioniert ganz gut. Ich habe der Schleife einen besseren Test mit formatierter Ausgabe hinzugefügt.


Oh man, das ist cool. printf ist viel sicherer als sprintf. Es gibt Ihnen kostenlos Format-Strings, was großartig ist. Cooler Trick. Vielen Dank. (Abgestimmt)
Duncan C

Eine Frage: Geben Sie in Ihrer serial_putcharFunktion die return-Anweisung ab return !Serial.write(c);. Ist das nicht sauberer als ein trinärer Operator zum Invertieren des Sinns eines booleschen Rückgabewerts?
Duncan C

Das ist ein guter Punkt und ich mag es. Der Code gehörte nicht mir und ich habe ihn so eingefügt, wie ich ihn gefunden habe.
Madivad

Danke für die serial_putcharFunktion. Es funktioniert ein Vergnügen. :-) Kannst du die Fließkommabegrenzung korrigieren ?
Greenonline

4

Das ist wahrscheinlich nicht besser, nur anders. Sie können das String- Objekt für die Ausgabe verwenden. Diese Objekte ermöglichen die Verkettung und unterstützen das automatische Typecasting.

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
Natürlich ist es wichtig, auf Speicherbeschränkungen zu achten. Viele Verkettungen und andere Zeichenfolgenoperationen an einem Ort können überraschend viel Platz beanspruchen.
Peter Bloomfield

@ PeterR.Bloomfield Absolut wahr! Das ist der Grund, warum ich erwähnt habe, dass diese Variante nicht besser ist;)
Klaus-Dieter Warzecha

4

Normalerweise habe ich Tabs verwendet, um die Reihenfolge in der Serie zu verbessern. Wenn die Dinge so ausgerichtet sind, wie ich es tue, kann der Arduino so schnell wie möglich feuern, während er in der Lage ist, bestimmte Änderungen in den Variablen zu bemerken.

Versuchen Sie so etwas:

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();

Oder so ähnlich:

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

Ehrlich gesagt, mache ich das Gleiche ("\ t" und "\ n") und vermeide normalerweise das Aufblähen von String-Objekten.
Klaus-Dieter Warzecha

1
@KlausWarzecha, ich gebe selten den Variablennamen an, da sie in schönen Spalten stehen. Machen Sie es auch einfacher, zufällige Ausdrucke zu sehen, die dieser Syntax nicht entsprechen
Steven10172

4

Ich benutze dies nur zum Debuggen, aber:

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

Was ist String $?
Juraj

LMFTFM (Lass mich das für mich reparieren).
Linhartr22

2

Ich bin ein Neuling in der Arduino-Welt, aber ich habe kürzlich festgestellt, dass dies nur ein reguläres C ++ ist (ohne Ausnahmen und wahrscheinlich Polymorphismus). Sie können jedoch weiterhin Vorlagen genießen. Meine Lösung besteht also darin, folgende Vorlagen zu verwenden:

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);

Das Schöne dabei ist, dass hier kein zusätzlicher Speicher und keine zusätzliche Verarbeitung erforderlich ist.


1

Ich halte mich normalerweise (schmerzhaft) an mehrere Zeilen von, Serial.printaber wenn es verworren ist, gehe ich zurück zu sprintf. Es ist ärgerlich, dass Sie einen verfügbaren Puffer dafür haben müssen.

Die Verwendung ist so einfach (??) wie:

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);

Ein Hinweis: Floating-Typen werden (standardmäßig) nicht unterstützt.


1
Sprintf ist ein schrecklicher Gräuel. Nicht typensicher, leicht zu überlaufende Puffer usw. Es ist ein Werkzeug aus den 1960er Jahren. Das heißt, ich benutze es auch, aber es ist nicht für schwache Nerven ....
Duncan C

Verwenden Sie snprintf ..., um ein Überlaufen zu vermeiden. Übrigens überprüfen die meisten moder-IDEs (NICHT die Arduino-IDE) das Zeichenfolgenformat anhand der bereitgestellten Variablentypen und geben eine Warnung aus.
Next-Hack

1

Verwenden Sie Streaming.hanstelle von

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

man kann schreiben

Serial << "Var 1:" << var1) << " Var 2:" << var2 << " Var 3:" << var3 << endl;

Die Definition von <<in Streaming.hin effect übersetzt dies in eine Reihe von gewöhnlichen Serial.print()Aufrufen. Das heißt, es <<handelt sich um syntaktischen Zucker, der ohne Erhöhung der Codegröße implementiert wird.

Wenn Sie nicht Streaming.hinstalliert haben, erhalten Sie Streaming5.zipvon arduiniana.org . Entpacken Sie es in Ihr Bibliotheksverzeichnis, zum Beispiel in ~/sketchbook/libraries. Fügen Sie die Linie #include <Streaming.h>in die Skizzen ein, die Sie <<als Stream-Operator verwenden.

Es werden die Basisumwandlungsspezifizierer _HEX, _DEC, _OCT und _BIN sowie eine _FLOAT-Funktion (mit der Anzahl der Dezimalstellen) und bereitgestellt endl. Wenn Sie beispielsweise Breiten- und Längengrade in einem Formular wie "Ihre Koordinaten sind -23,123, 135,4567" drucken möchten, können Sie Folgendes eingeben:

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

Dies könnte auch geschrieben werden als

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

Das würde den längeren String in PROGMEM behalten, anstatt ihn in den RAM zu bringen.

Beachten Sie, Streaming.h dass keine Zeichenfolgen als solche erstellt werden. es liefert nur den Text seiner <<Argumente an einen Stream. Eine PString-Klasse in arduiniana kann Zeichenfolgen aus Stream-Eingaben erstellen , wenn Zeichenfolgen anstelle von Stream-Ausgaben gewünscht oder benötigt werden.


1

Die Verwendung hängt vom Datentyp Ihrer Variablen ab.

Wenn sie sind int, wäre es %doder %i wenn sie sind string, wäre es%s

Wrapper für printf

Sie können das Limit gemäß Ihren Anforderungen ändern

#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
}

Quelle: https://playground.arduino.cc/Main/Printf

Anwendungsbeispiele:

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

Es ist in der SerialKlasse des Frameworks eingebaut . Keine Notwendigkeit für zusätzliche Bibliothek oder Funktion.

// 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);

Weitere Informationen zu Formatierungstipps finden Sie auf der Referenzseite zum PrintF-Format: http://www.cplusplus.com/reference/cstdio/printf/

\n ist die Escape-Sequenz für den Zeilenvorschub.

Escape-Sequenzen werden verwendet, um bestimmte Sonderzeichen innerhalb von String-Literalen und Zeichenliteralen darzustellen.

Quelle: http://en.cppreference.com/w/cpp/language/escape

[ BEARBEITEN ] - Wie bei @Juraj bereits erwähnt, ist es bei den meisten AVR-Modulen nicht verfügbar. Also habe ich ESP8266 erwähnt und einen printf-Wrapper für gängige AVR-Module hinzugefügt


das ist nicht wahr. Es gibt keine serielle Klasse. printf wäre in der Print-Klasse, aber es ist nicht in der am häufigsten verwendeten AVR-Paket
Juraj

@Juraj du hast recht, ich habe es nur auf ESP8266 getestet, die es haben ( Link ) und dachte, es war von Arduino Core. Ich werde meine Antwort entsprechend aktualisieren
Remi

für die p-funktion würde ich noch eine downvote hinzufügen, wenn es möglich wäre.
Juraj

Dies ist eine alte Frage, und ich kann die alten Antworten nicht beurteilen, da ich nicht weiß, was 2014 verfügbar war. Jetzt gibt es Bibliotheken, mit denen ein Print-Stream in einen Print-Stream mit printf-Implementierung eingeschlossen werden kann.
Juraj

0

Eine mögliche Lösung ist:

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


-1

Unter http://playground.arduino.cc/Main/Printf habe ich festgestellt, dass dies auf meinem mega2560 einwandfrei funktioniert

Das ist alles, was es gerade funktioniert hat, keine Notwendigkeit für vsnprintf_P oder 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
Warum sollte jemand dies tun wollen, anstatt sich selbst zu benutzenprintf() ?
Edgar Bonet

-3
int Money_amount = 55;

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

Sie sehen am Terminal:

New amount: $55

1
Sie können eine Ganzzahl nicht mit einem +Operator zu einer C-Zeichenfolge verketten .
gre_gor
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.