¿Cómo imprimo múltiples variables en una cadena?


46

Digamos que tengo algunas variables que quiero imprimir en la terminal, ¿cuál es la forma más fácil de imprimirlas en una cadena?

Actualmente hago algo como esto:

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

¿Hay una mejor manera de hacer esto?


Una idea, pero no sé si funcionaría, es alguna modificación de esto ... De nuevo, no sé si esto es compatible con Arduino: stackoverflow.com/questions/804288/…
apnorton

Respuestas:


37

ardprintfes una función que pirateé juntos que simula printfla conexión en serie. Esta función (dada en la parte inferior) se puede pegar al comienzo de los archivos donde se necesita la función. No debe crear ningún conflicto.

Se le puede llamar similar a printf. Véalo en acción en este ejemplo:

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

}

La salida como se espera es:

test 2 123456789 g test 2.30

El prototipo de la función es:

int ardprintf(char *, ...);

Devuelve el número de argumentos detectados en la llamada a la función.

Esta es la definición de la función:

#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

** Para imprimir el %personaje, use %%. *


Ahora, disponible en Github Gists .


3
Buena idea, aunque sentí que podría ser más minimalista, así que reescribí esta versión a una sin buffering. Cualquier persona interesada puede consultar la esencia: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

Normalmente no pondría dos respuestas a una pregunta, pero acabo de encontrar esto hoy, donde puedes usar printf sin ningún búfer.

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

Esto todavía tiene la limitación de coma flotante.

editar: pensé que haría una pequeña prueba en esto, y funciona bastante bien. Agregué una mejor prueba al bucle con salida formateada.


Oh hombre, eso es genial. printf es mucho más seguro que sprintf. Te da cadenas de formato gratis, lo cual es genial. Buen truco. Gracias. (Votado)
Duncan C

Una pregunta: en su serial_putcharfunción, ¿por qué no hacer la declaración de devolución return !Serial.write(c);? ¿No es eso más limpio que un operador trinario para invertir el sentido de un valor de retorno booleano?
Duncan C

Ese es un buen punto y me gusta. El código no era mío y lo pegué cuando lo encontré.
Madivad

Gracias por la serial_putcharfuncion. Funciona de maravilla. :-) ¿Puedes arreglar la limitación de coma flotante ?
Greenonline

4

Esto probablemente no sea mejor, solo diferente. Puede usar el objeto String para la salida. Estos objetos permiten la concatenación y admiten el encasillado automático.

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

44
Obviamente, es importante tener cuidado con los límites de memoria. Muchas concatenaciones y otras operaciones de cadena en un solo lugar pueden usar una sorprendente cantidad de espacio.
Peter Bloomfield

@ PeterR.Bloomfield ¡Absolutamente cierto! Esa es la razón por la que mencioné que esta variante no es mejor;)
Klaus-Dieter Warzecha

4

Por lo general, usaba pestañas para hacer que las cosas se alineen mejor en la serie. Hacer que las cosas se alineen como yo permite que el arduino dispare lo más rápido posible al tiempo que puede notar ciertos cambios en las variables.

Intenta algo como esto:

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 algo como esto:

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

Honestamente, hago lo mismo ("\ t" y "\ n") y normalmente evito las campanas y silbatos del objeto String que hinchan el código.
Klaus-Dieter Warzecha

1
@KlausWarzecha, rara vez doy el nombre de la variable ya que están en bonitas columnas. También facilite ver impresiones aleatorias que no coinciden con esta sintaxis
Steven10172

4

Solo uso esto para depurar pero:

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

¿Qué es String $?
Juraj

LMFTFM (Déjame arreglar eso por mí).
linhartr22

2

Soy un novato en el mundo de Arduino, pero recientemente descubrí que esto es solo un C ++ normal (sin excepciones y probablemente polimorfismo). Pero aún puedes disfrutar de las plantillas. Entonces mi solución es usar las siguientes plantillas:

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

Lo bueno aquí es que no usa memoria adicional ni procesamiento adicional aquí.


1

Por lo general (dolorosamente) me quedo con varias líneas de Serial.printpero cuando se vuelve complicado vuelvo a sprintf. Es molesto porque tienes que tener un búfer disponible para ello.

El uso es tan simple (??) como:

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

Sin embargo, una advertencia: no admite (por defecto) los tipos flotantes.


1
Sprintf es una horrible abominación. No es seguro, fácil de sobrepasar sus memorias intermedias, etc. Es una herramienta de la década de 1960. Dicho esto, yo también lo uso, pero no es para los débiles de corazón ...
Duncan C

Para evitar el desbordamiento, use snprintf ... Por cierto, la mayoría de los IDE modernos (NO el IDE de Arduino) verificará el formato de cadena con los tipos de variables proporcionados y emitirá una advertencia.
next-hack

1

Usando Streaming.h, en lugar de

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

uno puede escribir

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

La definición de <<en Streaming.hefecto traduce eso en una serie de Serial.print()llamadas ordinarias . Es decir, <<es un azúcar sintáctico, implementado sin aumentar el tamaño del código.

Si no lo ha Streaming.hinstalado, obtenga Streaming5.zipde arduiniana.org . Descomprímalo en su directorio de bibliotecas, por ejemplo en ~/sketchbook/libraries. Agregue la línea #include <Streaming.h>dentro de los bocetos que usa <<como operador de flujo.

Se proporcionan los especificadores de conversión de base _HEX, _DEC, _OCT y _BIN, así como una función _FLOAT (con número de lugares decimales) y endl. Por ejemplo, para imprimir valores de latitud y longitud en una forma como "Sus coordenadas son -23.123, 135.4567" se podría escribir:

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

Esto también podría escribirse como

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

que mantendría la cadena más larga en PROGMEM en lugar de llevarla a la RAM.

Tenga en cuenta Streaming.h que no crea ninguna secuencia como tal; solo entrega el texto de sus <<argumentos a una secuencia. Una clase PString en arduiniana puede construir cadenas a partir de entradas de flujo, si se desean o necesitan cadenas en lugar de una salida fluida.


1

El uso dependerá del tipo de datos de sus variables.

Si lo son int, sería %do %i si lo fueran string, sería%s

Envoltorio para imprimir

Puede cambiar el límite según sus requisitos.

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

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

Ejemplos de uso:

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

Está integrado en la Serialclase del marco. No hay necesidad de biblioteca o función adicional.

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

Más detalles sobre consejos de formato en la página de referencia del formato printf: http://www.cplusplus.com/reference/cstdio/printf/

\n es la secuencia de escape para el avance de línea.

Las secuencias de escape se utilizan para representar ciertos caracteres especiales dentro de literales de cadena y literales de caracteres.

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

[EDITAR] - Como mencionó @Juraj, no está disponible en la mayoría de los módulos AVR. Así que agregué la mención ESP8266 y un contenedor printf para módulos AVR comunes


esto no es verdad. No hay clase de serie. printf estaría en la clase Print, pero no está en el paquete AVR más utilizado
Juraj

@Juraj tienes razón, solo lo probé en ESP8266 que lo tiene ( enlace ) y pensé que era de arduino core. Actualizaré mi respuesta en consecuencia
Remi

para la función p, agregaría un voto negativo más si fuera posible.
Juraj

Esta es una pregunta antigua y no puedo juzgar las respuestas antiguas porque no sé qué estaba disponible en 2014. Pero ahora hay bibliotecas para envolver un flujo de impresión en un flujo de impresión con la implementación de printf.
Juraj

0

Una posible solución es:

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


-1

Desde http://playground.arduino.cc/Main/Printf observé que esto funciona bien en mi mega2560

Eso es todo lo que funcionó, no hay necesidad de 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
¿Por qué alguien querría hacer esto en lugar de usarlo printf()solo ?
Edgar Bonet

-3
int Money_amount = 55;

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

Verás en la terminal:

New amount: $55

1
No puede concatenar un int a una cadena c con un +operador.
gre_gor