Print Formatted Data through Arduino Serial

Simple printf() function for Arduino Serial Communication

Looi Kian Seong
5 min readAug 3, 2020

Arduino is a development board which contains AVR microcontroller. It is designed to be easy-to-use and helpful for engineers, students or hobbyists in quick idea testing and prototyping. Often during prototyping with Arduino, we need to debug our programs by using serial monitor provided by the open-sourced and free Arduino platform. And do you wonder if the Serial.print function of Arduino can be used to print formatted data just like what we do it with the standard C printf function?

Although the Arduino Print class does not support formatted data printing, we can still achieve it by writing a wrapper function for the Serial.print function.

Variadic Function

To achieve the similar function as printf in Arduino Serial, we need to first understand the concept of variadic function which can be found in my previous post. In C and C++, we can declare function that can accept variable number of arguments and this type of function is known as variadic function. To achieve this, we need to include the standard library <stdarg.h> that provides the routines to create a variadic function.

Wrapper Function of Serial.print for Printing Formatted Data

  • Direct and simple way to print formatted data through Arduino Serial (has limitation of floating point conversion):
#include <stdarg.h>#define SERIAL_PRINTF_MAX_BUFF      256
void serialPrintf(const char *fmt, ...);
void serialPrintf(const char *fmt, ...) {
/* Buffer for storing the formatted data */
char buff[SERIAL_PRINTF_MAX_BUFF];
/* pointer to the variable arguments list */
va_list pargs;
/* Initialise pargs to point to the first optional argument */
va_start(pargs, fmt);
/* create the formatted data and store in buff */
vsnprintf(buff, SERIAL_PRINTF_MAX_BUFF, fmt, pargs);
va_end(pargs);
Serial.print(buff);
}
void setup() {
Serial.begin(9600);
delay(500);
Serial.println("Arduino Serial print formatted data...");
Serial.println();
serialPrintf( "Name: %s\nAge: %d\nHeight: %fm\nWeight: %fkg",
"Luka", 21, 2.01, 104.8);
}
void loop() {

}

Output on Serial Monitor:

Figure 1: Output on serial monitor that print formatted data with integer(%d), string(%s) and floating-point(%f) conversion.

From Figure 1, we can observe that the Name (string) and Age (integer) fields are filled with the expected values. However, the fields that are supposed to be filled with floating point numbers (Height and Weight) are left with the character ?.

According to AVR Libc website, the default printf family functions in avr-gcc do not implement floating point conversion, which is as quoted below.

“ Since the full implementation of all the mentioned features becomes fairly large, three different flavours of vfprintf() can be selected using linker options. The default vfprintf() implements all the mentioned functionality except floating point conversions. A minimized version of vfprintf() is available that only implements the very basic integer and string conversion facilities, but only the # additional option can be specified using conversion flags (these flags are parsed correctly from the format specification, but then simply ignored). ”

So, we need to make modification in the code for it to perform the floating point conversion.

  • Modification for enabling floating point conversion:
#include <stdarg.h>#define SERIAL_PRINTF_MAX_BUFF      256
#define F_PRECISION 6
void serialPrintf(const char *fmt, ...);
/**
* --------------------------------------------------------------
* Perform simple printing of formatted data
* Supported conversion specifiers:
* d, i signed int
* u unsigned int
* ld, li signed long
* lu unsigned long
* f double
* c char
* s string
* % '%'
* Usage: %[conversion specifier]
* Note: This function does not support these format specifiers:
* [flag][min width][precision][length modifier]
* --------------------------------------------------------------
*/
void serialPrintf(const char *fmt, ...) {
/* buffer for storing the formatted data */
char buf[SERIAL_PRINTF_MAX_BUFF];
char *pbuf = buf;
/* pointer to the variable arguments list */
va_list pargs;

/* Initialise pargs to point to the first optional argument */
va_start(pargs, fmt);
/* Iterate through the formatted string to replace all
conversion specifiers with the respective values */
while(*fmt) {
if(*fmt == '%') {
switch(*(++fmt)) {
case 'd':
case 'i':
pbuf += sprintf(pbuf, "%d", va_arg(pargs, int));
break;
case 'u':
pbuf += sprintf(pbuf, "%u", va_arg(pargs, unsigned int));
break;
case 'l':
switch(*(++fmt)) {
case 'd':
case 'i':
pbuf += sprintf(pbuf, "%ld", va_arg(pargs, long));
break;
case 'u':
pbuf += sprintf( pbuf, "%lu",
va_arg(pargs, unsigned long));
break;
}
break;
case 'f':
pbuf += strlen(dtostrf( va_arg(pargs, double),
1, F_PRECISION, pbuf));
break;

case 'c':
*(pbuf++) = (char)va_arg(pargs, int);
break;
case 's':
pbuf += sprintf(pbuf, "%s", va_arg(pargs, char *));
break;
case '%':
*(pbuf++) = '%';
break;
default:
break;
}
} else {
*(pbuf++) = *fmt;
}

fmt++;
}

*pbuf = '\0';

va_end(pargs);
Serial.print(buf);
}
void setup() {
Serial.begin(9600);
delay(500);
Serial.println("Arduino Serial print formatted data...");
Serial.println();
serialPrintf( "Name: %s\nAge: %d\nHeight: %fm\nWeight: %fkg",
"Luka", 21, 2.01, 104.8);
}
void loop() {

}

Output on Serial Monitor:

Figure 2: Output on serial monitor after modification on the code.

From Figure 2, we can observe that the formatted string is printed correctly on serial monitor after the modification on the code.

Note: The code above has only the minimal functionality of printf() as it does not deal with the flag, minimum width, precision and length modifier.

Conclusion

In conclusion, we can perform formatted data printing in Arduino serial communication by writing a wrapper function with the help of variadic function. If you are not going to deal with the printing of floating point numbers, you can just use the first code above which is simple and straightforward. However, if printing of floating point numbers is necessary, you need to perform the conversion specifier checking as shown in the second (modified) code above. To make a full implementation of printf features as in standard C library (which is not included in this post), we need to add another algorithm in the code to parse the format specifiers and perform the formatting accordingly.

Thank you for reading and hope that you learnt how to write a function for printing formatted data through Arduino Serial.

Feel free to comment if you find any part is confusing and I will try my best to help.

--

--