문자열에 여러 변수를 어떻게 인쇄합니까?


46

터미널에 인쇄하려는 변수가 있는데 문자열로 인쇄하는 가장 쉬운 방법은 무엇입니까?

현재 나는 다음과 같은 것을한다 :

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

더 좋은 방법이 있습니까?


아이디어, 그러나 그것이 작동하는지 모르겠지만, 이것의 일부 수정입니다 ... 다시, 나는 이것이 Arduino에서 지원되는지 모르겠습니다 : stackoverflow.com/questions/804288/…
apnorton

답변:


37

ardprintfprintf직렬 연결을 통해 시뮬레이션하는 해킹 된 기능입니다 . 이 기능 (맨 아래에 제공)은 기능이 필요한 파일의 시작 부분에 붙여 넣을 수 있습니다. 충돌을 일으키지 않아야합니다.

와 비슷하게 호출 할 수 있습니다 printf. 이 예제에서 실제로 작동하는 것을 참조하십시오.

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

}

예상대로 출력은 다음과 같습니다.

test 2 123456789 g test 2.30

함수 프로토 타입은 다음과 같습니다.

int ardprintf(char *, ...);

함수 호출에서 감지 된 인수 수를 리턴합니다.

이것은 함수 정의입니다.

#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

** %문자 를 인쇄하려면 %%. *를 사용하십시오 .


이제 Github gists 에서 사용할 수 있습니다 .


3
좋은 생각이지만 더 미니멀 할 수 있다고 생각했지만 버퍼링하지 않고이 버전을 다시 작성했습니다. 관심있는 사람은 gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

나는 일반적으로 질문에 두 가지 대답을하지 않을 것이지만, 나는 오늘 이것을 단지 발견 했다 . 여기서 버퍼없이 printf를 사용할 수있다.

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

여기에는 여전히 부동 소수점 제한이 있습니다.

편집 : 나는 이것에 대해 약간의 테스트를 할 것이라고 생각했으며 꽤 잘 작동합니다. 포맷 된 출력으로 루프에 더 나은 테스트를 추가했습니다.


오, 멋지다. printf는 sprintf보다 훨씬 안전합니다. 문자열을 무료로 형식화 할 수 있습니다. 멋진 트릭. 감사. (투표)
Duncan C

한 가지 질문 : serial_putchar함수에서 return 문을 작성하지 않으 return !Serial.write(c);시겠습니까? 부울 리턴 값의 의미를 뒤집는 삼항 연산자보다 깨끗하지 않습니까?
Duncan C

좋은 지적이었고 좋아합니다. 코드는 내 것이 아니며 내가 찾은대로 붙여 넣었습니다.
Madivad

serial_putchar기능 주셔서 감사 합니다. 대접을합니다. :-) 부동 소수점 제한 을 고칠 수 있습니까 ?
Greenonline

4

이것은 아마도 더 좋지 않을 것입니다. 출력을 위해 String 객체를 사용할 수 있습니다 . 이러한 객체는 연결을 허용하고 자동 유형 변환을 지원합니다.

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
메모리 제한에주의하는 것이 중요합니다. 한곳에서 많은 연결과 다른 문자열 연산은 놀라운 공간을 사용할 수 있습니다.
피터 블룸필드

@ PeterR.Bloomfield 절대적으로 사실입니다! 그것이 내가이 변형이 더 좋지 않다고 언급 한 이유입니다.)
Klaus-Dieter Warzecha

4

나는 일반적으로 탭을 사용하여 일련 번호가 더 잘 표시되도록했습니다. 내가 일하는 것처럼 정렬하면 arduino가 변수에 특정 변화가 있음을 알면서 가능한 한 빨리 발사 할 수 있습니다.

다음과 같이 해보십시오 :

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

또는 이와 같은 것 :

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

솔직히, 나는 똑같이 ( "\ t"와 "\ n") 코드를 사용하는 String 객체 벨과 휘파람을 피합니다.
Klaus-Dieter Warzecha

1
@ KlausWarzecha, 변수 열이 멋진 열에 있으므로 변수 이름을 거의 지정하지 않습니다. 또한이 구문과 일치하지 않는 임의의 인쇄물을보다 쉽게 ​​볼 수 있습니다.
Steven10172

4

나는 이것을 디버깅에만 사용하지만 :

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

String $는 무엇입니까?
Juraj

LMFTFM (나를 위해 그것을 고치자).
linhartr22

2

나는 Arduino 세계의 초보자이지만 최근에는 이것이 예외적 인 C ++ (예외 및 다형성 제외)임을 알았습니다. 그러나 여전히 템플릿을 즐길 수 있습니다. 그래서 내 솔루션은 다음 템플릿을 사용하는 것입니다.

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

여기서 좋은 점은 여기에 여분의 메모리와 추가 처리를 사용하지 않는다는 것입니다.


1

나는 보통 (통증 적으로) 여러 줄을 고수 Serial.print하지만 그것이 복잡해지면 다시 돌아갑니다 sprintf. 사용 가능한 버퍼가 있어야한다는 점에서 짜증납니다.

사용법은 다음과 같이 간단합니다 (??).

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

경고의 말이지 만, 기본적으로 부동 유형을 지원하지 않습니다.


1
sprintf는 끔찍한 혐오입니다. 타입 안전하지 않고 버퍼 오버런 등을 쉽게 할 수 있습니다. 1960 년대의 도구입니다. 그것은, 나도 그것을 사용하지만, 그것은 희미한 마음을위한 것이 아닙니다 ....
Duncan C

오버런을 피하려면 snprintf ...를 사용하십시오. BTW 대부분의 모더 IDE (Arduino IDE 아님)는 제공된 변수 유형과 비교하여 문자열 형식을 확인하고 경고를 발생시킵니다.
next-hack

1

Streaming.h대신 에을 사용하여

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

하나는 쓸 수있다

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

사실상 의 정의는 <<Streaming.h를 일련의 일반 Serial.print()호출 로 변환 합니다. 즉, <<코드 크기를 늘리지 않고 구현되는 구문 설탕입니다.

당신이하지 않은 경우 Streaming.h설치 얻을 Streaming5.zip에서 arduiniana.org . 라이브러리 디렉토리에서 압축을 풉니 다 (예 :) ~/sketchbook/libraries. 스트림 연산자로 #include <Streaming.h>사용하는 스케치 내에 선을 추가하십시오 <<.

기본 변환 지정자 _HEX, _DEC, _OCT 및 _BIN과 _FLOAT 함수 (소수 자릿수 포함) 및가 제공됩니다 endl. 예를 들어 위도 및 경도 값을 "좌표는 -23.123, 135.4567"과 같은 형식으로 인쇄하려면 다음과 같이 쓸 수 있습니다.

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

이것은 또한 다음과 같이 쓸 수 있습니다

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

RAM으로 가져 오는 대신 PROGMEM에서 더 긴 문자열을 유지합니다.

참고로 Streaming.h 문자열을 작성하지는 않습니다. 인수의 텍스트를 <<스트림으로 전달합니다 . 스트림 출력 대신 문자열이 필요하거나 필요한 경우 arduinianaPString 클래스는 스트림 입력에서 문자열을 작성할 수 있습니다.


1

사용법은 변수의 데이터 유형에 따라 다릅니다.

그들이 있다면 int, 그렇 %d거나 %i 한다면 string,%s

printf 용 래퍼

요구 사항에 따라 제한을 변경할 수 있습니다

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

출처 : https://playground.arduino.cc/Main/Printf

사용 예 :

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프레임 워크 클래스에 내장되어 있습니다. 추가 라이브러리 또는 기능이 필요하지 않습니다.

// 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 형식 참조 페이지의 형식 지정 팁에 대한 자세한 내용 : http://www.cplusplus.com/reference/cstdio/printf/

\n 줄 바꿈의 이스케이프 시퀀스입니다.

이스케이프 시퀀스는 문자열 리터럴 및 문자 리터럴 내의 특정 특수 문자를 나타내는 데 사용됩니다.

출처 : http://en.cppreference.com/w/cpp/language/escape

[편집] -@Juraj가 언급했듯이 대부분의 AVR 모듈에서는 사용할 수 없습니다. 그래서 일반적인 AVR 모듈에 대한 ESP8266 언급과 printf 래퍼를 추가했습니다.


이것은 사실이 아닙니다. 시리얼 클래스는 없습니다. printf는 Print 클래스에 있지만 가장 많이 사용되는 AVR 패키지에는 없습니다
Juraj

@ Juraj는 맞습니다 .ESP8266에서만 테스트했으며 ( 링크 ) arduino 코어에서 나온 것으로 생각했습니다. 이에 따라 내 대답을 업데이트하겠습니다
Remi

p 함수의 경우 가능한 경우 downvote를 하나 더 추가합니다.
Juraj

이것은 오래된 질문이며 2014 년에 무엇을 사용할 수 있었는지 알 수 없기 때문에 이전 답변을 판단 할 수 없습니다. 그러나 printf 구현으로 Print 스트림을 Print 스트림에 래핑하는 라이브러리가 있습니다.
Juraj

0

가능한 해결책은 다음과 같습니다.

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


-1

에서 http://playground.arduino.cc/Main/Printf 내 mega2560에 노력하고 벌금을 관찰

그것이 방금 작동 한 전부이며 vsnprintf_P 또는 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
왜 누군가가 스스로를 사용하는printf() 대신 이것을하고 싶 습니까?
Edgar Bonet 17

-3
int Money_amount = 55;

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

터미널에서 볼 수 있습니다 :

New amount: $55

1
연산자를 사용하여 int를 c- 문자열에 연결할 수 없습니다 +.
gre_gor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.