This function emulates the stdio.h printf() functionality in C and will send the output to the Serial connection using Serial.print(). The resulting string sent over serial is limited to 128 chars. See the sprintf man page or the examples herein for details on how to build format strings.
#include <stdarg.h>
void p(char *fmt, ... ){
char tmp[128]; // resulting string limited to 128 chars
va_list args;
va_start (args, fmt );
vsnprintf(tmp, 128, fmt, args);
va_end (args);
Serial.print(tmp);
}
Examples:
p("%s", "Hello world");
p("%s\n", "Hello world"); // with line break
unsigned long a=0xFFFFFFFF;
p("Decimal a: %l\nDecimal unsigned a: %lu\n", a, a);
p("Hex a: %x\n", a);
If anyone has a better version please feel free to remove or edit this version.
You can also avoid using Serial.print() altogether and redirect STDIN and STDOUT to UART.
The avr-gcc C library, avr-libc, provides the printf() family of functions. You can use them in Arduino sketches after some preparation. General details are in the avr-libc documentation for file stdio.h. These notes are specific to the Arduino development environment.
You must:
- create a FILE structure instance,
- create a character output function,
- write the function address into the FILE structure,
- assign the structure address to that of 'stdout'.
printf() makes your executable object ~1000 bytes larger, so you may not want to use it if size is a problem.
Example
// we need fundamental FILE definitions and printf declarations
#include <stdio.h>
// create a FILE structure to reference our UART output function
static FILE uartout = {0} ;
// create a output function
// This works because Serial.write, although of
// type virtual, already exists.
static int uart_putchar (char c, FILE *stream)
{
Serial.write(c) ;
return 0 ;
}
void setup(void)
{
// Start the UART
Serial.begin(9600) ;
// fill in the UART file descriptor with pointer to writer.
fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
// The uart is the standard output device STDOUT.
stdout = &uartout ;
}
void loop(void)
{
float seconds ;
// wait 1000 milliseconds
delay(1000) ;
// calculate seconds as a floating point value
seconds = (float) millis() /1000.0 ;
// report seconds since starting
printf("Alive %.3f sec", seconds ) ;
/*
// without printf(), you would do this:
Serial.print("Alive ") ;
Serial.print(seconds,3) ;
Serial.print("sec") ;
*/
#if 0
// you can explicitly use a FILE structure like this:
fprintf( &uartout, "Alive %.3f sec", seconds ) ;
#endif
}
Extensions
If you can define a character-based output function and reference it in a FILE structure, you can use fprintf() as well. An example for an LCD shield follows below.
Example
#include <LiquidCrystal.h>
// register the LiquidCrystal control and data pins
// for the dfrobots LCD shield.
LiquidCrystal LCD(8,9,4,5,6,7) ;
static FILE lcdout = {0} ; // LCD FILE structure
// LCD character writer
static int lcd_putchar(char ch, FILE* stream)
{
LCD.write(ch) ;
return (0) ;
}
void setup(void)
{
// LCD shield is 16 columns by 2 rows
LCD.begin(16,2) ;
// fill in the LCD FILE structure
fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE);
}
void loop(void)
{
float seconds ;
// calculate seconds as a floating point value
seconds = (float) millis() /1000.0 ;
// set the LCD cursor position to home
LCD.setCursor(0,0) ;
// report seconds since starting
// in the X.xxx floating point format.
fprintf(&lcdout, "Alive %.3f sec", seconds ) ;
// wait 1000 milliseconds
delay(1000) ;
}
Using Flash Memory for string storage
Version 1.0 of the Arduino IDE introduced the F() syntax for storing strings in flash memory rather than RAM. e.g.
Serial.println(F("This string will be stored in flash memory"));
The same result can be achieved with printf by using the _P versions of printf, fprintf, etc and wrapping your string in the PSTR() macro. You'll need to #include <avr/pgmspace.h>, e.g.
#include <avr/pgmspace.h>
// do all the stdout setup as above here
printf_P(PSTR("This string will be stored in flash memory"));
Using flash memory this way only works for the format string. If you include a string (%s) in your format you'll need to pass a regular string, e.g.
// this works
printf_P(PSTR("string %s"), "embedded string");
// this fails
printf_P(PSTR("string %s"), PSTR("embedded string"));
Tweaked version of the above example using printf_P:
// we need fundamental FILE definitions and printf declarations
#include <stdio.h>
#include <avr/pgmspace.h>
// create a FILE structure to reference our UART output function
static FILE uartout = {0} ;
// create a output function
// This works because Serial.write, although of
// type virtual, already exists.
static int uart_putchar (char c, FILE *stream)
{
Serial.write(c) ;
return 0 ;
}
void setup(void)
{
// Start the UART
Serial.begin(9600) ;
// fill in the UART file descriptor with pointer to writer.
fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
// The uart is the standard output device STDOUT.
stdout = &uartout ;
}
void loop(void)
{
int seconds ;
// wait 1000 milliseconds
delay(1000) ;
seconds = millis() / 1000 ;
// use flash storage for the format string
printf_P(PSTR("Alive %d sec\n"), seconds);
/*
// without printf_P(), you would do this:
Serial.print(F("Alive ")) ;
Serial.print(seconds) ;
Serial.println(F(" sec")) ;
*/
#if 0
// you can explicitly use a FILE structure like this:
fprintf_P( &uartout, PSTR("Alive %d sec\n"), seconds ) ;
#endif
}
I've tweaked the previous example to use regular integers rather than floating point values. My environment (Ubuntu Linux 10.04, Arduino IDE 1.0) doesn't include support for floating points (%f) by default when compiling printf support. According to notes in stdio.h it's possible to change this behaviour by passing gcc extra parameters. I'm not sure if this limitation applies to Windows and OSX environments.