Add Serial.Printf using va_args and vprinf

my title says it all, why not add a simple wrapper for vprintf? Nothing will get broken.

Mistakes with printf are very difficult for beginners to debug. There was a post just today asking for help with sprintf. Are you willing to assist those who run into trouble?

I like the idea of this and was just about to write something the other day because I really hate having 10 Serial.prints in a row just to format a debug print.

As for beginners getting into trouble, true enough.


Rob

@Rob
There allready exists this lib - Streaming | Arduiniana - for easier output C++ style

From this site:

lcd.print("GPS #");
lcd.print(gpsno);
lcd.print(" date: ");
lcd.print(day);
lcd.print("-");
lcd.print(month);
lcd.print("-");
lcd.println(year); // ugh!!

becomes

lcd << "GPS #" << gpsno << " date: " <<  day << "-" << month << "-" << year << endl;

With the new library you can also use formatting manipulators like this:

Serial << "Byte value: " << _HEX(b) << endl;
lcd << "The key pressed was " << _BYTE(c) << endl;

This syntax is familiar to many, is easy to read and learn, and, importantly, consumes no resources. (Because the operator functions are essentially just inline aliases for their print() counterparts, no sketch gets larger or consumes more RAM as a result of their inclusion.)

Thanks, I'll check that out.


Rob

I found this works fine:

http://www.arduino.cc/playground/Main/Printf

As an old school C/C++ programmer this put me back in my comfort zone.

sixeyes:
I found this works fine:

Arduino Playground - Printf

As an old school C/C++ programmer this put me back in my comfort zone.

Yes, that's exactly what I'm taking about, except I would have liked to see it as a part of the Serial class.

Also the << stream operator should be more well known, or made official, it seems like a good solution. It doesn't look like it works with progmem strings with PSTR yet though, does it?

I to probably feel more at home with a printf version, but I thought I'd try Streaming.h first.

I LOVE it, no more crappy Serial.print()s.

Thanks a million for the link Rob.


Rob

I like the idea of streaming, especially as I don't need to pull in printf.

However I've no idea how you format a number as you can with printf (e.g. leading zeroes, width etc)

I'm going to be Contrarian here, but...

Variable lists of parameters is one of the most ill-conceived and poorly executed notions in c.
Overloaded operators is one of the most ill-conceived and poorly executed notions in c++.

They both undermind the notion of a strongly-typed language and make for code that is hard to understand and hard to maintain. There is a reason that subsequent languages have abandonned them.

There's nothing unreasonable about a) using functions to perform the job of functions, rather than operators and format strings; and b) calling a function once for every time you need to use it. Saving keystrokes costs you time in the long run.

Yeah, well when C++ can create formatted output the simple way C can,
I'll concede but until then xxxprintf() functions are the way to really do formatting.
C++ in all its years of trying to stream i/o has yet to create decent formatting capablities
that are as easy to use as xxxprintf() functions.
(try to output a 4 digit numeric field that is zero filled, as an example)
AND.... todays gcc compilers have knowledge of printf and its formatting strings and check
the types of all the arguments passed to the functions against the expected types
based on the formatting string at compile time.
So you have the type checking of C++ builtin to the compiler with the added
benefit of variable arguments and easy to use formatting capability strings.
Kind of the best of all worlds.
It isn't like the old days where you could easily pass an incorrect format string
or invalid argument to a printf function.
Now you get a warning or even an error depending on your compile options.
So the C++ type checking argument of not using printf() no longer applies
(for most uses of the printf() functions).

===
One of the issues with AVRs and printf() type functions is the RAM usage.
Strings by default end up in the data section when using gcc
and all things in the "data" sections end up in RAM.
This means that all printf formatting strings will consume RAM.

The way to correct this is to move the strings into flash. But that means calling
non standard printf() functions and using a casting element each time you
declare a printf() formatting string. This is a total pain and most people
simply will not do it.

Here is a collection of functions and macros that I wrote that hides all this complexity
and moves the formatting strings off to FLASH automagically.

/*
 * Define a REAL printf since Arduino doesn't have one
 *
 * SerialPrintf() will automatically put the format string in AVR program space
 * 
 */

#define SerialPrintf(fmt, ...) _SerialPrintf(PSTR(fmt), ##__VA_ARGS__)

extern "C" {
  int serialputc(char c, FILE *fp)
  { 
      if(c == '\n')
        Serial.write('\r'); 
    Serial.write(c); 
  }
}


void _SerialPrintf(const char *fmt, ...)
{
FILE stdiostr;
va_list ap;

  fdev_setup_stream(&stdiostr, serialputc, NULL, _FDEV_SETUP_WRITE);

  va_start(ap, fmt);
  vfprintf_P(&stdiostr, fmt, ap);
  va_end(ap);
}

To use it, simple call
SerialPrintf() just like you would use printf().
Thats it.
--- bill

hmmm... can you define macros but like a C++ class function?

like this

#define Serial.printf(fmt, ...) fprintf_P(&serstdout, PSTR(fmt), ##VA_ARGS)

if that worked, it'll be awesome

frank26080115:
hmmm... can you define macros but like a C++ class function?

like this

#define Serial.printf(fmt, ...) fprintf_P(&serstdout, PSTR(fmt), ##VA_ARGS)

if that worked, it'll be awesome

No. The "." is not allowed to be used in that way.
Having played with this quite a bit, using printf() isn't as bad as people think.
It costs about 1.8k of code. The Serial print functions (print class), if you start to use the ones
like print(n, base) or floating pint, you will rapidly eat up more space than using the
printf functions. The key to using printf() is that if you use it, don't use any of the arduino print class
functions.

--- bill

The key to using printf() is that if you use it, don't use any of the arduino print class
functions.

How do you NOT use the Print class methods?

I guess I was a bit vague/confusing there.
What I meant to say is that if you use printf() functions for your formatting,
(as shown in the examples above)
don't also use things like Serial.print() because then you end up with
the code space overhead of both formatting routines.
Stick to one or the other.

One other note is that by default the printf() functions provide no
floating point support, which is the reason that the printf() functions
don't eat up that much space.

Where as some of the print class functions yank in floating point
which eats up quite a bit of code space.

-- bill