Bagaimana cara mencetak beberapa variabel dalam sebuah string?

46

Katakanlah saya memiliki beberapa variabel yang ingin saya cetak ke terminal, apa cara termudah untuk mencetaknya dalam sebuah string?

Saat ini saya melakukan sesuatu seperti ini:

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

Apakah ada cara yang lebih baik untuk melakukan ini?

sachleen
sumber
Sebuah ide, tetapi saya tidak tahu apakah itu akan berhasil, adalah beberapa modifikasi dari ini ... Sekali lagi, saya tidak tahu apakah ini didukung di Arduino: stackoverflow.com/questions/804288/…
apnorton

Jawaban:

37

ardprintfadalah fungsi yang saya retas bersama yang disimulasikan printfmelalui koneksi serial. Fungsi ini (diberikan di bagian bawah) dapat disisipkan di awal file di mana fungsi tersebut diperlukan. Seharusnya tidak membuat konflik.

Ini bisa disebut mirip dengan printf. Lihat dalam aksi dalam contoh ini:

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

}

Output seperti yang diharapkan adalah:

test 2 123456789 g test 2.30

Prototipe fungsi adalah:

int ardprintf(char *, ...);

Ini mengembalikan jumlah argumen yang terdeteksi dalam panggilan fungsi.

Ini adalah definisi fungsi:

#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

** Untuk mencetak %karakter, gunakan %%. *


Sekarang, tersedia di inti Github .

asheeshr
sumber
3
Ide bagus, walaupun saya merasa ini bisa lebih minimalis, jadi saya menulis ulang versi ini menjadi versi tanpa buffering. Siapa pun yang tertarik dapat melihat intinya: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram
13

Saya biasanya tidak akan memberikan dua jawaban untuk sebuah pertanyaan, tetapi saya baru saja menemukan ini hari ini , di mana Anda dapat menggunakan printf tanpa 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);    
  }
}

Ini masih memiliki batasan floating point.

sunting: Saya pikir saya akan melakukan sedikit pengujian pada ini, dan itu berfungsi dengan baik. Saya menambahkan tes yang lebih baik ke loop dengan output yang diformat.

Madivad
sumber
Astaga, itu keren. printf jauh lebih aman daripada sprintf. Ini memberi Anda format string gratis, yang bagus. Trik keren. Terima kasih. (Terpilih)
Duncan C
Satu pertanyaan: Dalam serial_putcharfungsi Anda , mengapa tidak membuat pernyataan pengembalian return !Serial.write(c);? Bukankah itu lebih bersih daripada operator trinary karena membalikkan rasa nilai pengembalian boolean?
Duncan C
Itu poin yang bagus dan saya menyukainya. Kode itu bukan milik saya dan saya tempel ketika saya menemukannya.
Madivad
Terima kasih untuk serial_putcharfungsinya. Ini bekerja menyenangkan. :-) Bisakah Anda memperbaiki batasan floating point ?
Greenonline
4

Ini mungkin tidak lebih baik, hanya berbeda. Anda dapat menggunakan objek String untuk output. Objek-objek ini memungkinkan penyatuan dan mendukung typecasting otomatis.

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
sumber
4
Jelas penting untuk berhati-hati terhadap batas memori. Banyak penggabungan dan operasi string lainnya di satu tempat dapat menggunakan jumlah ruang yang mengejutkan.
Peter Bloomfield
@ PeterR.Bloomfield Sepenuhnya benar! Itulah alasan mengapa saya menyebutkan bahwa varian ini tidak lebih baik;)
Klaus-Dieter Warzecha
4

Saya biasanya menggunakan Tab untuk membuat hal-hal yang lebih baik dalam Serial. Memiliki hal-hal yang sejajar seperti yang saya lakukan memungkinkan Arduino menyala secepat mungkin sambil dapat melihat perubahan tertentu dalam variabel.

Coba sesuatu seperti ini:

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

Atau sesuatu seperti ini:

Serial.print("Var 1:");Serial.println(var1);
Serial.print("\tVar 2:");Serial.println(var2);
Serial.print("\tVar 3:");Serial.println(var3);
Steven10172
sumber
Jujur, saya melakukan hal yang sama ("\ t" dan "\ n") dan biasanya menghindari lonceng dan peluit objek String yang membengkak.
Klaus-Dieter Warzecha
1
@ KlausWarzecha, saya jarang memberikan nama variabel karena mereka berada di kolom yang bagus. Juga membuatnya lebih mudah untuk melihat cetakan acak yang tidak cocok dengan sintaks ini
Steven10172
4

Saya hanya menggunakan ini untuk debugging tetapi:

int a = 10;
int b = 20;
Serial.println("a = " + String(a) + " and b = " + String(b));
linhartr22
sumber
apa itu String $?
Juraj
LMFTFM (Biarkan saya memperbaikinya untuk saya).
linhartr22
2

Saya pemula di dunia Arduino, tetapi saya baru-baru ini menemukan bahwa ini hanyalah C ++ biasa (tanpa pengecualian dan mungkin polimorfisme). Tapi Anda masih bisa menikmati templat. Jadi solusi saya adalah menggunakan templat berikut:

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

Yang menyenangkan di sini adalah tidak menggunakan memori ekstra dan pemrosesan ekstra di sini.

pengguna270049
sumber
1

Saya biasanya (dengan menyakitkan) tetap dengan beberapa baris Serial.printtetapi ketika menjadi berbelit-belit saya kembali ke sprintf. Ini menjengkelkan karena Anda harus memiliki buffer yang tersedia untuk itu.

Penggunaannya sesederhana (??) seperti:

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

Namun, kata peringatan, itu tidak (secara default) mendukung tipe mengambang.

Madivad
sumber
1
sprintf adalah kekejian yang mengerikan. Bukan tipe safe, mudah diserbu buffer Anda, dll, dll. Ini adalah alat dari tahun 1960-an. Yang mengatakan, saya menggunakannya juga, tetapi ini bukan untuk orang yang lemah hati ....
Duncan C
Untuk menghindari overrun, gunakan snprintf ... BTW sebagian besar IDE moder (BUKAN Arduino IDE) akan memeriksa format string terhadap tipe variabel yang disediakan, dan akan memberikan peringatan.
next-hack
1

Menggunakan Streaming.h, di tempat

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

orang bisa menulis

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

Definisi <<in Streaming.hin effect menerjemahkannya menjadi serangkaian Serial.print()panggilan biasa . Artinya, <<adalah gula sintaksis, diterapkan tanpa meningkatkan ukuran kode.

Jika Anda belum Streaming.hmenginstal, dapatkan Streaming5.zipdari arduiniana.org . Buka zip di direktori perpustakaan Anda, misalnya di ~/sketchbook/libraries. Tambahkan baris #include <Streaming.h>dalam sketsa tempat Anda menggunakan <<sebagai operator streaming.

Penentu konversi basis _HEX, _DEC, _OCT, dan _BIN disediakan, serta fungsi _FLOAT (dengan jumlah tempat desimal) dan endl. Misalnya, untuk mencetak nilai garis lintang dan bujur dalam bentuk seperti "Koordinat Anda -23.123, 135.4567" orang dapat menulis:

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

Ini juga dapat ditulis sebagai

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

yang akan menyimpan string yang lebih panjang di PROGMEM alih-alih membawanya ke RAM.

Catatan, Streaming.h tidak membangun string apa pun seperti itu; itu hanya mengirimkan teks-nya <<ke sebuah stream. Kelas PString di arduiniana dapat membangun string dari input stream, jika string bukan output stream yang diinginkan atau dibutuhkan.

James Waldby - jwpat7
sumber
1

Penggunaan akan tergantung dari tipe data variabel Anda.

Jika intya, itu akan menjadi %datau %i Jika stringya, itu akan menjadi%s

Wrapper untuk printf

Anda dapat mengubah batas berdasarkan kebutuhan Anda

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

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

Contoh penggunaan:

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

Built-in di Serialkelas kerangka kerja. Tidak perlu perpustakaan atau fungsi tambahan.

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

Rincian lebih lanjut tentang memformat tips pada halaman referensi format printf: http://www.cplusplus.com/reference/cstdio/printf/

\n adalah urutan pelarian untuk umpan baris.

Escape sequence digunakan untuk mewakili karakter khusus tertentu dalam string literal dan literal karakter.

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

[EDIT] - Seperti yang disebutkan @Juraj, itu tidak tersedia di sebagian besar modul AVR. Jadi saya menambahkan ESP8266 disebutkan dan pembungkus printf untuk modul AVR umum

Remi
sumber
ini tidak benar. tidak ada kelas Serial. Printf akan berada dalam kelas Print, tetapi itu bukan dalam paket AVR yang paling banyak digunakan
Juraj
@Juraj Anda benar, saya hanya mengujinya pada ESP8266 yang memilikinya ( tautan ) dan mengira itu dari arduino core. Akan memperbarui jawaban saya sesuai
Remi
untuk fungsi p saya akan menambahkan satu lagi downvote jika memungkinkan.
Juraj
ini adalah pertanyaan lama dan saya tidak dapat menilai jawaban lama karena saya tidak tahu apa yang tersedia pada tahun 2014. tetapi sekarang ada perpustakaan untuk membungkus aliran cetak dalam aliran cetak dengan implementasi printf.
Juraj
0

Salah satu solusi yang mungkin adalah:

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

Iain Crawford
sumber
-1

Dari http://playground.arduino.cc/Main/Printf Saya mengamati ini berfungsi dengan baik pada mega2560 saya

Hanya itu yang berhasil, tidak perlu untuk vsnprintf_P atau 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
sumber
1
Mengapa ada orang yang mau melakukan ini daripada hanya menggunakan printf()itu sendiri ?
Edgar Bonet
-3
int Money_amount = 55;

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

Anda akan melihat di terminal:

New amount: $55
Michael Kushner
sumber
1
Anda tidak dapat menggabungkan int ke c-string dengan +operator.
gre_gor