sprintf() zero padding with byte issue

Hi guys, I've come across two issues with sprintf outputting expected ascii characters. Check out the following code:

#include <stdio.h>
word outputVal;
char outputValChar[5]; /* char storage array */

void setup()   {                
  Serial.begin(57600);
}

void loop()                     
{
  if (Serial.available()) {
    byte mode = Serial.read();

    if (mode == 0x00)
      sprintf(outputValChar, "%01c", 0x0000); /* This should output 0x00 */
    else if (mode == 0x01) 
      sprintf(outputValChar, "%01c", 0x0001); /* This outputs 0x01 */
    else if (mode == 0x02)
      sprintf(outputValChar, "%02c", 0x0000); /* This should output 0x0000 */
    else if (mode == 0x03)
      sprintf(outputValChar, "%02c", 0x0001); /* This should output 0x0001 */
    
    Serial.print(outputValChar); 
  }
}

You will need to be using a terminal that allows you to send and receive hex or this example will end up being pretty useless. What happens when this code is run is this:

mode 0x00, the format mask is telling the Arduino to pad with Zeroes, and output one character with a value of 0x0000. The actual result is that the Arduino does not output anything.

mode 0x01, the format mask is telling the Arduino to pad with Zeroes, and output one character with a value of 0x0001. The actual result is as expected - the Arduino outputs one character with the correct corresponding hex value.

mode 0x02, the format mask is telling the Arduino to Pad with Zeroes and output two characters with a value of 0x0000. The actual result is really weird here - the Arduino outputs the value 0x20 for the first character and then no other characters to represent the second character.

finally, mode 0x03, the format mask is telling the Arduino to Pad with Zeroes and output two characters with a value of 0x0001. The actual result is also weird here - the Arduino outputs the value 0x20 for the first character and then the correct hex code for the second value, too. Now, this doesn't actually work as expected - the Format mask is "%02C" which should pad the value 0x0001 with Zeros. Instead, the Arduino is changing the high byte of the value into 0x20.

Now, I understand that 0x20 is the ASCII representation for a space, but the format mask in all instances is telling sprintf to pad the values with Zeroes. That's not happening in most cases. Why not?

Also, if the value passed to sprintf is 0x0000 (or 0x0 or 0x00 if you modify the code above), then why does sprintf not return anything at all (not even 0x20) when the format mask defines 1 as the number of characters to return?

And no, I can't just use Serial.print(value, BYTE) - even if the value is 0x0001 - I should be able to return the equivalent as a character string (which would be 2 characters long.)

Any help with this would be great - thanks guys.

Well, for one thing, the c format is to print a character. If you want to print an integer in hex format, use an x, not a c.

I'm not looking to print these as hex values in ascii - I'm looking to print them as characters in ascii. The Hex format "%02X", for example, seems to work properly.

I'd rather use a char for speed and efficiency in transferring data from the Arduino to my computer - The data I'm transferring isn't meant to be human readable, but I am using hex in my examples so that humans can read it. I don't have the ability to print 0x0000 as a byte into the forum software :wink:

You appear to be confused about how to use sprintf incorrectly. First - "C" in the format specification indicates to sprintf to print a character, 8 bits, (0x00-0xff).

Using:

sprintf(blah, "%2.2C", 0x0000);

would put character 0 in blah, which is why Serial.print prints nothing. (Serial.print continues printing until NULL, which is what you just set to the first byte.

Try something like this, which does the same thing:

word outputVal = 0x0001;
Serial.print((byte)((outputVal & 0xff00)>>8));
Serial.print((byte)(outputVal & 0x00ff));

The above works well if outputVal is set by reading some input or by the result of some non-constant sampling.

Or, since your test program appears to be setting constants, try this instead:

outputValChar[0] = 0x00;
outputValChar[1] = 0x01;
outputValChar[2] = 0x00;
Serial.print(outputValChar);

... unless your sketch is some sort of a solution to "how to use sprintf" in which case, you will have to get the format parameter correct for what you want to do.

Oops. It appears that my second sample prints nothing also - don't use that.

Bad, Bad post from above:

outputValChar[0] = 0x00;
outputValChar[1] = 0x01;
outputValChar[2] = 0x00;
Serial.print(outputValChar);

I will submit for flogging shortly.

Spinlock: The only confusion I have about how sprintf works would come from whether or not it prints one or more characters.

With this code:

sprintf(blah, "%0C", 0x00);

shouldn't sprintf print the equivalent to 0x00, the (null) character? It prints nothing. The 0 after the % should be telling sprintf to pad with a zero. It should print something, shouldn't it? And why, when I use:

sprintf(blah, "%0C", 0x0001);

does it print the equivalent of 0x2001 ? That does not make sense.

According to this page, I am using sprintf correctly by using "%02c" as a mask.

When I use "%02X" for the format mask to output a HEX value, it correctly prints the values as HEX (and like I said, I don't want the values in HEX - I want them send from the Arduino as chars to maximize bandwidth.) I also don't have to do any sort of bit shifting to get this to work properly - I can do this:

char blah[5];
unsigned int aReallyLongValue = 65536;
sprintf(blah, "%04X", aReallyLongValue); 
Serial.print(blah);

and sprintf will print out "FFFF" in HEX. When aReallyLongValue is 0, sprintf will properly print "0000" in HEX. Why does the char output format not work in the same manner?

Because the c format specifier is converting the value to a character. If the value was 0x41, the c format specifier would correctly put an A in the output string.

Since you are trying to convert a non-printing ascii character to a printable string, the results are NOT what you expect.

As for

shouldn't sprintf print the equivalent to 0x00, the (null) character? It prints nothing.

What, exactly, do you want nothing, null, nada, zip to look like when it is printed?

Get real. Use the proper format specifier for the data type you are trying to print.

Paul: Have you even tried the code? If not, then lay off until you do. Maybe this is actually messed up.

My question still stands:

shouldn't sprintf print the equivalent to 0x00, the (null) character? It prints nothing.

Because the c format specifier is converting the value to a character. If the value was 0x41, the c format specifier would correctly put an A in the output string.

Right - that works for characters other than 0x00, but it does not work for 0x00. It even works for 0x01, which is also non-printable. This is why, in my original post, I said You will need to be using a terminal that allows you to send and receive hex or this example will end up being pretty useless.

You asked:

What, exactly, do you want nothing, null, nada, zip to look like when it is printed?

When I look at what's returned as a hex value, I expect 0x00 to be returned. 0x00 as an ascii character is the (null) character, but when I examine the stream as hex, not even 0x00 is returned - No data is returned at all. That's my problem. SOMETHING should be returned. Load up a terminal that will allow you to send/receive hex to the Arduino and try my code, keyboard muscles.

Get real. Use the proper format specifier for the data type you are trying to print.

Are you always so nice to people? I'm asking for help, and you're telling me to get real. Show me where, exactly, I'm not using the proper format specifier?

When aReallyLongValue is 0, sprintf will properly print "0000" in HEX. Why does the char output format not work in the same manner?

Skizzo, my point is that sprintf is working as it should and is doing exactly what it is told to do. The mask in sprintf or printf is less important than the format - "%02C" tells sprintf to pad a single character in this case with a null byte.

It does not tell sprintf to expand the word into two chars, each a byte.

As such 0x0001 will give two bytes 0x00 and 0x01, 0x0900 will end up being 0x00 and 0x00 with your method.

The only time your sprintf will work with "%02C" is if you pass it a value from 0x0000 to 0x00ff. Anything out of that range will only print the lower part (and in hex it is the far right two characters).

shouldn't sprintf print the equivalent to 0x00, the (null) character? It prints nothing.

Well, no. It properly will set up the array of chars though - it won't print anything. Keep sprintf and Serial.print apart - they are both separately doing what you ask 100% correctly.

Serial.print prints nothing, because you are using a function that prints UNTIL it reaches a NULL character.

In your code, Serial.print ("\0") essentially justs sets up a call to Serial.print, checks the first character and returns because it is NULL.

See my other post to show how to print a NULL to the serial port.

HINT: It wasn't the incorrect and completely invalid sample.

:wink:

Serial.print prints nothing, because you are using a function that prints UNTIL it reaches a NULL character.

Thank you - this explains why Serial.print is not printing anything when I'm expecting it to print a null character (the equivalent to 0x00).

But, it does not explain the other formatting issue...

Skizzo, my point is that sprintf is working as it should and is doing exactly what it is told to do. The mask in sprintf or printf is less important than the format - "%02C" tells sprintf to pad a single character in this case with a null byte.

sprintf isn't working like this for me. When passing sprintf a word, if the high byte is 0x00, then sprintf returns 0x20 for the high byte, which is not the desired output according to the mask passed.

As such 0x0001 will give two bytes 0x00 and 0x01, 0x0900 will end up being 0x00 and 0x00 with your method.

Using sprintf with 0x0001 returns 0x20 and 0x01, not 0x00 and 0x01 like you're describing it should. 0x0900 validly returns 0x09 and 0x00 though. The issue here seems to be that I'm telling sprintf to pad with zeroes, but it's ignoring that mask and padding with spaces instead, which is it's default padding mode. Why is it ignoring the leading zero padding in the mask?

The 0 in sprintf does not pad with zero bytes. It pads with ascii digit zero. (This is useful for printing dates like "2010-01-09".) But it will only pad if you have a formatting width that is greater than the number you're printing. So you need a format like %02d to give an actual desired width of the output. (It's also meant for numbers. I don't think it pads with digit zeroes for %s et al.) See the last few examples in particular.

Serial.print(0, BYTE); // puts a 0x00 byte to the port

char buffer[5];
sprintf(buffer, "0x%02X", 0);
Serial.print(buffer); // puts four characters, '0', 'x', '0', '0' to the port

sprintf(buffer, "%c", 65);
Serial.print(buffer); // puts one character, 'A' to the port

sprintf(buffer, "%0c", 65);
Serial.print(buffer); // puts one character, 'A' to the port
// (length of 'A' is longer than zero, no padding added)

sprintf(buffer, "%3c", 65);
Serial.print(buffer); // puts three characters, ' ', ' ', 'A' to the port
// (length of 'A' is shorter than 3, two padding spaces added)

sprintf(buffer, "%3d", 65);
Serial.print(buffer); // puts three characters, ' ', '6', '5' to the port
// (length of '6', '5' is shorter than 3, one padding space added)

sprintf(buffer, "%03d", 65);
Serial.print(buffer); // puts three characters, '0', '6', '5' to the port
// (length of '6', '5' is shorter than 3, one padding zero added)

sprintf(buffer, "%-3d", 65);
Serial.print(buffer); // puts three characters, '6', '5', ' ' to the port
// (length of '6', '5' is shorter than 3, one padding space added AFTER)

Serial.print prints nothing, because you are using a function that prints UNTIL it reaches a NULL character.

Thank you - this explains why Serial.print is not printing anything when I'm expecting it to print a null character (the equivalent to 0x00).

Spinlock: Actually, after thinking about this and in line with the example that halley posted, Serial.print(0, BYTE) does print a zero byte character. Or, are you referring to the sprintf function?

halley: your examples omit both of the issues I'm explaining - you're using values other than 0 in your character examples. Replace the values with 0 and try to output them.

Shouldn't Serial.print(0, BYTE) and sprintf(buffer, "%c", 0); output equivalent data? They don't.

Also, based on halley's own example:

sprintf(buffer, "%3c", 65);
Serial.print(buffer); // puts three characters, ' ', ' ', 'A' to the port
// (length of 'A' is shorter than 3, two padding spaces added)

sprintf's mask works for values other than just numbers with char output. Why, then, does it not work for zero padding?

The 0 in sprintf does not pad with zero bytes. It pads with ascii digit zero.

It's not doing that, either. If it was, it would be outputting the equivalent to 0x30. It's always padding with spaces, the equivalent of 0x20.

Shouldn't Serial.print(0, BYTE) and sprintf(buffer, "%c", 0); output equivalent data? They don't.

I don't understand this question. Serial.print and sprintf do fundamentally different things. Serial.print transfers data to the serial port, performing some formatting in the process.

The sprintf function performs some formatting, to populate a character array.

Shouldn't Serial.print(0, BYTE) and sprintf(buffer, "%c", 0); output equivalent data? They don't.

I don't understand this question. Serial.print and sprintf do fundamentally different things. Serial.print transfers data to the serial port, performing some formatting in the process.

The sprintf function performs some formatting, to populate a character array.

Okay, here's clarification for you.

Shouldn't this code:

Serial.print(0, BYTE);

and this code:

char buffer[5];
sprintf(buffer, "%c", 0); 
Serial.print(buffer);

output equivalent data? They don't.

Shouldn't this code:
Serial.print(0, BYTE);

and this code:

char buffer[5];
sprintf(buffer, "%c", 0);
Serial.print(buffer);

output equivalent data?

I don't think so -
Serial.print(0, BYTE); (in this case the byte value zero)
outputs a single byte, whereas
Serial.print(buffer);
attempts to output a string (in this case, an empty string)

What do you think they should output?

char buffer[5] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
sprintf(buffer, "%c", 0);
Serial.print(buffer);

I'm not gonna download this code, but it will result in the buffer containing either 0x00 0xFF 0xFF 0xFF 0xFF, or 0x00 0x00 0xFF 0xFF 0xFF. In either case, Serial.print(buffer) will STOP printing when it finds the FIRST zero (ascii nul) byte. Result, nothing printed.

The Serial.print(number, BYTE) case is the only way I'm aware of, to send an ascii nul byte through the port. All the other methods will try to print digits or stop when it encounters an ascii nul byte in the string/buffer.