Come stampare più variabili in una stringa?


46

Supponiamo di avere alcune variabili che voglio stampare sul terminale, qual è il modo più semplice per stamparle in una stringa?

Attualmente faccio qualcosa del genere:

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

C'è un modo migliore per farlo?


Un'idea, ma non so se avrebbe funzionato, è una certa modifica di questo ... Ancora una volta, non so se questo è supportato su Arduino: stackoverflow.com/questions/804288/...
apnorton

Risposte:


37

ardprintfè una funzione che ho hackerato insieme che simula printftramite la connessione seriale. Questa funzione (indicata in basso) può essere incollata all'inizio dei file in cui è necessaria la funzione. Non dovrebbe creare alcun conflitto.

Può essere chiamato simile a printf. Guardalo in azione in questo esempio:

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

}

L'output come previsto è:

test 2 123456789 g test 2.30

Il prototipo della funzione è:

int ardprintf(char *, ...);

Restituisce il numero di argomenti rilevati nella chiamata di funzione.

Questa è la definizione della funzione:

#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

** Per stampare il %personaggio, utilizzare %%. *


Ora, disponibile su Github .


3
Bella idea, anche se ho pensato che potesse essere più minimalista, quindi ho riscritto questa versione su una senza buffering. Chiunque sia interessato può dare un'occhiata all'argomento
eleotlecram

13

Io normalmente non mettere due risposte a una domanda, ma ho appena trovato questo oggi, in cui è possibile utilizzare printf senza buffer.

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

Questo ha ancora la limitazione in virgola mobile.

modifica: ho pensato di fare un piccolo test su questo, e funziona abbastanza bene. Ho aggiunto un test migliore al loop con output formattato.


Oh amico, va bene. printf è molto più sicuro di sprintf. Ti dà le stringhe di formato gratuitamente, il che è fantastico. Bel trucco. Grazie. (Votato)
Duncan C

Una domanda: nella tua serial_putcharfunzione, perché non fare la dichiarazione di ritorno return !Serial.write(c);? Non è più pulito di un operatore trinario per invertire il senso di un valore di ritorno booleano?
Duncan C,

Questo è un buon punto e mi piace. Il codice non era mio e l'ho incollato come l'ho trovato.
Madivad,

Grazie per la serial_putcharfunzione Funziona a meraviglia. :-) Puoi correggere la limitazione in virgola mobile ?
Greenonline,

4

Questo probabilmente non è migliore, solo diverso. È possibile utilizzare l' oggetto String per l'output. Questi oggetti consentono la concatenazione e supportano la tipografia automatica.

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
Ovviamente è importante fare attenzione ai limiti di memoria. Molte concatenazioni e altre operazioni sulle stringhe in un unico posto possono utilizzare una quantità sorprendente di spazio.
Peter Bloomfield,

@ PeterR.Bloomfield Assolutamente vero! Questo è il motivo per cui ho detto che questa variante non è migliore;)
Klaus-Dieter Warzecha,

4

Di solito usavo le schede per rendere le cose migliori in serie. Avere le cose allineate come faccio consente all'arduino di sparare il più velocemente possibile mentre sono in grado di notare alcuni cambiamenti nelle variabili.

Prova qualcosa del genere:

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

O qualcosa del genere:

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

Onestamente, faccio lo stesso ("\ t" e "\ n") ed evito normalmente le campane e i fischietti dell'oggetto String che gonfiano il codice.
Klaus-Dieter Warzecha,

1
@KlausWarzecha, raramente do il nome della variabile in quanto sono in belle colonne.
Semplifica anche la

4

Lo uso solo per il debug ma:

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

che cos'è String $?
Juraj,

LMFTFM (Fammi riparare per me).
linhartr22,

2

Sono un principiante nel mondo di Arduino, ma recentemente ho scoperto che questo è solo un normale C ++ (senza eccezioni e probabilmente polimorfismo). Ma puoi ancora goderti i modelli. Quindi la mia soluzione è utilizzare i seguenti modelli:

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

La cosa bella qui è che qui non usa memoria aggiuntiva o elaborazione extra.


1

Di solito (dolorosamente) rimango con più linee di Serial.printma quando diventa contorto torno a sprintf. È fastidioso il fatto che devi avere un buffer disponibile per questo.

L'utilizzo è semplice (??) come:

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

Un avvertimento, tuttavia, non supporta (per impostazione predefinita) i tipi mobili.


1
sprintf è un abominio orribile. Non digitare sicuro, facile da sovraccaricare i tuoi buffer, ecc. Ecc. È uno strumento degli anni '60. Detto questo, lo uso anche io, ma non è per i deboli di cuore ....
Duncan C

Per evitare il sovraccarico, utilizzare snprintf ... A proposito, la maggior parte degli IDE moderati (NON l'IDE Arduino) verificherà il formato della stringa rispetto ai tipi di variabili forniti e genererà un avviso.
prossimo hack del

1

Usando Streaming.h, al posto di

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

si può scrivere

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

La definizione di <<in Streaming.heffetti la traduce in una serie di Serial.print()chiamate ordinarie . Cioè, <<è lo zucchero sintattico, implementato senza aumentare la dimensione del codice.

Se non è stato Streaming.hinstallato, ottenere Streaming5.zipda arduiniana.org . Decomprimilo nella directory delle tue librerie, ad esempio in ~/sketchbook/libraries. Aggiungi la linea #include <Streaming.h>all'interno di schizzi in cui utilizzi <<come operatore di flusso.

Vengono forniti gli identificatori di conversione di base _HEX, _DEC, _OCT e _BIN, nonché una funzione _FLOAT (con numero di cifre decimali) e endl. Ad esempio, per stampare i valori di latitudine e longitudine in una forma come "Le tue coordinate sono -23.123, 135.4567" si potrebbe scrivere:

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

Questo potrebbe anche essere scritto come

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

che manterrebbe la stringa più lunga in PROGMEM invece di portarla nella RAM.

Nota, Streaming.h non crea stringhe come tali; fornisce semplicemente il testo dei suoi <<-argument a uno stream. Una classe PString in arduiniana può creare stringhe dagli input di flusso, se si desiderano o sono necessarie stringhe anziché output in streaming.


1

L'utilizzo dipenderà dal tipo di dati delle variabili.

Se lo sono int, lo sarebbero %do %i Se lo fossero string, lo sarebbero%s

Wrapper per printf

È possibile modificare il limite in base alle proprie esigenze

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

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

Esempi di utilizzo:

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

È integrato nella Serialclasse del framework. Non è necessaria alcuna libreria o funzione aggiuntiva.

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

Maggiori dettagli sui suggerimenti per la formattazione nella pagina di riferimento del formato printf: http://www.cplusplus.com/reference/cstdio/printf/

\n è la sequenza di escape per l'alimentazione di riga.

Le sequenze di escape vengono utilizzate per rappresentare alcuni caratteri speciali all'interno di stringhe e caratteri letterali.

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

[EDIT] - Come accennato da @Juraj, non è disponibile sulla maggior parte dei moduli AVR. Quindi ho aggiunto la menzione ESP8266 e un wrapper printf per i comuni moduli AVR


questo non è vero. non esiste una classe seriale. printf sarebbe nella classe Print, ma non è nel pacchetto AVR più usato
Juraj

@Juraj hai ragione, l'ho provato solo su ESP8266 che ce l'hanno ( link ) e ho pensato che fosse dal core Arduino. Aggiornerò di conseguenza la mia risposta
Remi,

per la funzione p aggiungerei un altro downvote se fosse possibile.
Juraj,

questa è una vecchia domanda e non posso giudicare le vecchie risposte perché non so cosa fosse disponibile nel 2014. ma ora ci sono librerie per avvolgere un flusso di stampa in un flusso di stampa con l'implementazione di printf.
Juraj,

0

Una possibile soluzione è:

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


-1

Da http://playground.arduino.cc/Main/Printf ho notato che funziona perfettamente sul mio mega2560

È tutto ciò che ha funzionato, non è necessario vsnprintf_P o 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
Perché qualcuno dovrebbe voler fare questo invece di usareprintf() solo se stesso ?
Edgar Bonet,

-3
int Money_amount = 55;

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

Vedrai sul terminale:

New amount: $55

1
Non è possibile concatenare un int a una stringa C con un +operatore.
gre_gor,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.