아두이노에서는 기본적으로 printf()함수를 지원하지 않는데 그 기능이 아쉬울 때가 있다. 그런데 다음과 같이 vsprintf() 와 vsnprintf() 함수를 이용하면 쉽게 printf() 기능을 구현할 수 있다. 아두이노 setup()함수 위에 다음과 같이 _printf()함수를 정의한다.


#include <stdio.h>

#include <stdarg.h>


void _printf(const char *s, ...){

   va_list args;

   va_start(args, s);

   int n = vsnprintf(NULL, 0, s, args);//문자열 길이를 먼저 구함

   char *str = new char[n+1]; //메모리 할당

   vsprintf(str, s, args);//실제 내용을 복사

   va_end(args);

   Serial.print(str);

   delete [] str; //메모리 해제

}


그런데 이렇게만 하면 _printf() 함수 내에서 실수를 표시하는 서식문자 %f, %g, %e를  사용할 수 없다는 문제가 있다. 이 문제를 해결하려면 다음 파일을 열어서 수정해야 한다.


{아두이노 설치 폴더}\hardware\arduino\avr\platform.txt

윈도우의 경우 C:\Program Files\Arduino\hardware\arduino\avr\platform.txt


이 파일에서 다음 줄을 찾아서


compiler.c.elf.extra_flags=


다음과 같이 끝에 추가한다.


compiler.c.elf.extra_flags=-Wl,-u,vfprintf -lprintf_flt -lm


이제 아두이노 IDE를 다시 실행하면 된다. 전체 프로그램은 다음과 같다.


#include <stdio.h>

#include <stdarg.h>


void _printf(const char *s,...){

   va_list args;

   va_start(args, s);

     int n = vsnprintf(NULL, 0, s, args);

     char *str = new char[n+1];

     vsprintf(str, s, args);

   va_end(args);

   Serial.print(str);

   delete [] str;

}


void setup() {

 Serial.begin(9600);

 _printf("%d+%d=%d\n",1,2,3);

 _printf("float var %.4f\n",1.234);

 _printf("hello %s\n", "world");

}


void loop() {

}


시리얼 모니터를 통해서 볼 수 있는 실행 결과는 다음과 같다.


1+2=3

float var 1.2340

hello world


 한 가지 더 지적할 것은 아두이노에서는 문자열 상수를 전역변수로 저장하기 때문에 RAM을 차지한다. 그러니까 위의 프로그램에서 그냥 큰따옴표로 묶인 문자열들은 전부 램에 저장된다. 램용량이 많지 않은 아두이노에서는 문자열 상수는 보통 F() 매크로로 플래시메모리 영역에 저장하여 RAM을 아낄 수 있다. 위의 _printf() 함수의 첫 번째 인자를 F()매크로로 저장한 문자열로 받을 수 있도록 수정하려면 내부에서 vsnprintf()함수 대신 vsnprintf_P()을, 그리고 vsprintf()함수 대신 vsprintf_P()함수를 사용해야 하는데 다음과 같다.


#include <stdio.h>

#include <stdarg.h>


void _printf(const __FlashStringHelper *s, ...){

   va_list args;

   va_start(args, s);

     int n = vsnprintf_P(NULL, 0, (const char*)s, args);

     char *str = new char[n];

     vsprintf_P(str, (const char*)s, args);

   va_end(args);

   Serial.print(str);

   delete [] str;

}


void setup() {

 Serial.begin(9600);

 

 _printf(F("%d+%d=%d\n"),1,2,3);

 _printf(F("float var %.4f\n"),1.234);

 _printf(F("hello %S\n"), F("world"));

}


void loop() {

}


서식문자열 내부에서 플래시 문자열을 표시하려면 대문자 S를 사용한 ‘%S’ 서식문자를 사용해야 한다는 것에 유의하자. 실행 결과는 앞의 경우와 동일하다.



Posted by 살레시오
,