Comment imprimer plusieurs variables dans une chaîne?

46

Supposons que je souhaite imprimer certaines variables sur le terminal. Quel est le moyen le plus simple de les imprimer dans une chaîne?

Actuellement, je fais quelque chose comme ça:

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

Y a-t-il une meilleure manière de faire cela?

Sachleen
la source
Une idée, mais je ne sais pas si cela fonctionnerait, est une modification de cela ... Encore une fois, je ne sais pas si cela est supporté par Arduino: stackoverflow.com/questions/804288/…
apnorton 13/02

Réponses:

37

ardprintfest une fonction que j'ai piratée ensemble et qui simule printfune connexion série. Cette fonction (indiquée en bas) peut être collée au début des fichiers où elle est nécessaire. Cela ne devrait pas créer de conflits.

Il peut être appelé similaire à printf. Voyez-le en action dans cet exemple:

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

}

Le résultat attendu est:

test 2 123456789 g test 2.30

Le prototype de fonction est:

int ardprintf(char *, ...);

Il retourne le nombre d'arguments détectés dans l'appel de fonction.

C'est la définition de la fonction:

#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

** Pour imprimer le %caractère, utilisez %%. *


Maintenant, disponible sur Gistsub Gists .

Asheeshr
la source
3
Belle idée, même si j’ai pensé que cela pourrait être plus minimaliste, j’ai donc réécrit cette version dans une version sans mise en mémoire tampon. Toute personne intéressée peut consulter l’essentiel: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram le
13

Normalement, je ne mettrais pas deux réponses à une question, mais je viens juste de trouver ceci aujourd'hui, où vous pouvez utiliser printf sans aucun tampon.

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

Cela a toujours la limitation en virgule flottante.

edit: Je pensais faire un peu de test à ce sujet, et cela fonctionne assez bien. J'ai ajouté un meilleur test à la boucle avec une sortie formatée.

Madivad
la source
Oh mec, c'est cool. printf est beaucoup plus sûr que sprintf. Il vous donne des chaînes de formatage gratuitement, ce qui est excellent. Truc cool. Merci. (Vote)
Duncan C
Une question: dans votre serial_putcharfonction, pourquoi ne pas faire la déclaration de retour return !Serial.write(c);? N'est-ce pas plus propre qu'un opérateur trinaire pour inverser le sens d'une valeur de retour booléenne?
Duncan C
C'est un bon point et j'aime ça. Le code n'était pas le mien et je l'ai collé tel que je l'ai trouvé.
Madivad
Merci pour la serial_putcharfonction. Cela fonctionne un régal. :-) Pouvez-vous corriger la limitation en virgule flottante ?
Greenonline le
4

Ce n'est probablement pas mieux, mais différent. Vous pouvez utiliser l' objet String pour la sortie. Ces objets permettent la concaténation et prennent en charge la conversion automatique.

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]);
}
Klaus-Dieter Warzecha
la source
4
Évidemment, il est important de faire attention aux limites de la mémoire. Un grand nombre de concaténations et d’autres opérations sur les chaînes au même endroit peuvent utiliser une quantité surprenante d’espace.
Peter Bloomfield
@ PeterR.Bloomfield Absolument vrai! C'est la raison pour laquelle j'ai mentionné que cette variante n'était pas meilleure;)
Klaus-Dieter Warzecha
4

J'utilisais généralement des onglets pour améliorer l'alignement dans la publication en série. Avoir les choses alignées comme si je permettais à l’arduino de se déclencher le plus rapidement possible tout en pouvant remarquer certains changements dans les variables.

Essayez quelque chose comme ça:

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 quelque chose comme ça:

Serial.print("Var 1:");Serial.println(var1);
Serial.print("\tVar 2:");Serial.println(var2);
Serial.print("\tVar 3:");Serial.println(var3);
Steven10172
la source
Honnêtement, je fais la même chose ("\ t" et "\ n") et évite normalement les cloches et sifflets d’objet String encombrant le code.
Klaus-Dieter Warzecha
1
@ KlausWarzecha, je donne rarement le nom de la variable car ils sont dans de belles colonnes.
Facilitez également l'affichage d'
4

Je ne l'utilise que pour le débogage mais:

int a = 10;
int b = 20;
Serial.println("a = " + String(a) + " and b = " + String(b));
linhartr22
la source
qu'est-ce que String $?
Juraj
LMFTFM (Permettez-moi de résoudre ce problème pour moi).
linhartr22
2

Je suis novice dans le monde Arduino, mais j'ai récemment découvert qu'il ne s'agissait que d'un C ++ standard (sans exceptions et probablement polymorphisme). Mais vous pouvez toujours profiter des modèles. Ma solution consiste donc à utiliser les modèles suivants:

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 bonne chose ici est qu'il n'utilise pas de mémoire supplémentaire et de traitement supplémentaire ici.

utilisateur270049
la source
1

Je colle habituellement (douloureusement) à plusieurs lignes, Serial.printmais quand cela devient compliqué, je retourne à sprintf. C'est embêtant dans la mesure où vous devez avoir un tampon disponible pour cela.

L'utilisation est aussi simple (??) que:

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 mot d'avertissement cependant, il (par défaut) ne supporte pas les types flottants.

Madivad
la source
1
Sprintf est une horrible abomination. Pas sûr, facile à surcharger vos tampons, etc. C'est un outil des années 1960. Ceci dit, je l'utilise aussi, mais ce n'est pas pour les âmes sensibles ....
Duncan C
Pour éviter les dépassements, utilisez snprintf ... BTW. La plupart des IDE modérés (PAS l'IDE Arduino) vérifieront le format de la chaîne par rapport aux types de variable fournis et émet un avertissement.
next-hack
1

Utilisation Streaming.h, à la place de

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

on peut écrire

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

La définition de <<in Streaming.htraduit en fait cela en une série d' Serial.print()appels ordinaires . C'est-à-dire qu'il <<s'agit d'un sucre syntaxique, mis en œuvre sans augmenter la taille du code.

Si vous n'avez pas Streaming.hinstallé, procurez-vous Streaming5.zipde arduiniana.org . Décompressez-le dans le répertoire de vos bibliothèques, par exemple, dans ~/sketchbook/libraries. Ajoutez la ligne #include <Streaming.h>dans les esquisses où vous utilisez en <<tant qu'opérateur de flux.

Les spécificateurs de conversion de base _HEX, _DEC, _OCT et _BIN sont fournis, ainsi qu'une fonction _FLOAT (avec le nombre de décimales) et endl. Par exemple, pour imprimer les valeurs de latitude et de longitude dans un formulaire du type "Vos coordonnées sont -23.123, 135.4567", on pourrait écrire:

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

Cela pourrait aussi être écrit comme

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

qui conserverait la chaîne la plus longue dans PROGMEM au lieu de la placer dans la RAM.

Remarque, Streaming.h ne construit aucune chaîne en tant que telle; il ne fait que transmettre le texte de ses <<arguments à un flux. Une classe PString chez arduiniana peut créer des chaînes à partir d'entrées de flux, si des chaînes au lieu d'une sortie en flux sont souhaitées ou nécessaires.

James Waldby - jwpat7
la source
1

L'utilisation dépendra du type de données de vos variables.

Si elles le sont int, ce serait %dou %i si elles le seraient string, ce serait%s

Wrapper pour printf

Vous pouvez modifier la limite en fonction de vos besoins

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

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

Exemples d'utilisation:

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

Son intégré dans la Serialclasse du cadre. Pas besoin de bibliothèque ou de fonction supplémentaire.

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

Plus de détails sur les astuces de formatage sur la page de référence du format printf: http://www.cplusplus.com/reference/cstdio/printf/

\n est la séquence d'échappement du saut de ligne.

Les séquences d'échappement servent à représenter certains caractères spéciaux dans les littéraux de chaîne et les littéraux de caractères.

Source: http://fr.cppreference.com/w/cpp/language/escape

[EDIT] - Comme @Juraj l’a mentionné, il n’est pas disponible sur la plupart des modules AVR. J'ai donc ajouté la mention ESP8266 et un wrapper printf pour les modules AVR courants

Rémi
la source
ce n'est pas vrai. il n'y a pas de classe Serial. printf serait dans la classe Print, mais ce n'est pas dans le package AVR le plus utilisé
Juraj
@ Juraj vous avez raison, je ne l'ai testé que sur l'ESP8266 qui l'a ( lien ) et je pensais que c'était à partir du noyau arduino. Mettra à jour ma réponse en conséquence
Remi
pour la fonction p, j'ajouterais un autre vote négatif si cela était possible.
Juraj
c’est une vieille question et je ne peux pas juger les anciennes réponses parce que je ne sais pas ce qui était disponible en 2014. Mais il existe maintenant des bibliothèques pour encapsuler un flux Print dans un flux Print avec l’implémentation de printf.
Juraj
0

Une solution possible est:

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

Iain Crawford
la source
-1

De http://playground.arduino.cc/Main/Printf j'ai observé que cela fonctionne très bien sur mon méga2560

C'est tout ce que cela a juste fonctionné, pas besoin 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
rzr
la source
1
Pourquoi quelqu'un voudrait-il faire cela au lieu de simplement utiliser printf()lui - même ?
Edgar Bonet
-3
int Money_amount = 55;

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

Vous verrez sur le terminal:

New amount: $55
Michael Kushner
la source
1
Vous ne pouvez pas concaténer un int avec une c-string avec un +opérateur.
gre_gor