printf(...) for ESP- and AVR-chips

if you have multiple Serial.print's adjacent printf() might be a better solution. I've tried printf() with this small program:
//------------------------------------------------------------------------------------------------
char buffer[200];
int i, j;
float fp;
char *s = "char array not NULL terminated";
char c;

void setup(){
Serial.begin(115200);
char intro[] = "\nsome printf examples\n\n\0";
Serial.print(intro);
}

void loop(){
c = 'A'; // single character
i = 35; // integer
fp = 3.14; // real ge nicht
String XYZ = "irgend was"; // String geht nicht

// fill in the buffer by successive calling sprint()
j = sprintf(buffer, "%s\n", s); // char string / array of char
j += sprintf(buffer+j, "%c\n", c); // single char
j += sprintf(buffer+j, "%d\n", i); // integer
// j += sprintf(buffer+j, "%f\n", fp); // **** floating p not implemented****
j += sprintf(buffer+j, "ABC");

// finally print the buffer by printf() along with a sting containing formatting specifications %xx
printf("string:\n%s\ncharacter count = %d\n", buffer, j); // j => character count by printf
printf("\na digit: %d\n",5);
Serial.println("**********");
delay( 5000 );
}
//----------------------------------------------------------------------------------------------------------

and it works fine in an ESP enviroment however in an AVR enviroment no serial monitor output from printf(). The compiler runs through with no complaints.

What is missing? Some #include's?

Thanks

The AVR LIBC documentation says:
"The function printf performs formatted output to stream stdout."
http://nongnu.org/avr-libc/user-manual/group__avr__stdio.html#ga4c04da4953607fa5fa4d3908fecde449

This post shows how to assign Serial to stdout:
http://forum.arduino.cc/index.php?topic=120440.0

What model Arduino are you using for your "AVR environment"?

You may want to take a look at the PrintEx library: GitHub - Chris--A/PrintEx: An extension to the Arduino Print library, and much, much more...
Examples are a bit lacking but it is a very nice library package.
It extends the Print class of an object to add a printf() function, including floating point support on the AVR so you do things like Serial.printf(...), or lcd.printf(...) etc...
Since the code leverages the Print class code, it is actually smaller than using the AVR libC xxprintf() code.

It also supports the ability to output to a buffer like sprintf().

While you can play games with setting up the stdio output hooks to make printf() work in each processor core environment, PrintEx uses is implemented using C++ classes that hook into standard Arduino services so it is portable across all the platforms.

--- bill

Thank-you Johwasser for your reply. To answer your question I'm running Arduino 1.8.4 using a nano as target.

I'm not familar with all the hidden details of arduinos internals (and won't care about it) so I can't follow the discussion in Using stdin, stdout, printf, scanf, etc - Libraries - Arduino Forum and make use of it :-((

What bothers me is the different behavior of "printf" - a quite normal c function in my opinion - on different target systems. Even more, no compiler complains. Running the same code on an generic ESP chip I got what I expected. I wonder what makes printf() hardware dependend.

I'll give bill's suggestion a try on both target systems AVR and ESP. Thank-you bill for the hint.

I think what that forum post was saying is:

Put this part at the top of your sketch file:

#if defined(ARDUINO_ARCH_AVR) 
// Function that printf and related will use to print
int serial_putchar(char c, FILE* f) {
   if (c == '\n') serial_putchar('\r', f);
   return Serial.write(c) == 1? 0 : 1;
}
FILE serial_stdout;
#endif

Put this in setup() after Serial.begin():

#if defined(ARDUINO_ARCH_AVR)
   // Set up stdout
   fdev_setup_stream(&serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);
   stdout = &serial_stdout;
#endif

johnwasser,
While the fdev stuff works to setup stdout on AVR, it isn't portable to other cores.
Each processor's gcc libC is unique in terms of how to hook up an output device to stdout.
This is because stdio services are outside the C standard and libC and embedded platforms really don't have true stdio services.

Mhz,
while printf() may seem like a "normal" function, it depends on stdio services (stdout being the critical one) and embedded systems are quite different than a system running a full operating system.
And the AVR platform is quite limited on resources which makes things complicated on that platform.
stdio is simply a place where things will go to or be from by default when not specifying anything.
This can be many things and in operating systems that support it (Windows doesn't).
What is nice about stdio is that it can be redirected to anything without the application ever knowing.

You may want to have a read of the information I put on the Printf page some years ago.
It explains some various alternatives of how to get printf() formatting type services.
https://playground.arduino.cc/Main/Printf

Also keep in mind that since the AVR is so limited on resources that by default floating point format capabilities are disabled by AVR libC. While it can be re-enabled (it will expand your code by about 2k) it can't be done from the Arduino IDE. In order to enable it requires modifying a linker option. The only way to do this is to manually modify the IDE recipe file to alter the linker options.

The Teensy core includes printf() support in the Print class "out of the box" so things like Serial.printf() "just work".
The Arduino team has rejected including printf() support in the Print class.
I'm not sure why since if you don't use it, it does not consume any resources in your f/w image.

--- bill

As another alternative, you could write a fprintf() type function that could take the stream/Print class object pointer as the first argument so that it could work on any Print class object.
That would give you this capability:

Pprintf(Serial, "this is a number: %d\n", value);

This would not require modifying any system files, but would require declaring/using a local buffer in that function to hold the output prior to sending it to the desired Print class object.

The other thing to keep in mind is the xxprintf() formatting strings.
The AVR SUCKS when it comes to const data. That is because, unlike every other processor, the AVR can't directly access flash. That is why you end up with all the PROGMEM stuff or _P functions and have to use indirect access routines like pgm_read_byte() that really wonk up your code.
These are all AVR specific kludges to keep from using the very limited RAM for const data.
None of this is required on any other Arduino core since all the other processors support direct access to const data in flash.

So to support const data in flash on the AVR the above function would need a sister function that would support:

Pprintf(Serial, F("this is a number: %d\n"), value);

The reason I bring this up is that if you are going to have lots of xxprintf() formatting strings, it can (will) be an issue on the AVR platform since you won't want those strings stored in RAM.
This means that the functions that handle the formatting will need to understand how to handle formatting strings stored in flash.
The F() macro is the Arduino proprietary way to declare the strings in the Arduino world.
Keep in mind that all this F() stuff for declaring string constants in flash was tossed into Arduino specifically for the AVR, it is not needed on any other processor. All the other processor Arduino cores have had to emulate all the hoops that have to be jumped through on the AVR.

But the bottom line is portability across platforms can get messy due to the limitations and proprietary things that must be done to support const data on the AVR. Since xxprintf() uses const data strings for the formatting strings, this can become an issue.
Just keep that in mind on whatever solution go decide to use.

--- bill

Thank-you bill, thank-you johnwasser for time and effort. However I've to admit I am still confused but on a much higher level now.
As I understand main reason is AVR-chips don't have sufficient resources. It would be verry helpful for the not so experienced user - as I am - to get a hint from the compiler/what ever of incompatibily with the target system.

All the mentioned/ suggested "work arounds" seems to be complicated or intransparent, at least for me.

Still trying to circumvent the unattractive use of multiple xxx.print/ln(..) to format output I suggest using sprintf(...) to assembel a nice formatted string/array of characters. This string can then be "printed" using the well known and famous xxx.print(...) statement - just in only one step. More over you can assemble the string in different program steps/places by appending formatted pieces like this j += sprintf(buffer+j, "%c\n", c) like shown in my initial example.
Pls give it a try and send feedback. Thank-you again.

BTW and off topic: I did't receive notification by e-mail. Whats wrong with my settings?

bperrybap:
johnwasser,
While the fdev stuff works to setup stdout on AVR, it isn't portable to other cores.

I thought that, since the OP was interested in ESP and AVR (and ESP already worked as desired), an AVR-specific solution would be sufficient.

There are two big issues with the AVR

  1. Resources (limited code space and VERY limited RAM)
  2. AVR can not directly access FLASH.

That second issue is caused by the internal h/w design of the AVR chip.
It is the cause for all the PROGMEM stuff which is an AVR proprietary hack to keep constant data from using up both FLASH and RAM.

The recent IDEs will show the amount of code space and data space being used when a sketch is built.

Pls give it a try and send feedback. Thank-you again.

Whoa.... This is your project.
You are fee to use any style or methodology you want for outputing your formatted text.

Others may choose to a different approach.
Me personally, it depends on the project and the quantity of output.
If is very much, then I'd be much more likely to use PrintEx or patch the Print class so that the printf() method can be added to the Print class object.
i.e. Serial.printf() or lcd.printf() etc...

--- bill

johnwasser:
I thought that, since the OP was interested in ESP and AVR (and ESP already worked as desired), an AVR-specific solution would be sufficient.

Fair enough. Point taken.
--- bill