Bir dizgede birden fazla değişkeni nasıl yazdırabilirim?


46

Terminale yazdırmak istediğim bazı değişkenler var diyelim, dizgede yazdırmanın en kolay yolu nedir?

Şu anda böyle bir şey yapıyorum:

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

Bunu yapmanın daha iyi bir yolu var mı?


Bir fikir, ama işe
yarayıp yaramayacağını

Yanıtlar:


37

ardprintfprintfSeri bağlantı üzerinden taklit eden bir araya getirdiğim bir işlev . Bu işlev (altta verilen), işlevin gerekli olduğu dosyaların başlangıcına yapıştırılabilir. Herhangi bir çatışma yaratmamalı.

Benzer olarak adlandırılabilir printf. Bu örnekte çalışırken görün:

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

}

Beklendiği gibi çıktı:

test 2 123456789 g test 2.30

İşlev prototipi:

int ardprintf(char *, ...);

İşlev çağrısında algılanan argüman sayısını döndürür.

İşlev tanımı budur:

#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

** %Karakteri yazdırmak için , kullanın %%. *


Şimdi, Github rehberlerinde mevcut .


3
Güzel bir fikir, daha minimalist olabileceğimi düşünmeme rağmen, bu sürümü tamponlama olmadan bir başkasına yazdım. İlgilenen herkes özü kontrol edebilir: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

Normalde bir soruya iki cevap koymak olmaz, ama ben sadece buldum bu herhangi tampon olmadan printf kullanabilirsiniz bugün.

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

Bu hala kayan nokta sınırlamasına sahiptir.

düzenleme: Bu konuda biraz test yapacağımı düşündüm ve oldukça iyi çalışıyor. Biçimlendirilmiş çıktı ile döngüye daha iyi bir test ekledim.


Ah dostum, bu harika. printf, sprintf'den çok daha güvenlidir. Dizeleri ücretsiz olarak biçimlendiriyor, ki bu harika. Harika numara. Teşekkürler. (Oy verildi)
Duncan C

Bir soru: serial_putcharİşlevinizde neden return ifadesini yazmıyorsunuz return !Serial.write(c);? Bir boolean dönüş değeri duygusunu tersine çevirmek için triner operatöründen daha temiz değil mi?
Duncan C

Bu iyi bir nokta ve hoşuma gitti. Kod benim değildi ve bulduğum gibi yapıştırdım.
Madivad

serial_putcharİşlev için teşekkürler . Bir tedavi çalışır. :-) Kayan nokta sınırlamasını düzeltebilir misiniz ?
Greenonline

4

Bu muhtemelen daha iyi değil, sadece farklı. String nesnesini çıktı için kullanabilirsiniz . Bu nesneler bitiştirmeye izin verir ve otomatik tip belirlemeyi destekler.

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
Belli ki hafıza sınırlarına dikkat etmek önemlidir. Tek bir yerde çok sayıda birleştirme ve dize işlemi şaşırtıcı miktarda boşluk kullanabilir.
Peter Bloomfield

@ PeterR.Bloomfield Kesinlikle doğru! Bu varyantın daha iyi olmadığını söylememin nedeni bu;)
Klaus-Dieter Warzecha

4

Genellikle Seri'de işleri daha iyi sıraya koymak için Sekmeler kullandım. Benim yaptığım gibi hizaya sokmak, arduino'nun değişkenlerdeki belirli değişiklikleri farkedebildiği gibi olabildiğince hızlı ateşlenmesine izin veriyor.

Böyle bir şey dene:

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

Veya böyle bir şey:

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

Dürüst olmak gerekirse, aynı şeyi ("\ t" ve "\ n") yapıyorum ve normalde kod şişiren String nesnesinin çan ve ıslıklarını kullanmaktan kaçınıyorum.
Klaus-Dieter Warzecha

1
@KlausWarzecha, değişken isimlerini nadiren güzel sütunlarda olduğu gibi veririm. Ayrıca bu sözdizimiyle eşleşmeyen rastgele çıktıları görmeyi kolaylaştırın
Steven10172

4

Bunu sadece hata ayıklamak için kullanıyorum ama:

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

String $ nedir?
Juraj

LMFTFM (Bunu benim için düzelteyim).
linhartr22

2

Arduino dünyasında acemiyim, ancak son zamanlarda bunun sadece normal bir C ++ olduğunu (istisnalar ve muhtemelen polimorfizm olmadan) buldum. Ancak yine de şablonların keyfini çıkarabilirsiniz. Bu yüzden benim çözümüm aşağıdaki şablonları kullanmaktır:

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

Buradaki güzel şey, burada fazladan hafıza ve fazladan işlem yapmamasıdır.


1

Genelde (acı bir şekilde) çoklu çizgilerden Serial.printyapıyorum ama kıvrılınca geri dönüyorum sprintf. Bunun için uygun bir tamponun olması gerekmesi sinir bozucu.

Kullanımı kadar basittir (??):

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

Yine de, bir uyarı kelimesi, (varsayılan olarak) değişken tipleri desteklemiyor.


1
sprintf korkunç bir berbattır. Güvenli yazmayın, tamponlarınızı vb. Aşmanız kolay, vb. 1960'lardan kalma bir araçtır. Ben de onu kullandım, ama kalbimin solukluğu için değil ...
Duncan C

Aşınmayı önlemek için snprintf ... BTW moder IDE'lerin çoğu (Arduino IDE DEĞİLDİR) dizilim biçimini sağlanan değişken tiplerine göre kontrol edecek ve bir uyarı verecektir.
sonraki

1

Kullanma Streaming.hyerine,

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

biri yazabilir

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

Tanımı <<içinde Streaming.hyürürlükte sıradan bir dizi içine çevirir Serial.print()aramalar. Yani <<kod boyutunu artırmadan uygulanan sözdizimsel bir şekerdir.

Eğer var yoksa Streaming.hyüklü olsun Streaming5.zipgelen arduiniana.org . Örneğin, kütüphaneler dizininizden çıkarın ~/sketchbook/libraries. Akışı işleç olarak #include <Streaming.h>kullandığınız eskizlerin içine çizgi ekleyin <<.

Temel dönüşüm özellikleri _HEX, _DEC, _OCT ve _BIN, ayrıca bir _FLOAT işlevi (ondalık basamak sayısı ile) ve endl. Örneğin, enlem ve boylam değerlerini "Koordinatlarınız -23.123, 135.4567" şeklinde olabilir.

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

Bu ayrıca olarak da yazılabilir.

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

bu da uzun dizgiyi PROGMEM'de RAM'e getirmek yerine tutacaktır.

Not, Streaming.h herhangi bir dize oluşturmaz; sadece <<belgelerinin metnini bir akıma aktarır. Arduiniana'da bir PString sınıfı, akışlı çıkış yerine dizeler isteniyorsa veya gerekliyse, akış girdilerinden dizeler oluşturabilir.


1

Kullanım, değişkenlerinizin veri türüne bağlı olacaktır.

Onlar ise int, olurdu %dya %i da varsa string, olurdu%s

Printf için sarıcı

Gereksinimlerinize göre limiti değiştirebilirsiniz

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

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

Kullanım örnekleri:

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

SerialÇerçeve sınıfında yerleşik . Ek kütüphane veya fonksiyona gerek yok.

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

Printf formatı referans sayfasındaki biçimlendirme ipuçları hakkında daha fazla bilgi: http://www.cplusplus.com/reference/cstdio/printf/

\n Satır besleme için kaçış dizisidir.

Escape dizileri string değişmezleri ve karakter değişmezleri içindeki belirli özel karakterleri temsil etmek için kullanılır.

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

[EDIT] - @Juraj'ın belirttiği gibi, AVR modüllerinin çoğunda mevcut değildir. Bu yüzden ESP8266 bahsini ve genel AVR modülleri için bir printf sargısını ekledim


Bu doğru değil. Seri sınıf yok. printf Print sınıfında olurdu, ancak en çok kullanılan AVR paketinde yok
Juraj

@Juraj haklısın, ben sadece ( link ) sahip ESP8266 üzerinde test ettim ve arduino çekirdeği olduğunu düşündüm. Cevabımı buna göre güncelleyeceğim
Remi

p işlevi için eğer mümkün olsaydı bir daha oy verdim.
Juraj

bu eski bir sorudur ve eski cevapları yargılayamam çünkü 2014'te neyin mevcut olduğunu bilmiyorum. Ama şimdi bir Print akışını printf uygulaması ile bir Print akışına sarmak için kütüphaneler var.
Juraj

0

Olası bir çözüm:

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


-1

Gönderen http://playground.arduino.cc/Main/Printf benim mega2560 bu çalışıyor cezası gözlenen

Hepsi işe yaramadı, vsnprintf_P ya da PROGMEM'e gerek yok ...

#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
Neden biri sadece kendini kullanmakprintf() yerine bunu yapmak istesin ki ?
Edgar Bonet,

-3
int Money_amount = 55;

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

Terminalde göreceksiniz:

New amount: $55

1
Bir int'yi bir c-string ile işleçle birleştiremezsiniz +.
gre_gor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.