OK, which is fine, to each is own, but the code you provided in post #63 doesn't work, so I still believe you have some misconceptions about using printf() and printf_P() from Arduino sketches vs adding a printf() method inside the Arduino Print class or creating a C++ wrapper function like my Pprintf() function.
Arduino is a C++ development environment so getting the AVR libC printf() and printf_P() functions to actually work is non trivial.
IMO, getting it to work is considerably beyond the typical Arduino user.
It involves not only to do some magic foo to interface the C++ sketch code to the AVR libC C functions but some magic foo to create and setup the linkage necessary to allow the stdout capability of that AVR libC functions like printf() and printf_P() to send their output to the Arduino Print class write() function of the desired Print class object.
i.e. you can't just call printf()
You have to do some magic stuff in your sketch to get it to work.
And then even after you get stdout to work, to use printf_P() you have to use PSTR() instead of F() for the format string becuse F() is a C++ construct and printf_P() is a C function. AVR libC provides a PSTR() macro to use which is similar to F() in the Arduino world.
Whereas when a printf() method exists in the Print class, things "just work".
No magic stuff. You can just call the function like Serial.printf() and it works.
Even for format strings that use F()
Same is true for my Pprintf() function since it is a C++ wrapper function that wraps around the Arduino C++ Print class.
For all those still following along and are interested in using "printf" like formatted output, here are some additional comments and examples on a couple of options.
- using the AVR libC printf() and printf_P() functions
- using my Pprintf() function
To use printf() you need some real magic to create the linkage.
This linkage is very core specific, the magic you do for the AVR core will be different for other cores. The example provided shows how to do this for the AVR core library which uses the AVR libC library.
For printf_P() which is a AVR proprietary call to use strings stored in flash you have to use the PSTR macro instead of the F() macro.
All of this means that using printf() and printf_P() are non portable.
To use Pprintf() you also need a bit of magic, but the magic is portable and works on any Arduino core. And using F() for format strings "just works".
Look over the examples and you can see the differences.
IMO, having a printf() method in the Print class is the ideal solution but so far Arduino.cc has refused to put it into their cores for more than 15 years.
You could edit the Print class files yourself to add it
or if you want a portable solution, you could use my Pprintf() function and just cut/paste a small bit of code into your sketch.
I have included some examples that I wrote to demonstrate each way of doing xxprintf() output.
Example of using printf() and printf_P() on AVR platform
// sample code to demostrate use of printf() and printf_P()
// on Arduino AVR platforms.
// It uses the Serial object for demonstration.
// magic foo for the printf() linkage to get the stdout output
// from printf() and send it to the Arduino Print class object.
#include <stdio.h>
extern "C"
{
static FILE mystdout; // declare the FILE structure to use for stdout
int _putchar(char c, FILE *fp)
{
Print *pcp;
pcp = (Print *) fdev_get_udata(fp); // recover the Print class object
pcp->write((uint8_t) c);
return(0);
}
}
// function to initilize all the data structures for stdout
// to use _putchar() for the output
void initSTDOUT(Print & outdev)
{
// initalize the AVR libC stdio STDOUT linkage to link printf()
// output to an Arduino Print class object output by linking it to
// the _putchar() function above and storing a pointer to
// the desired Print class object.
// fill in the FILE structure used for stdout to point to
// to the _putchar() function for charactre output.
fdev_setup_stream(&mystdout, _putchar, NULL, _FDEV_SETUP_WRITE);
// save a pointer to the Print class so _putchar can recover it
// to know where to send the output
fdev_set_udata(&mystdout, &outdev);
stdout = &mystdout;
// at this point printf() is now linked to _putchar() which will
// send output to the Print class object passed in.
}
// To add printf() and printf_P() support to your sketch include all the
// code above this point to your sketch.
// Add a call to initSTDOUT() in setup()
// to set up the linkage to your desired Print class object to use
// for the printf() and printf_P() output.
//------------------------------------------------------------------------
void setup(void)
{
// Initialize the Serial device
Serial.begin(9600);
// initalize the printf() linkage to use Serial object for output
// this also initializes things for printf_P()
initSTDOUT(Serial);
printf("printf Demo...\n");
}
void loop(void)
{
printf(" printf: Seconds up: %04ld\n", millis()/1000);
// to use printf_P() to directly use string constants stored in
// FLASH without the RAM copy, you must use the PSTR() macro
// as the proprietary Arduino AVR F() which is supported by the
// the Arduino environment is a C++ construct that will
// not work with printf_P() as printf_P() is C code.
printf_P(PSTR("printf_P: Seconds up: %04ld\n"), millis()/1000);
delay(1000);
}
Example for Pprintf() that works on any platform
// PrintClass printf(), AKA Pprintf() works similar to the fprintf() function.
// The differnce is that instead of the first argument being a FILE * pointer
// the first argument is a Print class object.
//
// Since Pprintf(PrintClassobj, const char *format, ...) taks a Print class
// object, the sketch can direct the printf formatted output to any
// Print class device output.
// i.e the print class object can be Serial, lcd, etc...
// As long as the device supports Print class printing you can use Pprintf()
// to print to it.
//
// define output buffer size (maximum sized output string that can be created)
#define PPRINTF_BUFSIZE 64
size_t Pprintf(Print &outdev, const char *format, ...)
{
char buf[PPRINTF_BUFSIZE];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
return(outdev.write(buf));
}
// this version of the a function is an overload to support uses of the F()
// macro for FLASH based format strings for the AVR enviroment.
size_t Pprintf(Print &outdev, const __FlashStringHelper *format, ...)
{
char buf[PPRINTF_BUFSIZE];
va_list ap;
va_start(ap, format);
vsnprintf_P(buf, sizeof(buf), (const char *) format, ap);
va_end(ap);
return(outdev.write(buf));
}
// sample code to demonstrate use of a printf() like function called Pprintf()
// It uses the Serial object for demonstration.
// To add Pprintf() to your sketch include all the code above this point
// in your sketch.
//------------------------------------------------------------------------
void setup(void)
{
Serial.begin(9600);
// nothing has to be initailised. Just call Pprintf()
Pprintf(Serial, "Pprintf Demo...\n");
}
void loop(void)
{
Pprintf(Serial, "Seconds up: %04ld\n", millis()/1000);
// Pprintf() also supports the F() macro for format strings
Pprintf(Serial, F("Seconds up: %04ld\n"), millis()/1000);
delay(1000);
}
Example of printf() method in Print class.
Note: this will not work on the AVR platform. It works on 3rd party platforms
like ESPxxx, chipkit, etc...
// Example of how to use a printf() method when it is included in the
// Print class.
// Since it is in the Print class, there no magic to do.
// just use it.
void setup(void)
{
Serial.begin(9600);
Serial.printf("printf Print Class Demo...\n");
}
void loop(void)
{
Serial.printf("Seconds up: %04ld\n", millis()/1000);
delay(1000);
}