Como imprimo várias variáveis ​​em uma string?


46

Digamos que tenho algumas variáveis ​​que quero imprimir no terminal. Qual é a maneira mais fácil de imprimi-las em uma string?

Atualmente eu faço algo assim:

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

Existe uma maneira melhor de fazer isso?


Uma idéia, mas não sei se funcionaria, é alguma modificação disso ... Novamente, não sei se isso é suportado no Arduino: stackoverflow.com/questions/804288/…
apnorton

Respostas:


37

ardprintfé uma função que eu cortei junto que simula printfatravés da conexão serial. Esta função (fornecida na parte inferior) pode ser colada no início dos arquivos em que a função é necessária. Não deve criar conflitos.

Pode ser chamado de semelhante a printf. Veja em ação neste exemplo:

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

}

A saída conforme o esperado é:

test 2 123456789 g test 2.30

O protótipo da função é:

int ardprintf(char *, ...);

Retorna o número de argumentos detectados na chamada de função.

Esta é a definição da função:

#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 o %caractere, use %%. *


Agora, disponível nas listas principais do Github .


3
Boa ideia, embora eu achasse que poderia ser mais minimalista, então reescrevi esta versão para uma sem buffer. Qualquer pessoa interessada pode conferir a lista principal: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

Eu normalmente não colocar duas respostas a uma pergunta, mas eu só encontrei apenas este hoje, onde você pode usar printf sem 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);    
  }
}

Isso ainda tem a limitação de ponto flutuante.

edit: Eu pensei em fazer um pequeno teste sobre isso, e funciona muito bem. Eu adicionei um teste melhor ao loop com saída formatada.


Oh cara, isso é legal. printf é muito mais seguro que o sprintf. Dá a você formatar seqüências de graça, o que é ótimo. Truque legal. Obrigado. (Votado)
Duncan C

Uma pergunta: em sua serial_putcharfunção, por que não fazer a declaração de retorno return !Serial.write(c);? Não é mais limpo que um operador trinário por inverter a sensação de um valor de retorno booleano?
Duncan C

Esse é um bom argumento e eu gosto. O código não era meu e eu colei como o encontrei.
Madivad 30/09/14

Obrigado pela serial_putcharfunção. Isso funciona um prazer. :-) Você pode corrigir a limitação do ponto flutuante ?
Greenonline 22/02

4

Provavelmente não é melhor, apenas diferente. Você pode usar o objeto String para saída. Esses objetos permitem concatenação e suportam a conversão automática de tipos.

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
Obviamente, é importante ter cuidado com os limites de memória. Muitas concatenações e outras operações de cadeia de caracteres em um local podem usar uma quantidade surpreendente de espaço.
Peter Bloomfield

@ PeterR.Bloomfield Absolutamente verdade! Essa é a razão pela qual eu mencionei que esta variante não é melhor;)
Klaus-Dieter Warzecha

4

Eu geralmente usava guias para tornar as coisas mais alinhadas no Serial. Tendo as coisas alinhadas como eu, permitirei que o arduino seja disparado o mais rápido possível, ao mesmo tempo em que percebo certas alterações nas variáveis.

Tente algo como isto:

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

Ou algo parecido com isto:

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

Honestamente, faço o mesmo ("\ t" e "\ n") e normalmente evito os sinos e assobios do objeto String, que incha o código.
Klaus-Dieter Warzecha

1
@KlausWarzecha, raramente dou o nome da variável, pois estão em boas colunas. Também torná-lo mais fácil de ver impressões aleatórias que não correspondem a essa sintaxe
Steven10172

4

Eu só uso isso para depuração, mas:

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

o que é String $?
Juraj

LMFTFM (deixe-me consertar isso para mim).
linhartr22

2

Sou novato no mundo do Arduino, mas recentemente descobri que este é apenas um C ++ regular (sem exceções e provavelmente polimorfismo). Mas você ainda pode desfrutar de modelos. Portanto, minha solução é usar os seguintes modelos:

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

O legal aqui é que ele não usa nenhuma memória e processamento extra aqui.


1

Eu costumo (dolorosamente) ficar com várias linhas de Serial.printmas, quando fica complicado, eu volto para sprintf. É irritante porque você precisa ter um buffer disponível para isso.

O uso é tão simples (??) quanto:

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

Uma palavra de aviso, porém, (por padrão) não suporta tipos flutuantes.


1
sprintf é uma abominação horrível. Não é um tipo seguro, fácil de exceder seus buffers, etc. É uma ferramenta da década de 1960. Dito isto, eu usá-lo também, mas não é para os fracos de coração ....
Duncan C

Para evitar a sobrecarga, use snprintf ... A maioria dos IDEs moderados (NÃO o IDE do Arduino) verificará o formato da string nos tipos de variáveis ​​fornecidos e emitirá um aviso.
next-hack

1

Usando Streaming.h, no lugar de

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

alguém pode escrever

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

A definição de <<na Streaming.hverdade traduz isso em uma série de Serial.print()chamadas comuns . Ou seja, <<é o açúcar sintático, implementado sem aumentar o tamanho do código.

Se você não tiver Streaming.hinstalado, começar Streaming5.zipa partir arduiniana.org . Descompacte-o no diretório de bibliotecas, por exemplo, em ~/sketchbook/libraries. Adicione a linha #include <Streaming.h>nos esboços onde você usa <<como operador de fluxo.

Os especificadores de conversão base _HEX, _DEC, _OCT e _BIN são fornecidos, bem como uma função _FLOAT (com número de casas decimais) e endl. Por exemplo, para imprimir valores de latitude e longitude em um formato como "Suas coordenadas são -23.123, 135.4567", pode-se escrever:

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

Isso também pode ser escrito como

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

que manteria a seqüência mais longa no PROGMEM em vez de trazê-la para a RAM.

Observe Streaming.h que não cria nenhuma string como tal; apenas entrega o texto de seus <<argumentos a um fluxo. Uma classe PString na arduiniana pode construir cadeias de caracteres de entradas de fluxo, se forem desejadas ou necessárias cadeias em vez de saída de fluxo.


1

O uso dependerá do tipo de dados de suas variáveis.

Se eles são int, seria %dou %i Se são string, seria%s

Wrapper para impressãof

Você pode alterar o limite com base em seus 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
}

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

Exemplos 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

É incorporado na Serialclasse da estrutura. Não há necessidade de biblioteca ou função 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);

Mais detalhes sobre dicas de formatação na página de referência do formato printf: http://www.cplusplus.com/reference/cstdio/printf/

\n é a sequência de escape para o avanço de linha.

Sequências de escape são usadas para representar certos caracteres especiais dentro de literais de cadeia e literais de caracteres.

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

[EDIT] - Como o @Juraj mencionou, ele não está disponível na maioria dos módulos do AVR. Então eu adicionei a menção ESP8266 e um wrapper printf para módulos AVR comuns


isso não é verdade. não há classe serial. printf seria na classe de impressão, mas não está no pacote AVR mais utilizado
Juraj

@Juraj você está certo, eu só testei no ESP8266 que tem ( link ) e pensei que era do núcleo do arduino. Atualizo minha resposta de acordo.
Remi

para a função p, eu acrescentaria mais um voto negativo, se possível.
Juraj

essa é uma pergunta antiga e não posso julgar as respostas antigas porque não sei o que estava disponível em 2014. mas agora existem bibliotecas para agrupar um fluxo de impressão em um fluxo de impressão com a implementação printf.
Juraj

0

Uma solução possível é:

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


-1

De http://playground.arduino.cc/Main/Printf , observei que isso está funcionando bem no meu mega2560

É tudo o que funcionou, não há necessidade de vsnprintf_P ou 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 que alguém iria querer fazer isso em vez de apenas usar a printf()si próprio ?
Edgar Bonet

-3
int Money_amount = 55;

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

Você verá no terminal:

New amount: $55

1
Você não pode concatenar um int para um c-string com um +operador.
gre_gor
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.