Easy way of doing printf

I seem to recall that a while back one of the regular posters here suggested a library/method of doing printf in code (eg. to Serial) with minimal effort. Can you remind me of what that is? And no, not this one: Arduino Playground - Printf.

Preferably a method that supports the F() macro, although that might be pushing it.

1 Like

Is this the thread you were thinking of?

http://forum.arduino.cc/index.php?topic=381223.0

1 Like

Wow...this wouldn't happen to be for me, would it? I saw your reply to me on the other forum.

If so, there's actually a few other requirements I have affecting me that I didn't cover there (since they limit the length of my comments).

That looks like it! Thanks!

And does anyone remember how to use the F() macro with it?

BigBobby:
Wow...this wouldn't happen to be for me, would it? I saw your reply to me on the other forum.

If so, there's actually a few other requirements I have affecting me that I didn't cover there (since they limit the length of my comments).

Yes, that's you! (Now I'm cross-posting - well not really because I am just trying to help you get an answer).

I cleaned up the example, it looks better like this:

int getChar (FILE *fp)
  {
  while (!(Serial.available()));
  return (Serial.read());
  }

int putChar (char c, FILE *fp)
  {
  Serial.write (c);
  return c;
  }

void setup()
  {
  Serial.begin(115200);
  fdevopen (putChar, getChar);
  }

void loop()
  {
  int temp = 30;
  
  printf ("The temperature is %d degrees C.\n", temp);
  delay (100);
  }

The helper functions getChar and putChar can be used for any device you want - in this case it is Serial.

I can't see an easy way of implementing conversion of PROGMEM constants on the fly, however the Streaming library will do that.

I added the above to your question on Stack Overflow and an example of using the Streaming library.

Hah! I almost put "I'm honored to meet you" in my comment to you in the other forum. I recognized your name from the stickies here, and it looks like you've done a lot for this place.

I have a few other weird requirements that I should list here:

  1. My company decided to standardize on the Arduino for all demonstration circuits that require an embedded system. The reason is that half our customers are power or analog engineers that have traditionally avoided software. Arduino is supposed to not be intimidating.

  2. The Arduino code that we distribute is always going to be ported to another processor, where 99% of the time our customers program in C. Therefore, I'd like to avoid objects and such if possible.

  3. The manuals with our demo circuits are supposed to read 1) Download Arduino 1.6.4, 2) Download our Sketchbook, and 3) Press Build. There can't be any other steps like modifying files such that the built-in printf() can now use %f.

  4. To satisfy these 3 requirements I just wrote my own printf() for the Arduino and included it with my Sketchbook. Since my format strings are always going to be in flash I wrote the printf() to expect them there. Even though it's a valid criticism to say that my customers' compilers might not have F(), it's just so darn useful and it's not like my serial output is the part of the project the customers want anyway.

@Nick Gammon
The original poster was user Krupski
He had the thread deleted by a moderator.
There were a few others things mentioned, but this is all I copied.

BigBobby:
4. To satisfy these 3 requirements I just wrote my own printf() for the Arduino and included it with my Sketchbook. Since my format strings are always going to be in flash I wrote the printf() to expect them there. Even though it's a valid criticism to say that my customers' compilers might not have F(), it's just so darn useful and it's not like my serial output is the part of the project the customers want anyway.

Yes, but literal strings won't be in flash, so that won't work out of the box.

Nick, all you need to do everything you want is here:

This provides an easy way to setup stdin, stdout and stderr (to different devices even).

To be able to print strings from PROGMEM (and even from EEPROM), try my revised "Print" code:

This "Print" is the same as the stock Arduino Print.cpp and Print.h, but it has improvements (see the source code and README file).

Here's a piece of it to give you an idea of what's been added:

size_t Print::print_P (const char *str)
{
	size_t n = 0;
	char c;

	while ((c = PGM_R (str + n++))) {
		write (c);
	}

	return n;
}

// println_P, signed
size_t Print::println_P (const char *str)
{
	size_t n = print_P (str);
	return (n + println());
}

// print_E, EEMEM (eeprom) strings ////
size_t Print::print_E (const char *str)
{
	size_t n = 0;
	char c;

	while ((c = eeprom_read_byte ((const uint8_t *) (str + n++)))) {
		write (c);
	}

	return n;
}

size_t Print::println_E (const char *str)
{
	size_t n = print_E (str);
	return (n + println());
}

This should save you some programming time since it's already done! :slight_smile:

1 Like

Thanks, that should help BigBobby even more. :slight_smile:

So it was you?

Well my format strings never need to be RAM...even in the rare case I need to pick between two of them I can use ? : to pick between two in flash. As for my %s arguments, I have them in RAM but for %S I have them in PROGMEM. Heh...since my function name != printf() I can get away with those things and still be compatible C. Really, the way my custom printf() really saves me resources is by understanding iQ and fixed point decimal numbers. My customers usually have their own custom printfs too for the same reasons so they don't mind me doing this stuff. Going to c++ would be a jump though.

I'll read through the github links tomorrow. I really appreciate all of this effort to help me!

@Krupski
Thank you.

BigBobby:
Well my format strings never need to be RAM...even in the rare case I need to pick between two of them I can use ? : to pick between two in flash. As for my %s arguments, I have them in RAM but for %S I have them in PROGMEM. Heh...since my function name != printf() I can get away with those things and still be compatible C. Really, the way my custom printf() really saves me resources is by understanding iQ and fixed point decimal numbers. My customers usually have their own custom printfs too for the same reasons so they don't mind me doing this stuff. Going to c++ would be a jump though.

I'll read through the github links tomorrow. I really appreciate all of this effort to help me!

For printing, my suggestion is to grab the "Print" package from GitHub and then REPLACE your Arduino "Print.cpp" and "Print.h" with the new ones (save backups of the old ones, of course).

It's completely compatible with the old code (in fact, most of it IS the old code), it's just that NEW features are added such as "print_P" (along with println_P), a "print_E" which can print strings in EEPROM, the ability to print int64_t and uint64_t variables and a "print number" re-write that uses no SRAM buffer.

As far as using printf, note that printf already exists in AVR-GCC.... the only reason it doesn't work "out of the box" is that stdin, stdout and stderr are not "connected" to anything.

That's where the "Stdinout" library comes in. If you include that library in your code, an object named "STDIO" is pre-instantiated and it supports a few functions:

STDIO.open (device);
STDIO.close ();
STDIO.getStream (FILE *);

The first one connects "device" to stdin, stdout and stderr... for example:

STDIO.open (Serial);

Now, if you do "printf ("Hello\n"); it's sent to the serial port. Likewise, "getc" and other input functions will READ from Serial.

You can also specify different devices... for example:

STDIO.open (Serial, LCD);

Connects "Serial" to stdin and "LCD" to stdout and stderr. So, a simple "printf ("%c", fgetc (stdin));" would read serial input and display it on the LCD screen.

Lastly, you can do all three individually. Let's say you wanted to read serial input, write to an LCD and send any error info to serial output (using "fprintf (stderr, "string....").

You would do this:

STDIO.open (Serial, LCD, Serial);

That connects stdin and stderr to serial, and stdout to LCD. The parameters are:

STDIO.open (stdin, stdout, stderr);

STDIO.close(); just releases any resources used by the standard IO paths (typically around 40 bytes).

It's all documented in the package, along with two demo programs.

LarryD:
@Krupski
Thank you.

You are most welcomed.

Hey guys, thanks again. I checked out the github links.

If I didn't already have my own printf already then the equation would be how much time could I save myself by using something like this that already exists, but since I do already have one (and at this point in my career I should be capable of making one in a few hours) that changes the equation. It seems the pros of staying with what I have is 1) it's nearly 100% C (no C++), 2) doesn't require my customers to do any modifications to the standard Arduino installation, 3) compiles smaller than the avr-libc printf(), yet 4) can include features not in the standard printf() format specifiers to make the code very efficient. In my last post to this thread I thought that I'd made up %S for strings in flash, but I since learned that avr-libc already used that for their own non-standard specifier Using PROGMEM with sprintf_P - Programming Questions - Arduino Forum

My original question on the other forum was how I could get rid of the overload functions to handle both PGM_P and "const __FlashStringHelper*" without casting all over the place. In your code in github, I see a method for "const __FlashStringHelper *". What happens if you pass it a PGM_P?

BigBobby:
Hey guys, thanks again. I checked out the github links.

If I didn't already have my own printf already then the equation would be how much time could I save myself by using something like this that already exists, but since I do already have one (and at this point in my career I should be capable of making one in a few hours) that changes the equation. It seems the pros of staying with what I have is 1) it's nearly 100% C (no C++), 2) doesn't require my customers to do any modifications to the standard Arduino installation, 3) compiles smaller than the avr-libc printf(), yet 4) can include features not in the standard printf() format specifiers to make the code very efficient. In my last post to this thread I thought that I'd made up %S for strings in flash, but I since learned that avr-libc already used that for their own non-standard specifier Using PROGMEM with sprintf_P - Programming Questions - Arduino Forum

My original question on the other forum was how I could get rid of the overload functions to handle both PGM_P and "const __FlashStringHelper*" without casting all over the place. In your code in github, I see a method for "const __FlashStringHelper *". What happens if you pass it a PGM_P?

Wow... I didn't know that. Never saw any reference to that until just now. Thanks!

BigBobby:
My original question on the other forum was how I could get rid of the overload functions to handle both PGM_P and "const __FlashStringHelper*" without casting all over the place. In your code in github, I see a method for "const __FlashStringHelper *". What happens if you pass it a PGM_P?

The "__FlashStringHelper *" code always was part of the Arduino Print code. I didn't add that, and honestly I don't know what it does since I've never used it.

I suppose you'll have to experiment a little! :slight_smile:

BigBobby:
In your code in github, I see a method for "const __FlashStringHelper *". What happens if you pass it a PGM_P?

There was another thread about this recently. Basically PSTR returns a local string in PROGMEM. However it is type const char []. For various functions to realize it is really in PROGMEM and not RAM (and they can't tell from that type) __FlashStringHelper casts it into a different type. Then by overloading various functions they get the data from the right address space.