sprintf

I notice that lots of arduino fans are not coming from a C programming background. Even the C++ people may not know such a neat C feature, the sprintf function. I use it so often in my codes and libraries thus it demands a small introduction.

The following is a link to the sprintf function (I’ll be adding this link to my sample codes):
http://www.cplusplus.com/reference/clibrary/cstdio/sprintf/

Essentially the sprintf function takes in a few parameters, such as integers, floats etc. and then follow a format string, output a formatted string that involves the parameters. Since arduino doesn’t print float (to reduce compiled file size), you may need to print two integers for a float. Say if I want to print on LCD the current time in the following format:

13:02:00

I have three variables:

int hour=13;
 int min=2;
 int sec=0;

If I do the simple print, I have to do the following:

 lcd.print(hour);
 lcd.print(":");
 lcd.print(min);
 lcd.print(":");
 lcd.print(sec);

And the result is less than perfect:
13:2:0

So how do I demand the formatting of the numbers to have a maximal 2 integer digits and no missing leading zeros? I have to tell the print function so by means of a format string. Let’s start from simple and add all details step-by-step:

A format string is just a string.
First I want to output integer (not float or else), so integer output is “%d”, or “%ld” for long integer.
Next I want 2 digit integer so “%2d”, or “%4d” if I wanted 4 digit integer.
Next I want to keep leading zero so my time looks right, so”%02d”. If I used “%2d” then leading spaces are provided so I get 13:(space)2:(space)0

Now I’m done with format string, it’s time to call the sprintf:

char[9] buffer=""; ///< This is the buffer for the string the sprintf outputs to
sprintf(buffer, "%02d:%02d:%02d", hour, min, sec); ///< This has 3 2-digit integers with leading zeros, separated by ":" . The list of parameters, hour, min, sec, provides the numbers the sprintf prints out with.
lcd.print(buffer); ///< You will get "13:02:00"

This will get you:

13:02:00

From the above example you can see that you can mix regular texts such as “:” or else with the format string in a complex output. Say we want to output a count-down number like “You have XX minutes left.”, then we do:

sprintf(buffer, "You have %2d minutes left.", min);

You always need to have a buffer char array long enough to hold the output. Count the characters before you define the length of the buffer array and add 1 to the count for string terminator. For the above string, the output is 25 characters, the “%2d” counts as 2 characters. Then you do:

char [26] buffer="";

It’s worth mentioning that sprintf is just for the look. There is no reason to use it if you don’t care about the look of your output.

Now that you have read so much about sprintf, you may also want to know sscanf, which is the opposite. It picks out number from a complex string so if you have a string like “13:02:00″, you can extract hour, min, and sec from the string. This is especially useful when accepting parameters from serial port. If you want to know about this function, read here or reply to this post saying you are interested in a post about the sscanf.

http://www.cplusplus.com/reference/clibrary/cstdio/sscanf/

22 Responses to sprintf

  1. ahdavidson says:

    But be aware that floating point is not fully implemented on Arduino, so using %f does not work!

    • liudr says:

      Thanks for the reminder! That’s right. I’ll change the example to %ld for long integer and add your comment. There is a way to enable the %f but it will increase the file compile size by quite a bit and I don’t remember how to do it anymore.

      • ahdavidson says:

        Somewhere in the Arduino forum, I remember reading a lot about this issue, so there might some useful links there.

    • AndrewK says:

      Is that why my code isn’t working.. Thanks :)

      • liudr says:

        I’m a little confused. Which code was that?

      • AndrewK says:

        I was using %f in my code and only getting ? in the output, I hadn’t realized that it wasn’t handled. The code I mentioned is just my current project, not something I posted looking for help on :)

      • liudr says:

        Exactly AndrewK. The sprintf doesn’t understand %f since the default compiling option won’t include that for saving memory. You will get a “?”.

  2. Roy says:

    Liudr,
    Nice Post on the sprintf….if you have the time/inclination I would thoroughly enjoy short tutorial from you on the scanf as well. I find your use of examples very helpful.

    • liudr says:

      Roy,

      I’m glad that you liked the tutorial. I’ll get started with the sscanf tutorial. It’s just another very useful function to handle strings. You can send human-readable texts from PC via serial port to arduino and arduino can use sscanf to turn the texts into numbers.

  3. Pingback: sscanf « Liudr's Blog

  4. michael_x says:

    With Arduino not caring about where variables (and const literals) reside, it’s even slightly easier to define the buffer right:

    char* buffer= “00:00:00″; // the content will be overritten, just to avoid wrong size counting
    sprintf(buffer,”%02d:%02d:%02d”,h,m,s);

    • liudr says:

      Good idea. Just be on the cautious side of this practice. I can recall many times I started with a smaller buffer and grew beyond its limits, leading to endless headache in debugging.

      • michael_x says:

        You’re right, but that’s the same with your char buffer[9];
        No one stops you to strcpy(buffer, “This is too long”);

        Weird things will happen ;)

        Try this on Arduino :

        int a = 0;
        char buffer[4];
        int c = 0;
        void setup()
        {
        strcpy(buffer, “Hello world”);
        Serial.begin(9600);
        }
        void loop()
        {
        delay(2000);
        Serial.println(buffer);
        Serial.print(a);
        Serial.print(c);
        a++; c–;
        }

        The only thing to recognize always is the word “Hell”

      • liudr says:

        Yeah, in old C world you are responsible of “wiping your own ass”. The C++ changed it to “all destructed asses are automatically wiped”. The memory allocation will strongly influence how your code works since you are assuming continuous memory on the variables. In a different system where data and code are stored in the same space and without protection, you will be overwriting your code with that strcpy. Virus and encryption people do that to cover their tracks from sourcers. Been a while since I used the term sourcer.

      • liudr says:

        BTW, GNU C has this function that ANSI C doesn’t yet have. Check it out.

        int strlcpy(char *dst, const char *src, int siz)

        It’s safer for the ass especially when you pass strlen(buffer] as siz parameter ;)

    • bperrybap says:

      This is very bad practice. While it often works, it is not guaranteed to work.
      In fact the C standard says that the behavior of writing to a read only string is undefined.

      See this for some additional information about some of the issues and how to
      work around it by using a different declaration.

      http://stackoverflow.com/questions/1405594/writing-into-c-string

      — bill

      • liudr says:

        Good eye. I didn’t see the star. Without the star this would be fine and with the star I think verbose mode should complain that line.

      • michael_x says:

        @bperrybap:
        >This is very bad practice.

        You’re absolutely right.
        I was astonished that it compiles and works on an Arduino without warning.

        Replacing
        char* buffer= “00:00:00″;
        by
        char buffer[]= “00:00:00″;
        is definitely better: E.g. sizeof(buffer) gives a correct result.
        Thanks for your additional link.

  5. Anupam says:

    thanks sir,it really helped me to understand sprintf……..

  6. Abe says:

    FYI: your code renders with smart quotes, so when I coppied and pasted a snippet, the double quotes were the wrong character and caused an error. Took a while to track it down, so I thought I’d mention it. But this is not a complaint–thanks for the good info!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 53 other followers

%d bloggers like this: