Làm cách nào để in nhiều biến trong một chuỗi?


46

Nói rằng tôi có một số biến mà tôi muốn in ra thiết bị đầu cuối, cách dễ nhất để in chúng trong một chuỗi là gì?

Hiện tại tôi làm một cái gì đó như thế này:

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

Có cách nào tốt hơn để làm điều này?


Một ý tưởng, nhưng tôi không biết liệu nó có hoạt động không, là một số sửa đổi của điều này ... Một lần nữa, tôi không biết liệu điều này có được hỗ trợ trên Arduino hay không: stackoverflow.com/questions/804288/
Lỗi

Câu trả lời:


37

ardprintflà một chức năng mà tôi đã hack cùng nhau, mô phỏng printfqua kết nối nối tiếp. Chức năng này (được đưa ra ở dưới cùng) có thể được dán vào phần đầu của tệp nơi cần chức năng. Nó không nên tạo ra bất kỳ xung đột.

Nó có thể được gọi là tương tự như printf. Xem nó trong hành động trong ví dụ này:

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

}

Đầu ra như mong đợi là:

test 2 123456789 g test 2.30

Nguyên mẫu hàm là:

int ardprintf(char *, ...);

Nó trả về số lượng đối số được phát hiện trong lệnh gọi hàm.

Đây là định nghĩa hàm:

#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

** Để in %ký tự, sử dụng %%. *


Bây giờ, có sẵn trên các ý chính Github .


3
Ý tưởng hay, mặc dù tôi cảm thấy nó có thể tối giản hơn, vì vậy tôi đã viết lại phiên bản này thành một mà không cần đệm. Bất cứ ai quan tâm đều có thể kiểm tra ý chính: gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

Tôi sẽ không bình thường đặt hai câu trả lời cho một câu hỏi, nhưng tôi chỉ mới tìm thấy này ngày hôm nay, nơi bạn có thể sử dụng printf mà không cần bất kỳ bộ đệm.

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

Điều này vẫn có giới hạn điểm nổi.

chỉnh sửa: Tôi nghĩ rằng tôi sẽ làm một thử nghiệm nhỏ về điều này, và nó hoạt động khá tốt. Tôi đã thêm một bài kiểm tra tốt hơn vào vòng lặp với đầu ra được định dạng.


Ôi trời, thật tuyệt. printf an toàn hơn nhiều so với sprintf. Nó cung cấp cho bạn định dạng chuỗi miễn phí, đó là tuyệt vời. Thủ thuật hay. Cảm ơn. (Bình chọn)
Duncan C

Một câu hỏi: Trong serial_putcharchức năng của bạn , tại sao không thực hiện câu lệnh return return !Serial.write(c);? Không phải là sạch hơn một toán tử ba để đảo ngược ý nghĩa của giá trị trả về boolean sao?
Duncan C

Đó là một điểm tốt và tôi thích nó. Mã không phải là của tôi và tôi đã dán nó khi tôi tìm thấy nó.
Madivad

Cảm ơn các serial_putcharchức năng. Nó hoạt động một điều trị. :-) Bạn có thể sửa giới hạn dấu phẩy động không?
Greenonline

4

Điều này có lẽ không tốt hơn, chỉ khác nhau. Bạn có thể sử dụng đối tượng String cho đầu ra. Những đối tượng này cho phép ghép nối và hỗ trợ tự động gõ.

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
Rõ ràng điều quan trọng là phải cẩn thận về giới hạn bộ nhớ. Rất nhiều kết nối và các hoạt động chuỗi khác ở một nơi có thể sử dụng một lượng không gian đáng ngạc nhiên.
Peter Bloomfield

@ PeterR.Bloomfield Hoàn toàn đúng! Đó là lý do tại sao tôi đã đề cập rằng biến thể này không tốt hơn;)
Klaus-Dieter War817a

4

Tôi thường sử dụng Tab để làm cho mọi thứ xếp hàng tốt hơn trong Nối tiếp. Có những thứ xếp hàng như tôi cho phép arduino bắn càng nhanh càng tốt trong khi có thể nhận thấy những thay đổi nhất định trong các biến.

Hãy thử một cái gì đó như thế này:

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

Hoặc thứ gì đó giống thế này:

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

Thành thật mà nói, tôi cũng làm như vậy ("\ t" và "\ n") và thường tránh các chuông và còi của đối tượng String đầy mã.
Klaus-Dieter War Dixa

1
@KlausWar817a, tôi hiếm khi đặt tên biến vì chúng nằm trong các cột đẹp. Đồng thời giúp dễ dàng xem các bản in ngẫu nhiên không khớp với cú pháp này
Steven10172

4

Tôi chỉ sử dụng điều này để gỡ lỗi nhưng:

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

Chuỗi $ là gì?
Juraj

LMFTFM (Hãy để tôi sửa nó cho tôi).
linhartr22

2

Tôi là người mới trong thế giới Arduino, nhưng gần đây tôi thấy rằng đây chỉ là một C ++ thông thường (không có ngoại lệ và có lẽ là đa hình). Nhưng bạn vẫn có thể thưởng thức các mẫu. Vì vậy, giải pháp của tôi là sử dụng các mẫu sau:

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

Điều tuyệt vời ở đây là nó không sử dụng bất kỳ bộ nhớ bổ sung và xử lý thêm ở đây.


1

Tôi thường (đau đớn) gắn bó với nhiều dòng Serial.printnhưng khi nó trở nên hỗn độn, tôi quay trở lại sprintf. Thật khó chịu khi bạn phải có một bộ đệm có sẵn cho nó.

Cách sử dụng đơn giản (??) như:

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

Mặc dù vậy, một lời cảnh báo, nó không (theo mặc định) hỗ trợ các kiểu nổi.


1
sprintf là một sự ghê tởm khủng khiếp. Không phải loại an toàn, dễ dàng vượt qua bộ đệm của bạn, v.v. Đó là một công cụ từ những năm 1960. Điều đó nói rằng, tôi cũng sử dụng nó, nhưng nó không dành cho người yếu tim ....
Duncan C

Để tránh tràn ngập, hãy sử dụng snprintf ... BTW hầu hết các IDE kiểm duyệt (KHÔNG phải IDE IDE) sẽ kiểm tra định dạng chuỗi đối với các loại biến được cung cấp và sẽ đưa ra cảnh báo.
hack tiếp theo

1

Sử dụng Streaming.h, thay cho

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

người ta có thể viết

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

Định nghĩa về <<Streaming.hcó hiệu lực dịch đó vào một loạt các thông thường Serial.print()các cuộc gọi. Đó là, <<là cú pháp đường, được thực hiện mà không tăng kích thước mã.

Nếu bạn chưa Streaming.hcài đặt, hãy lấy Streaming5.ziptừ arduiniana.org . Giải nén nó trong thư mục thư viện của bạn, ví dụ như trong ~/sketchbook/libraries. Thêm dòng #include <Streaming.h>trong bản phác thảo mà bạn sử dụng <<làm toán tử luồng.

Các chỉ định chuyển đổi cơ sở _HEX, _DEC, _OCT và _BIN được cung cấp, cũng như hàm _FLOAT (với số vị trí thập phân) và endl. Ví dụ: để in các giá trị vĩ độ và kinh độ dưới dạng như "Tọa độ của bạn là -23.123, 135,4567, người ta có thể viết:

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

Điều này cũng có thể được viết là

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

sẽ giữ chuỗi dài hơn trong PROGMEM thay vì đưa nó vào RAM.

Lưu ý, Streaming.h không xây dựng bất kỳ chuỗi nào như vậy; nó chỉ truyền tải văn bản của nó <<vào một luồng. Một lớp PString tại arduiniana có thể xây dựng các chuỗi từ các đầu vào luồng, nếu các chuỗi thay vì đầu ra truyền phát được mong muốn hoặc cần thiết.


1

Việc sử dụng sẽ phụ thuộc vào kiểu dữ liệu của các biến của bạn.

Nếu họ là int, nó sẽ %dhoặc %i Nếu họ string, nó sẽ là%s

Wrapper cho printf

Bạn có thể thay đổi giới hạn dựa trên yêu cầu của bạn

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

Nguồn: https://playground.arduino.cc/Main/Printf

Ví dụ sử dụng:

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

Nó được xây dựng trong Seriallớp của khung. Không cần thêm thư viện hoặc chức năng.

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

Thêm chi tiết về các mẹo định dạng trên trang tham chiếu định dạng printf: http://www.cplusplus.com/reference/cstdio/printf/

\n là chuỗi thoát cho nguồn cấp dữ liệu.

Trình tự thoát được sử dụng để thể hiện một số ký tự đặc biệt trong chuỗi ký tự và ký tự chuỗi ký tự.

Nguồn: http://en.cppreference.com/w/cpp/lingu/escape

[EDIT] - Như @Juraj đã đề cập, nó không có sẵn trên hầu hết các mô-đun AVR. Vì vậy, tôi đã thêm đề cập đến ESP8266 và trình bao bọc printf cho các mô-đun AVR phổ biến


Đây không phải là sự thật. không có lớp nối tiếp. printf sẽ ở trong lớp Print, nhưng nó không nằm trong gói AVR được sử dụng nhiều nhất
Juraj

@Juraj bạn nói đúng, tôi chỉ thử nó trên ESP8266 có nó ( liên kết ) và nghĩ rằng nó là từ lõi arduino. Sẽ cập nhật câu trả lời của tôi cho phù hợp
Remi

đối với hàm p tôi sẽ thêm một downvote nếu có thể.
Juraj

đây là một câu hỏi cũ và tôi không thể đánh giá các câu trả lời cũ vì tôi không biết những gì đã có trong năm 2014. nhưng hiện tại có các thư viện để gói một luồng In trong luồng In với triển khai printf.
Juraj

0

Một giải pháp có thể là:

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


-1

Từ http://playground.arduino.cc/Main/Printf tôi đã quan sát thấy điều này đang hoạt động tốt trên mega2560 của tôi

Đó là tất cả những gì nó hoạt động, không cần vsnprintf_P hoặc 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
Tại sao bất cứ ai cũng muốn làm điều này thay vì chỉ sử dụng printf()chính nó ?
Edgar Bonet

-3
int Money_amount = 55;

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

Bạn sẽ thấy trên thiết bị đầu cuối:

New amount: $55

1
Bạn không thể nối một int thành một chuỗi c với một +toán tử.
gre_gor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.