[Solved] How to supress leading zero trimming with serial.print(var, HEX);

I'm working with V_88 to help him to get one Arduino to send data to another Arduino via XBee attached to a SoftwareSerial object, but my question would also apply to hardware serial. I'm advising him with one method of transmitting and receiving a structured data format. I am trying to use serial.print(var, HEX); to send a byte of data encoded as two ASCII characters. Unfortunately, I'm noticing that if the high nybble of the byte of data is zero then the print method will strip the leading zero. For example, serial.print(0x23, HEX); would send the ASCII character '2' and then the ASCII character '3'. This is good. But, serial.print(0x03, HEX); would only send the ASCII character '3'. Goodbye structured data format.

Is there a way to have serial.print(var, HEX); not strip leading zeros for any datatype of var, or would we best be served by rolling our own function to manually convert the nybbles to char and serial.write them?

For the insanely curious, see this post (and the surrounding posts) for context.

Edit: Add [Solved] to the subject.

Is there a way to have serial.print(var, HEX); not strip leading zeros

No. Serial.print() does not do formatted output.

sprintf() does, and you can tell it to include leading 0s, and output in a variety of bases.

PaulS:

Is there a way to have serial.print(var, HEX); not strip leading zeros

No. Serial.print() does not do formatted output.

sprintf() does, and you can tell it to include leading 0s, and output in a variety of bases.

So for our single byte of data, use sprintf() to convert to a null-terminated (thus 3 element) char array and then serial.print() that?

Sembazuru:

PaulS:

Is there a way to have serial.print(var, HEX); not strip leading zeros

No. Serial.print() does not do formatted output.

sprintf() does, and you can tell it to include leading 0s, and output in a variety of bases.

So for our single byte of data, use sprintf() to convert to a null-terminated (thus 3 element) char array and then serial.print() that?

Or more accurately asked for our partiucluar problem, SoftwareSerial.print() the null-terminated char array?

So for our single byte of data, use sprintf() to convert to a null-terminated (thus 3 element) char array and then serial.print() that?

Yes. Only two bytes will actually get transmitted.

On the other hand, you could use Serial.write() and skip the whole conversion process.

Or, you could send all the data as strings, with delimiters, and it wouldn't matter how long the string is. "123!" or "1!" or "122864662!".

PaulS:

So for our single byte of data, use sprintf() to convert to a null-terminated (thus 3 element) char array and then serial.print() that?

Yes. Only two bytes will actually get transmitted.

On the other hand, you could use Serial.write() and skip the whole conversion process.

Or, you could send all the data as strings, with delimiters, and it wouldn’t matter how long the string is. “123!” or “1!” or “122864662!”.

Well, for KISS reasons I was going with a start token character and a defined data structure after that. Because at least one of the two data bytes could potentially be any value from 0 to 255 there would be no way to differentiate a single byte start token from a potential data value. Also, the conversion from uint8_t to ASCII encoded hex is simple even without sprintf(). The receiver would just need to watch for the start token and then the next 4 characters would be the ASCII encoded hex value of the two bytes of data. The data is already well defined because it comes from an attached I2C device that outputs a count of 0.02 degree increments above 0 Kelvin. Eventually he wants the temperature as a float so I’m having him convert the data to a float on the receiver end instead of trying to transmit the float.

Simple data only requires a simple solution. And, keep things simple until absolutely necessary to add complexity.

Simple solution comes to

  char dataStream[6];
  sprintf(dataStream,"%c%02X%02X", data_start, data_high, data_low);

Where data_start is a const char defined as '*', and both data_high and data_low are defined as datatype byte. With these constraints the result of sprintf() will always be 5 chars, so defining dataStream as a 6 char long array dataStream[6] should never changed from the default NULL to provide a properly null-terminated char array (i.e. a string with lower-case "s").

so defining dataStream as a 6 char long array dataStream[6] should never changed from the default NULL to provide a properly null-terminated char array

That statement is going to write at least 6 characters to the array - the single character, two character or more, for the first value, two characters or more for the second value, and a terminating NULL.

Given that the value is a byte, there won't be more than 2 characters per value, but the assumption that the default value in the 6th position in the array (a NULL) won't be overwritten is incorrect. sprintf() DOES NULL terminate the array.

PaulS:

so defining dataStream as a 6 char long array dataStream[6] should never changed from the default NULL to provide a properly null-terminated char array

That statement is going to write at least 6 characters to the array - the single character, two character or more, for the first value, two characters or more for the second value, and a terminating NULL.

Given that the value is a byte, there won't be more than 2 characters per value, but the assumption that the default value in the 6th position in the array (a NULL) won't be overwritten is incorrect. sprintf() DOES NULL terminate the array.

So, because the value being fed to the template %02X is a byte, why can't I expect that in all cases the result will always be two characters? I'm expecting that so I would have 1 + 2 + 2 = 5 characters as a result from sprintf(). How would that ever overwrite the 6th character of the array? I suppose I could follow the sprintf() with dataStream[5] = 0x0;, but why would that ever be necessary? I'm just not seeing it.

Edit: Corrected the off-by-one error in the array index. ::face-palm::

So, because the value being fed to the template %02X is a byte, why can’t I expect that in all cases the result will always be two characters?

You can. I said that because the variable type is a byte, you won’t get more than two characters. You could, though, use %02x as the format specifier and an int or long as the type. Then, you could get more than two characters, but never less than 2.

I’m expecting that so I would have 1 + 2 + 2 = 5 characters as a result from sprintf(). How would that ever overwrite the 6th character of the array?

You are not counting the NULL that sprintf() adds to the array. 1 + 2 + 2 + 1 = 6.

I suppose I could follow the sprintf() with dataStream[5] = 0x0;, but why would that ever be necessary?

It isn’t. sprintf() adds the NULL for you. (I’m getting tired of typing that. 8))

PaulS:

So, because the value being fed to the template %02X is a byte, why can't I expect that in all cases the result will always be two characters?

You can. I said that because the variable type is a byte, you won't get more than two characters. You could, though, use %02x as the format specifier and an int or long as the type. Then, you could get more than two characters, but never less than 2.

I'm expecting that so I would have 1 + 2 + 2 = 5 characters as a result from sprintf(). How would that ever overwrite the 6th character of the array?

You are not counting the NULL that sprintf() adds to the array. 1 + 2 + 2 + 1 = 6.

I suppose I could follow the sprintf() with dataStream[5] = 0x0;, but why would that ever be necessary?

It isn't. sprintf() adds the NULL for you. (I'm getting tired of typing that. 8))

Ouch. My bad. Until I read this post I was reading "sprintf() DOES NULL terminate the array" as "DOES NOT terminate"... Sorry for that.